Search code examples
powershellstring-formatting

Format specific Lines in generated Text-File


I'm want to format specific Lines in a .st (text) file which was generated with Powershell.

  1. Load TextSnippet for demonstration use ($TemplateSnippetText)
  2. Replace some Text
  3. Align Text-Lines with ":=", so that all ":=" are in one line (as the $TemplateSnippetText)

I'm struggling with the formating, probably a math-problem...? Could that be done with the String.Format("{0,6} {1,15}) Method?

Desired Output

            diMoveStep_TFRX                         := 1;
        ELSE // Bewegung vorbereiten
            diMoveStep_TFRX                         := 0;
        END_IF
    END_IF

    // Schrittkette Bewegung
    CASE diMoveStep_TFRX OF
        0:  // Bewegung vorbereiten
            fbAxis_TFRX.i_stCmd.xMoveAbsolute       := FALSE;
            diMoveStep_TFRX                         := 1;
        1:  // Bewegung starten
            IF stPos.stTFRX.stPickPos1.xEnMove  THEN
                fbAxis_TFRX.i_stPar.stMove          := stMovePar_TFRX;
                fbAxis_TFRX.i_stPar.lrPosition      := stDataProduct.stTFRX.lrPos_PickPos1;
                strPickPos1_TFRX                    := 'PickPos1';
                fbAxis_TFRX.i_stCmd.xMoveAbsolute   := TRUE;
                diMoveStep_TFRX                     := 2;
            END_IF
        2:  // Bewegung ist gestartet

Actual Output

        IF NOT fbAxis_TFRX.i_stCmd.xMoveAbsolute THEN   // Bewegung starten
            diMoveStep_TFRX                     := 1;
        ELSE // Bewegung vorbereiten
            diMoveStep_TFRX                     := 0;
        END_IF
    END_IF

    // Schrittkette Bewegung
    CASE diMoveStep_TFRX OF
        0:  // Bewegung vorbereiten
            fbAxis_TFRX.i_stCmd.xMoveAbsolute       := FALSE;
            diMoveStep_TFRX                     := 1;
        1:  // Bewegung starten
            IF stPos.stTFRX.stPickPos1.xEnMove  THEN
                fbAxis_TFRX.i_stPar.stMove          := stMovePar_TFRX;
                fbAxis_TFRX.i_stPar.lrPosition      := stDataProduct.stTFRX.lrPos_PickPos1;
                strPickPos1_TFRX                        := 'PickPos1';
                fbAxis_TFRX.i_stCmd.xMoveAbsolute   := TRUE;
                diMoveStep_TFRX                     := 2;
            END_IF
        2:  // Bewegung ist gestartet

Full Powershell 5.1 Code:

$TemplateSnippetText = 
"ACTION ActAuto_Move_AXISNAME_TARGETPOS: // COMMENT
    // Rücksetzen
    IF NOT stStep.xEntryDone THEN
        IF NOT fbAxis_AXISNAME.i_stCmd.xMoveAbsolute THEN   // Bewegung starten
            diMoveStep_AXISNAME                             := 1;
        ELSE // Bewegung vorbereiten
            diMoveStep_AXISNAME                             := 0;
        END_IF
    END_IF

    // Schrittkette Bewegung
    CASE diMoveStep_AXISNAME OF
        0:  // Bewegung vorbereiten
            fbAxis_AXISNAME.i_stCmd.xMoveAbsolute           := FALSE;
            diMoveStep_AXISNAME                             := 1;
        1:  // Bewegung starten
            IF stPos.stAXISNAME.stTARGETPOS.xEnMove THEN
                fbAxis_AXISNAME.i_stPar.stMove              := stMovePar_AXISNAME;
                fbAxis_AXISNAME.i_stPar.lrPosition          := stDataProduct.stAXISNAME.lrPos_TARGETPOS;
                strTargetPos_AXISNAME                       := 'TARGETPOS';
                fbAxis_AXISNAME.i_stCmd.xMoveAbsolute       := TRUE;
                diMoveStep_AXISNAME                         := 2;
            END_IF
        2:  // Bewegung ist gestartet
            IF fbAxis_AXISNAME.q_stStat.xMoveDone AND
                stPos.stAXISNAME.stTARGETPOS.xInRange THEN
                fbAxis_AXISNAME.i_stCmd.xMoveAbsolute       := FALSE;
                stStep.xActDone                             := TRUE;
                diMoveStep_AXISNAME                         := 3;
            END_IF
        3:  // Bewegung ist beStringEndt 
            ;
    END_CASE
    
    // Stop deaktivieren während der Bewegung
    stStep.enStopMode                                       := SEL(fbAxis_AXISNAME.i_stCmd.xMoveAbsolute, EnableStop, DisableStop);
