3

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
  • what is the content of C:\scripts\test.txt?? Commented Sep 18, 2011 at 14:17

3 Answers 3

8

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Good point @Christian, the GetEnumerator() call doesn't really accomplish anything. Removed.
4

I got it work this way

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

Comments

4

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

1 Comment

To make it case-insensitive, use $regex = [regex]("(?i)" + ($regexes -join '|'))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.