Search code examples
powershellreplacecase-sensitive

Can PowerShell replace a case sensitive portion of text it found using -clike?


Let's say I have two addresses:

  • 123 Newark Road Ne
  • 987 Ne Netherland Avenue

I need to update the directional portion from Ne to NE. However, I don't want to update Newark to NEwark and the same for Netherland. I think I can find all the instances easy enough with this IF statement in a loop:

$testAddress = '123 Newark Road Ne'
if (($testAddress -clike '* Ne') -or ($testAddress -clike '* Ne *')){
   #code to replace Ne
}

But how do I go about replacing it? I can't use a -creplace '* Ne', '* NE'. Finding the index of '* Ne' just gives me -1 so I don't think I can do anything with that. I'm sure there's an easy concept that I'm just not coming across.


Solution

  • You can use Regular Expressions to replace a certain part of your input with something which is not possible in the substitution operand in a regex expression by design (like uppercasing in .NET) by using a MatchEvaluator, which is constructed in PowerShell like a scriptblock.

    With that MatchEvaluator you can manipulate the matched part like you want, hence you are not restricted to anything when it comes to manipulation.

    Beginning with PowerShell 6 you can even use it directly with the -replace and -creplace operators.
    PowerShell versions below 6 does not have this option, but it's still possible using the .NET Regex Replace Method [regex]::Replace() with a MatchEvaluator.

    PS 5.1

    $textToReplace = 'Ne 123 Newark Road Ne', '123 Newark Road Ne', '987 Ne Netherland Avenue'
    
    foreach ($text in $textToReplace) {
        # using a scriptblock as System.Text.RegularExpressions.MatchEvaluator
        # the param() part is mandatory. Everything that follows is the return for that particular match
        [regex]::Replace($text, '(?<!\w)Ne(?!\w)', { param($regexMatch) $regexMatch.Value.ToUpper() })
    }
    

    PS 6+

    $textToReplace = 'Ne 123 Newark Road Ne', '123 Newark Road Ne', '987 Ne Netherland Avenue'
    
    foreach ($text in $textToReplace) {
        $text -creplace '(?<!\w)Ne(?!\w)', { $_.Value.toUpper() }
    }
    

    Regular Expression Pattern Explanation

    The pattern (?<!\w)Ne(?!\w) matches all words Ne for which the preceding and following character is not a word character using a negative lookbehind (?<!) and negative lookahead (?!) group construct.

    \w (Word) in .NET includes all Unicode characters of the following categories:
    MSFT: Character classes in regular expressions -> Word character: \w:

    These include, but are not limited to:

    • a-z and variants like è
    • A-Z and variants like À
    • 0-9
    • _
    • cyrillic characters
    • chinese characters
    • ...

    In short, \w captures almost all word characters which are represented in the Unicode character set.

    Resources

    MSFT: Replacement with a script block in PS6+