Private/SieReader.ps1
|
# Tokenizer and record parser for SIE 4 files. # # A SIE line is a series of whitespace-separated tokens. The first token on a # data line is a label starting with '#' (e.g. #VER, #TRANS, #KONTO). Strings # containing spaces are enclosed in double quotes ("..."). The tokens '{' and # '}' are structural and may appear standalone or inline (e.g. an object list # after the account number in a #TRANS row). # # This reader returns an array of record objects: # [PSCustomObject]@{ Tag = 'VER'; Fields = @('A','1','20240315','Desc'); Transactions = @(...) } # Header records have an empty Transactions array. Standalone tokens '{' and # '}' are consumed by the VER parser and are not returned as records. function ConvertFrom-SieLine { [CmdletBinding()] param ( [Parameter(Mandatory)] [AllowEmptyString()] [string]$Line ) $tokens = New-Object System.Collections.Generic.List[string] $i = 0 $n = $Line.Length while ($i -lt $n) { $c = $Line[$i] if ([char]::IsWhiteSpace($c)) { $i++ continue } if ($c -eq '"') { $i++ $sb = New-Object System.Text.StringBuilder while ($i -lt $n) { $c = $Line[$i] if ($c -eq '"') { if ($i + 1 -lt $n -and $Line[$i + 1] -eq '"') { [void]$sb.Append('"') $i += 2 continue } $i++ break } [void]$sb.Append($c) $i++ } [void]$tokens.Add($sb.ToString()) continue } if ($c -eq '{' -or $c -eq '}') { [void]$tokens.Add([string]$c) $i++ continue } $sb = New-Object System.Text.StringBuilder while ($i -lt $n) { $c = $Line[$i] if ([char]::IsWhiteSpace($c) -or $c -eq '{' -or $c -eq '}' -or $c -eq '"') { break } [void]$sb.Append($c) $i++ } [void]$tokens.Add($sb.ToString()) } , $tokens.ToArray() } function ConvertFrom-SieText { [CmdletBinding()] param ( [Parameter(Mandatory)] [AllowEmptyString()] [string]$Text ) $lines = $Text -split "`r?`n" $records = New-Object System.Collections.Generic.List[object] $currentVer = $null $inVerBlock = $false foreach ($line in $lines) { $tokens = ConvertFrom-SieLine -Line $line if ($tokens.Count -eq 0) { continue } $first = $tokens[0] if ($first -eq '{') { if ($null -ne $currentVer) { $inVerBlock = $true } continue } if ($first -eq '}') { if ($null -ne $currentVer) { $records.Add($currentVer) | Out-Null $currentVer = $null $inVerBlock = $false } continue } if (-not $first.StartsWith('#')) { continue } $tag = $first.Substring(1) $fields = if ($tokens.Count -gt 1) { $tokens[1..($tokens.Count - 1)] } else { @() } if ($tag -eq 'VER') { if ($null -ne $currentVer) { $records.Add($currentVer) | Out-Null } $currentVer = [PSCustomObject]@{ Tag = 'VER' Fields = $fields Transactions = New-Object System.Collections.Generic.List[object] } continue } if ($tag -eq 'TRANS' -and $inVerBlock -and $null -ne $currentVer) { $transFields = New-Object System.Collections.Generic.List[string] $objectFields = New-Object System.Collections.Generic.List[string] $skipObject = $false for ($k = 0; $k -lt $fields.Count; $k++) { $f = $fields[$k] if (-not $skipObject -and $f -eq '{') { $skipObject = $true continue } if ($skipObject) { if ($f -eq '}') { $skipObject = $false } else { [void]$objectFields.Add($f) } continue } [void]$transFields.Add($f) } $currentVer.Transactions.Add([PSCustomObject]@{ Tag = 'TRANS' Fields = $transFields.ToArray() Objects = $objectFields.ToArray() }) | Out-Null continue } $records.Add([PSCustomObject]@{ Tag = $tag Fields = $fields Transactions = @() }) | Out-Null } if ($null -ne $currentVer) { $records.Add($currentVer) | Out-Null } , $records.ToArray() } |