Zahlenwert aus .txt durch neuen Zahlenwert ersetzen

  • Hallo zusammen,
    ich habe mal wieder ein "Suchen-und-Ersetzen" Problem.

    In Textdatei mit Zahlen in Spalten soll jede Zahl der 4. und 5. Spalte durch Zahl+2 ersetzt werden.
    Problem: Spalten sind nicht duch festen Wert getrennt (z.B. TAB), sondern durch variable Anzahl an Leerzeichen, meist 4 oder 5 (das kommt im Beispiel unten leider nicht rüber).
    Was wäre ein sinnvoller Ansatz?
    Ich habe 2 Skripte probiert. Bei beiden Skripten verliere ich leider das Zahlenformat (immer 3 Nachkommastellen) und weiß nicht wie ich die neuen Daten zurück in die Textdatei schreiben kann.
    1. FileReadLine, dann StringSplit. Im Array kann ich dann Zahlenwerte ersetzen.
    2. FileReadToArray: hier lese ich eine Zeile der Textdatei in eine Zeile des Arrays und splitte dann anhand von 4 Leerzeichen auf. Im Array kann ich dann Zahlenwerte ersetzen.


    1 101 0.000 0.0380 3.100 2.350 1.500 50.000 1906 --> 1 101 0.000 0.0380 5.1 4.35 1.500 50.000 1906
    1 201 0.000 0.0300 3.800 2.050 3.500 50.000 3600
    1 301 0.000 0.0180 3.000 0.250 5.500 50.000 3138
    1 401 0.710 0.0310 6.700 2.650 8.100 50.000 2766
    1 501 0.270 0.0370 9.000 5.500 7.000 50.000 2454
    1 601 0.000 0.0280 10.500 8.300 4.400 50.000 2210
    1 701 0.000 0.0230 7.400 6.300 2.200 50.000 1991
    1 801 0.030 0.0230 8.100 7.450 1.300 50.000 2009
    1 901 0.040 0.0210 6.500 5.650 1.700 50.000 2027
    11001 0.050 0.0330 9.600 7.350 4.500 50.000 2594
    11101 0.000 0.0230 11.200 7.800 6.800 50.000 5048
    11201 0.130 0.0540 11.200 7.000 8.400 50.000 3053
    11301 0.000 0.0280 8.200 5.700 5.000 50.000 2485
    11401 0.090 0.0420 10.800 7.750 6.100 50.000 3842



    C
    #include <File.au3>
    #include <Array.au3>
    
    
    $File = @ScriptDir & "\C_01.cli"
    Local $aArray
    _FileReadToArray($File, $aArray)
    _ArrayDisplay($aArray)
    Local $aNumberSplit = StringSplit($aArray[2], "    ", $STR_ENTIRESPLIT)
    _ArrayDisplay($aNumberSplit)


    Vielen Dank, stevie

  • @stevie : So sollte es gehen:

    Spoiler anzeigen

    Gruß
    Homer J. S.

    ...wenn die Donuts auch nur halb so gut schmecken wie sie aussehen, dann sehen sie doppelt so gut aus wie sie schmecken...

    Einmal editiert, zuletzt von Homer J. S. (3. März 2015 um 12:50)

  • Nur zur Information:
    1 - Mit der Lösung gehen negativen Zahlen nicht
    2 - Es dürfen nicht mehr oder weniger wie 6 Komma sein (SpringSplit splittet anhand der Komma)
    3 - Die Formatierung wird beibehalten
    1 101 0.000 0.0380 3.100 2.350 1.500 50.000 1906 -> 1 101 0.000 0.0380 3.100 4.350 3.500 50.000 1906

    Edit:
    Es gibt noch ein Problem, wenn die zu ändernde "vor Komma Zahl>einer Stelle" und die zu änderde Zahl ">7" ist. Heute Abend kann ich das noch ändern.


    Gruß
    Homer J. S.

    ...wenn die Donuts auch nur halb so gut schmecken wie sie aussehen, dann sehen sie doppelt so gut aus wie sie schmecken...

    2 Mal editiert, zuletzt von Homer J. S. (3. März 2015 um 13:16)

  • Problem: Spalten sind nicht duch festen Wert getrennt (z.B. TAB), sondern durch variable Anzahl an Leerzeichen, meist 4 oder 5.

    Bevor du die Werte mit StringSplit splittest entfernst du alle doppelten Leerzeichen per StringStripWS().

    Bei beiden Skripten verliere ich leider das Zahlenformat (immer 3 Nachkommastellen)

    Um Zahlen formatiert auszugeben verwendet man StringFormat().

    So wie ich das aber sehe, hat jede Spalte eine ganz bestimmte Breite.
    Wenn man die Daten per StringSplit splitten würde, müsste man dieses Format (am besten per StringFormat) dann wieder restaurieren.
    Lade daher am besten mal eine Beispieldatei hoch, an der wir dieses Format erkennen können.
    Falls dieses Format nicht so wichtig ist, kann man es auch ganz einfach per StringSplit splitten.
    Man muss dann aber aufpassen, da in deinem Beispiel bei manchen Werten zwischen der ersten und zweiten Spalte kein Leerzeichen ist.
    Hier mal als Beispiel zur Vorgehensweise:

    Spoiler anzeigen
    [autoit]


    Global $s_SourceFile = "Test.txt"

    [/autoit] [autoit][/autoit] [autoit]

    ; Datei zum Lesen öffnen
    Global $h_FileRead = FileOpen($s_SourceFile,0)
    ; Datei zum Schreiben öffnen:
    Global $h_FileWrite = FileOpen("Temp.txt", 2)

    [/autoit] [autoit][/autoit] [autoit]

    Global $s_Line, $a_Split, $s_NewLine

    [/autoit] [autoit][/autoit] [autoit]

    ; Datei zeilenweise durchgehen
    Do
    $s_Line = FileReadLine($h_FileRead)
    If @error Then ExitLoop

    [/autoit] [autoit][/autoit] [autoit]

    $a_Split = StringSplit(StringStripWS(StringTrimLeft($s_Line, 1), 7), " ", 2) ; Werte der Zeile in ein Array splitten
    If UBound($a_Split) < 5 Then ContinueLoop
    ; die gewünschten Spalten anpassen:
    $a_Split[3] = StringFormat("%.3f", Number($a_Split[3],3) + 2)
    $a_Split[4] = StringFormat("%.3f", Number($a_Split[4],3) + 2)

    ; Anhand der Array-Werte die veränderte Zeile in temporäre Datei schreiben:
    $s_NewLine = StringFormat("%-2s", StringLeft($s_Line, 1))
    For $i in $a_Split
    $s_NewLine &= $i & " "
    Next
    FileWriteLine($h_FileWrite, StringTrimRight($s_NewLine, 1))
    Until 0

    [/autoit] [autoit][/autoit] [autoit]

    ; Alles wieder schließen und Quelldatei mit der temporären Datei überschreiben:
    FileClose($h_FileRead)
    FileClose($h_FileWrite)
    FileMove("Temp.txt", $s_SourceFile, 1)

    [/autoit]
  • Hier meine Version:

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    $sText = FileRead(@ScriptDir & "\C_01.cli")

    [/autoit] [autoit][/autoit] [autoit]

    $aText = StringSplitW($sText, " ")
    For $i = 0 To UBound($aText) - 1
    $aText[$i][4] += 2
    $aText[$i][5] += 2
    Next
    _ArrayDisplay($aText)

    [/autoit] [autoit][/autoit] [autoit]

    ; #FUNCTION# ========================================================================================================================================
    ; Name .................: StringSplitW()
    ; Description ..........: Splits a string into columns instead of rows as it is done by SplitString(), like a csv file to a 2d array ;)
    ; Syntax ...............: StringSplitW($sString[, $sDelimiter = ";"[, $bStripWS = True[, $sQuotationMark = '"'[, $sDummy = "¦"[, $iWidthLen = 256]]]]])
    ; Parameters ...........: $sString - A string value.
    ; $sDelimiter - [optional] A string value. Default is ";".
    ; $bStripWS - [optional] A binary value. Default is True.
    ; $sQuotationMark - [optional] A string value. Default is '"'.
    ; $sDummy - [optional] A string value. Default is "¦".
    ; $iWidthLen - [optional] An integer value. Default is 256.
    ; Return values .......: Success - 2d array
    ; Error 1 - $sStringis not set
    ; Error 2 - $delimter is not set
    ; Error 3 - error splitting string
    ; Error 4 - array width exceeded
    ;
    ; Version .............: v0.97 build 2015-03-03 beta
    ; Author ..............: UEZ
    ; Modified ............:
    ; Remarks .............: RegEx take from http://stackoverflow.com/questions/4476…r-within-quotes
    ; Related .............: StringSplit, StringReplace, StringRegExpReplace, StringLen, StringStripCR
    ; ===================================================================================================================================================
    Func StringSplitW($sString, $sDelimiter = ";", $bStripWS = True, $sQuotationMark = '"', $sDummy = "¦", $iWidthLen = 256)
    If $sString = "" Then Return SetError(1, 0, 0)
    If $sDelimiter = "" Then Return SetError(2, 0, 0)
    Local $aSplit = StringSplit(StringStripCR($sString), @LF)
    If @error Then Return SetError(3, 0, 0)
    Local $chk, $iWidth, $i, $j, $k, $iLen, $iMax = 1, $iMaxWidth, $iSum = 0
    Local $aPos[1], $l = 0
    Local $aVertical[$aSplit[0]][$iWidthLen], $iDelimiterLen = StringLen($sDelimiter) - 1, $sLine
    For $k = 1 To $aSplit[0]
    $iLen = StringLen($aSplit[$k])
    If $iLen > 1 Then
    Switch $bStripWS
    Case True
    $sLine = StringRegExpReplace(StringStripWS($aSplit[$k], 4), '(?m)\' & $sDelimiter & '(?=[^' & $sQuotationMark & ']*' & $sQuotationMark & '(?:[^' & $sQuotationMark & '\r\n]*' & $sQuotationMark & '[^' & $sQuotationMark & ']*' & $sQuotationMark & ')*[^' & $sQuotationMark & '\r\n]*$)', $sDummy)
    Case False
    $sLine = StringRegExpReplace($aSplit[$k], '(?m)\' & $sDelimiter & '(?=[^' & $sQuotationMark & ']*' & $sQuotationMark & '(?:[^' & $sQuotationMark & '\r\n]*' & $sQuotationMark & '[^' & $sQuotationMark & ']*' & $sQuotationMark & ')*[^' & $sQuotationMark & '\r\n]*$)', $sDummy)
    EndSwitch
    $chk = StringReplace($sLine, $sDelimiter, $sDelimiter)
    $iWidth = @extended
    $iSum += $iWidth
    If $iWidth > $iWidthLen Then Return SetError(4, 0, 0)
    If $iWidth >= $iMax Then $iMax = $iWidth + 1
    Switch $iWidth
    Case 0
    $aVertical[$l][0] = $sLine
    Case Else
    Dim $aPos[$iWidth * 2 + 2]
    $j = 1
    $aPos[0] = 1
    For $i = 0 To $iWidth - 1
    $aPos[$j] = StringInStr($sLine, $sDelimiter, 0, $i + 1) - 1
    $aPos[$j + 1] = $aPos[$j] + 2 + $iDelimiterLen
    $j += 2
    Next
    $aPos[UBound($aPos) - 1] = StringLen($sLine)
    $j = 0
    For $i = 0 To UBound($aPos) - 1 Step 2
    $aVertical[$l][$j] = StringMid(StringReplace($sLine, $sDummy, $sDelimiter), $aPos[$i], $aPos[$i + 1] - $aPos[$i] + 1)
    $j += 1
    Next
    EndSwitch
    $l += 1
    EndIf
    Next
    ReDim $aVertical[$l][$iMax]
    Return $aVertical
    EndFunc

    [/autoit]

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