Search code examples
regexpowershellsplit

Spliting Strings by regex seems not to work as expected


Having a function in PowerShell:

function WriteToLog {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [AllowEmptyCollection()]
        [AllowEmptyString()]
        [string[]]$Message
    )
    BEGIN {}
    PROCESS {
        if ($Null -ne $Message) {
            $dt = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
            foreach ($item in $Message) {
                if ($item.length -gt 0) {
                    $it = $item -split '(.{1,70})' | Where-Object {$_}
                    $dt + ": " + $it[0] | Write-Host
                    $dt = $dt -replace '(.)',' '
                    $i = 1
                    while ($it[$i]) {
                        $dt + "  " + $it[$i] | Write-Host
                        $i++
                    }
                }
            }
        }
    }
    END {}
}

Now calling it with a string:

"----" | WriteToLog

gives:

2023-05-25 00:28:05: -
                     -
                     -
                     -
                     -

I'd expected:

2023-05-25 00:28:05: -----

Not splitting the string in characters. With a string longer than 70 characters it works as expected:

"--------------------------------------------------------------------------" | WriteToLog

gives:

2023-05-25 00:32:37: ----------------------------------------------------------------------
                     ----

Any idea why this happens?


Solution

  • Change your regex to '(?<=\G.{70})' so it only splits if the char count is 70, also using a positive lookbehind with \G removes the need to filter where the token is not an empty string (Where-Object { $_ }). Also the condition if ($Null -ne $Message) is unnecessary, you're already enumerating the collection and checking if ($item.Length -gt 0).

    function WriteToLog {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory, ValueFromPipeline)]
            [AllowEmptyCollection()]
            [AllowEmptyString()]
            [string[]] $Message
        )
    
        process {
            $dt = (Get-Date).ToString('yyyy-MM-dd HH:mm:ss')
            foreach ($item in $Message) {
                if ($item.Length -gt 0) {
                    $it = $item -split '(?<=\G.{70})'
                    $dt + ': ' + $it[0] | Write-Host
                    $dt = [string]::new(' ', $dt.Length)
                    for($i = 1; $i -lt $it.Length; $i++) {
                        $dt + '  ' + $it[$i] | Write-Host
                    }
                }
            }
        }
    }
    
    '---------------------------------------------------------------------------' | WriteToLog
    '-----' | WriteToLog
    WriteToLog '', ''