END_ACTION

"
#Dateipfad
$strFilePath = 'C:\Temp\CodeSnippet.st' 
$RegPatRepWhiteSpaces = '\s+?(?=:=)'    # https://regex101.com/r/IEiymN/1
$RegPatStartOfString = '^.*?(?=:=)'     # https://regex101.com/r/icJGAy/1
$RegPatEndOfString  = ':=(.*)'          # https://regex101.com/r/0UlzT7/1


# File-Inhalt löschen
Remove-Item $strFilePath  -Include *.st

$SnippetsToGenerate = @(
    [PSCustomObject]@{COMMENT = 'Zielposition1'; AXISNAME = 'TFRX'; TARGETPOS = 'PickPos1' }
   # [PSCustomObject]@{COMMENT = 'Zielposition2'; AXISNAME = 'TFRX'; TARGETPOS = 'PickPos2' }
    #[PSCustomObject]@{COMMENT = 'Zielposition3'; AXISNAME = 'TFRX'; TARGETPOS = 'PickPos3' }
)

$SnippetsToGenerate | ForEach-Object {
$CurrentSnippet = $TemplateSnippetText
$CurrentSnippet = $CurrentSnippet -replace "COMMENT", $_.COMMENT -replace "AXISNAME", $_.AXISNAME -replace "TARGETPOS", $_.TARGETPOS
$CurrentSnippet | Out-File -FilePath $strFilePath -Append 
} 

    (Get-Content -Path $strFilePath -Raw) -Replace $RegPatRepWhiteSpaces,'' | Set-Content -Path $strFilePath
    
    $IndexOfAllocation = Get-Content -Path $strFilePath | ForEach-Object {$_.IndexOf(":=") } | Measure-Object -Maximum
    
    (Get-Content -Path $strFilePath) | ForEach-Object {
            $currentObject = $_
            if($currentObject -match ':='){   
                $StringStart = [Regex]::Matches($currentObject, $RegPatStartOfString).Value 
                $StringEnd = [Regex]::Matches($currentObject, $RegPatEndOfString ).Value

                Write-Host "Lenght StringStart" $StringStart.Length"Lenght StringEnd" $StringEnd.Length
                Write-host "Max" $IndexOfAllocation.Maximum

                [uint32]$DiffToPaddAbsolute = ($IndexOfAllocation.Maximum  - $StringStart.Length)
        
                #if (($DiffToPaddAbsolute % 4) -gt 2) {
                    $DiffToPaddAbsoluteTabs = [math]::Ceiling($DiffToPaddAbsolute / 4) + 1
                #   } else {
                    #$DiffToPaddAbsoluteTabs = [math]::Floor($DiffToPaddAbsolute / 4) + 1
                #}
                $StringStartPadded= $StringStart.PadRight($StringStart.Length + $DiffToPaddAbsoluteTabs,[char]9)
                $currentObject = "$StringStartPadded$StringEnd"
                }
            $currentObject
    } | Set-Content  -Path $strFilePath
Invoke-Item $strFilePath

