Powershell: replacing in strings using a hashtable

本秂侑毒 提交于 2019-12-07 13:57:30

问题


Okay, so I've set up a hash table with names being what to replace and keys being what to replace with, like this:

$r = @{
    "dog" = "canine";
    "cat" = "feline";
    "eric" = "eric cartman"
}

What should I do next? I've tried this:

(Get-Content C:\scripts\test.txt) | Foreach-Object {
    foreach ( $e in $r.GetEnumerator() ) {
        $_ -replace $e.Name, $e.Value
    }
} | Set-Content C:\scripts\test.txt.out

But it doesn't work at all, it just writes each line three times, without replacing anything.

EDIT: Contains of test.txt:

dog
cat
eric

test.txt.out:

dog
dog
dog
cat
cat
cat
eric
eric
eric

回答1:


Here's one way to do it:

$file = Get-Content C:\scripts\test.txt
foreach ($e in $r) {
  $file = $file -replace $e.Name, $e.Value
}
Set-Content -Path C:\scripts\test.txt.out -Value $file

The reason you were seeing each line three times is because of the nested foreach loop. A replace operation was running once per hashtable entry for every line in the file. That doesn't change the source file, but by default it does output the result of the replace (even if nothing is changed).

You can get the desired functionality by reading the file into a variable first, and then using your looping replace to update that variable. You also don't need a separate foreach loop for the file contents; the replace can run against the full text in one pass per hashtable entry.




回答2:


Depending on your file and hashtable, there are various optimizations you could consider:

  1. You may be able to build a regex from the hashtable key collection like so:

    $regexes = $r.keys | foreach {[System.Text.RegularExpressions.Regex]::Escape($_)}
    $regex = [regex]($r.Keys -join '|')    
    

    In doing this you wouldn't to iterate every key, but now you need to know which key you matched in order to get the replacement. On the other hand, it may be faster to do string replacement instead of regex replacement (or something more complex like a string split and join process).

  2. In Powershell you can call the .NET Regex::Replace function:

    string Replace(string input, System.Text.RegularExpressions.MatchEvaluator evaluator)

    Calling this method you can define a MatchEvaluator with a scriptblock like so:

    $callback = { $r[$args[0].Value] }
    

    In the scriptblock, $args[0] is a System.Text.RegularExpressions.Match, so you can use its Value property to index into the $r hashtable.

  3. Get-Content returns an array of strings which is fine for the -replace operator, but also implies an extra loop running. [System.IO.File]::ReadAllText will instead return a single string, so the regex only needs to be parsed once.

    $file = [System.IO.File]::ReadAllText("C:\scripts\test.txt")
    
  4. If you used Get-Content, to use $regex.Replace (instead of -replace) you would need a loop:

    $file = $file | % { $regex.Replace($_, $callback) }
    

    Since I am not I can use a single replace call:

    $file = $regex.Replace($file, $callback)
    

Thus the full script:

$r = @{
    "dog" = "canine";
    "cat" = "feline";
    "eric" = "eric cartman"
}


$regexes = $r.keys | foreach {[System.Text.RegularExpressions.Regex]::Escape($_)}
$regex = [regex]($regexes -join '|')

$callback = { $r[$args[0].Value] }

$file = [System.IO.File]::ReadAllText("C:\scripts\test.txt")
$file = $regex.Replace($file, $callback)
Set-Content -Path C:\scripts\test.txt.out -Value $file



回答3:


I got it work this way

foreach ($i in $HashTable.Keys) {
  $myString = $myString -replace $i, $HashTable[$i]
}


来源:https://stackoverflow.com/questions/7461936/powershell-replacing-in-strings-using-a-hashtable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!