PowerShell -replace with multiple occurrences next to each other in the line

Viewed 653

I have a | delimited file and I have some data where for null values it has a space. So, in my data file I'll have something like this:

2080| | | | | | | | | | | | | |2000225

I tried this:

-replace '\| \|', '||'

but it matches pairs of | and still leaves the space when it's done between |. I'm just not really good with regex and totally new to Powershell.

2080|| || || ....|2000225

I'm not sure if recursion would solve this or if I'm going to need to write a short Java program to do it.

3 Answers

You can use the regex-based -replace operator as follows:

PS> ' |2080| | | | | | | | | | | | | |2000225| ' -replace ' (\||$)', '$1'
|2080||||||||||||||2000225|

This assumes that no non-empty fields have trailing spaces - if they do, their (last) trailing space will be removed; to avoid this, use the appropriate solution from Wiktor Stribiżew's helpful answer.

Regex (\||$) matches a single space char. followed by either a literal | (escaped as \|) or (|) the end of the string ($); $1 in the replacement string then replaces whatever the 1st capture group ((...)) matched; that is, if the space char. was followed by literal |, it is effectively replaced with just |; if it was followed by the end of the string, it is effectively removed.


A slight simplification is to use a positive lookahead assertion ((?=...)), as also used in Wiktor's answer, which captures the space character only, and therefore allows omission of the substitution-text -replace operand, which defaults to the empty string and therefore effectively removes the spaces:

PS> ' |2080| | | | | | | | | | | | | |2000225| ' -replace ' (?=\||$)'
|2080||||||||||||||2000225|

Using -replace with a regex based search, you may....

Remove all whitespace between two | chars:

$text -replace '(?<=\|)\s+(?=\|)'

To only remove spaces in between | and start/end of string

$text -replace '(?<=\||^)\s+(?=\||$)'
$text -replace '(?<![^|])\s+(?![^|])'

Remove all whitespace characters that are either followed with | or end of string

$text -replace '\s+(?=\||$)'
$text -replace '\s+(?![^|])'

Output: 2080||||||||||||||2000225. See the regex demo.

Details

  • \s+ - 1 or more whitespace characters
  • (?=\||$) - a positive lookahead that requires a | char (\|) or (|) end of string ($) immediately to the right of the current location.
  • (?![^|]) - a negative lookahead that fails the match if there is a char other than | immediately to the right of the current location.

You don't need to run a recursive function to do that. Just run it twice. The problem is that once you match | |, you are past the start of the next occurence. In the first pass, you leave all the ocurrences of | | | (so after the first match <| |> |, you will have | as starting point for new matches, which doesn't match) for the second one... of if you have more, you left without matching all the even occurences that are stuck together. If you run it only a second time, you'll match and change all those matches you left the first time. Run it a second time and you'll see that it works.

Just do:

PS> ' |2080| | | | | | | | | | | | | |2000225| ' -replace '| |', '||' -replace '| |', '||'
|2080||||||||||||||2000225|

You won't need more.

Related