Solution

  • I think you might be over-complicating the padding - regex is not required at all, we just need to find the offset at which := occurs in each line after the initial replacement:

    $FilePath = "C:\Temp\CodeSnippet.st"
    $TemplateSnippetText = "<Insert Your Template Here>"
    
    $SnippetsToGenerate = @(
      [PSCustomObject]@{COMMENT = 'Zielposition1'; AXISNAME = 'TFRX'; TARGETPOS = 'PickPos1' }
      [PSCustomObject]@{COMMENT = 'Zielposition2'; AXISNAME = 'TFRX'; TARGETPOS = 'PickPos2' }
      [PSCustomObject]@{COMMENT = 'Zielposition3'; AXISNAME = 'TFRX'; TARGETPOS = 'PickPos3' }
    )
    
    $SnippetsToGenerate | ForEach-Object {
      # Use `-creplace` (Case-sensitive replace)
      $snippet = $TemplateSnippetText -creplace 'COMMENT', $_.COMMENT -creplace 'AXISNAME', $_.AXISNAME -creplace 'TARGETPOS', $_.TARGETPOS
    
      # Split into individual lines
      $lines = $snippet -split '\r?\n'
      
      # Use String.IndexOf() to find the current column offset of `:=` in each line
      $offsets = $lines | ForEach-Object IndexOf ':='
      
      # Sort the offsets to find the right-most occurrence (we will align everything to this one)
      $targetOffset = $offsets | Sort-Object -Descending | Select-Object -First 1
      
      for ($i = 0; $i -lt $lines.Count; $i++) {
        $line = $lines[$i]
        $offset = $offsets[$i]
      
        if ($offset -in 0..$targetOffset) {
          # $offset is > -1, so we know the line contains a `:=`, let's left-pad it by 
          # inserting the required number of spaces at the position right in front of `:=`
          $lines[$i] = $line.Insert($offset, [string]::new(" ", $targetOffset - $offset))
        }
      }
    
      # Output 
      $lines
    } |Set-Content $FilePath
    

    Which, with the template provided in your question, gives me the following output in CodeSnippet.st:

    ACTION ActAuto_Move_TFRX_PickPos1: // Zielposition1
        // Rücksetzen
        IF NOT stStep.xEntryDone THEN
            IF NOT fbAxis_TFRX.i_stCmd.xMoveAbsolute THEN   // Bewegung starten
                diMoveStep_TFRX                                 := 1;
            ELSE // Bewegung vorbereiten
                diMoveStep_TFRX                                 := 0;
            END_IF
        END_IF
    
        // Schrittkette Bewegung
        CASE diMoveStep_TFRX OF
            0:  // Bewegung vorbereiten
                fbAxis_TFRX.i_stCmd.xMoveAbsolute               := FALSE;
                diMoveStep_TFRX                                 := 1;
            1:  // Bewegung starten
                IF stPos.stTFRX.stPickPos1.xEnMove THEN
                    fbAxis_TFRX.i_stPar.stMove                  := stMovePar_TFRX;
                    fbAxis_TFRX.i_stPar.lrPosition              := stDataProduct.stTFRX.lrPos_PickPos1;
                    strTargetPos_TFRX                           := 'PickPos1';
                    fbAxis_TFRX.i_stCmd.xMoveAbsolute           := TRUE;
                    diMoveStep_TFRX                             := 2;
                END_IF
            2:  // Bewegung ist gestartet
                IF fbAxis_TFRX.q_stStat.xMoveDone AND
                    stPos.stTFRX.stPickPos1.xInRange THEN
                    fbAxis_TFRX.i_stCmd.xMoveAbsolute           := FALSE;
                    stStep.xActDone                             := TRUE;
                    diMoveStep_TFRX                             := 3;
                END_IF
            3:  // Bewegung ist beStringEndt 
                ;
        END_CASE
        
        // Stop deaktivieren während der Bewegung
        stStep.enStopMode                                       := SEL(fbAxis_TFRX.i_stCmd.xMoveAbsolute, EnableStop, DisableStop);
    END_ACTION
    
    ACTION ActAuto_Move_TFRX_PickPos2: // Zielposition2
        // Rücksetzen
        IF NOT stStep.xEntryDone THEN
            IF NOT fbAxis_TFRX.i_stCmd.xMoveAbsolute THEN   // Bewegung starten
                diMoveStep_TFRX                                 := 1;
            ELSE // Bewegung vorbereiten
                diMoveStep_TFRX                                 := 0;
            END_IF
        END_IF
    
        // Schrittkette Bewegung
        CASE diMoveStep_TFRX OF
            0:  // Bewegung vorbereiten
                fbAxis_TFRX.i_stCmd.xMoveAbsolute               := FALSE;
                diMoveStep_TFRX                                 := 1;
            1:  // Bewegung starten
                IF stPos.stTFRX.stPickPos2.xEnMove THEN
                    fbAxis_TFRX.i_stPar.stMove                  := stMovePar_TFRX;
                    fbAxis_TFRX.i_stPar.lrPosition              := stDataProduct.stTFRX.lrPos_PickPos2;
                    strTargetPos_TFRX                           := 'PickPos2';
                    fbAxis_TFRX.i_stCmd.xMoveAbsolute           := TRUE;
                    diMoveStep_TFRX                             := 2;
                END_IF
            2:  // Bewegung ist gestartet
                IF fbAxis_TFRX.q_stStat.xMoveDone AND
                    stPos.stTFRX.stPickPos2.xInRange THEN
                    fbAxis_TFRX.i_stCmd.xMoveAbsolute           := FALSE;
                    stStep.xActDone                             := TRUE;
                    diMoveStep_TFRX                             := 3;
                END_IF
            3:  // Bewegung ist beStringEndt 
                ;
        END_CASE
        
        // Stop deaktivieren während der Bewegung
        stStep.enStopMode                                       := SEL(fbAxis_TFRX.i_stCmd.xMoveAbsolute, EnableStop, DisableStop);
    END_ACTION
    
    ACTION ActAuto_Move_TFRX_PickPos3: // Zielposition3
        // Rücksetzen
        IF NOT stStep.xEntryDone THEN
            IF NOT fbAxis_TFRX.i_stCmd.xMoveAbsolute THEN   // Bewegung starten
                diMoveStep_TFRX                                 := 1;
            ELSE // Bewegung vorbereiten
                diMoveStep_TFRX                                 := 0;
            END_IF
        END_IF
    
        // Schrittkette Bewegung
        CASE diMoveStep_TFRX OF
            0:  // Bewegung vorbereiten
                fbAxis_TFRX.i_stCmd.xMoveAbsolute               := FALSE;
                diMoveStep_TFRX                                 := 1;
            1:  // Bewegung starten
                IF stPos.stTFRX.stPickPos3.xEnMove THEN
                    fbAxis_TFRX.i_stPar.stMove                  := stMovePar_TFRX;
                    fbAxis_TFRX.i_stPar.lrPosition              := stDataProduct.stTFRX.lrPos_PickPos3;
                    strTargetPos_TFRX                           := 'PickPos3';
                    fbAxis_TFRX.i_stCmd.xMoveAbsolute           := TRUE;
                    diMoveStep_TFRX                             := 2;
                END_IF
            2:  // Bewegung ist gestartet
                IF fbAxis_TFRX.q_stStat.xMoveDone AND
                    stPos.stTFRX.stPickPos3.xInRange THEN
                    fbAxis_TFRX.i_stCmd.xMoveAbsolute           := FALSE;
                    stStep.xActDone                             := TRUE;
                    diMoveStep_TFRX                             := 3;
                END_IF
            3:  // Bewegung ist beStringEndt 
                ;
        END_CASE
        
        // Stop deaktivieren während der Bewegung
        stStep.enStopMode                                       := SEL(fbAxis_TFRX.i_stCmd.xMoveAbsolute, EnableStop, DisableStop);
    END_ACTION
    

    If you want to retain the original offset simply calculate the $targetOffset based on the template without making any replacements, before generating the actual snippets:

    $targetOffset = $TemplateSnippetText -replace '\r?\n' |ForEach-Object IndexOf := |Sort-Object -Descending |Select-Object -First 1
    
    $SnippetsToGenerate | ForEach-Object {
       # ...