Parameter auf Array splitten

  • Hi Leute!

    Ich brauch mal wieder eure Hilfe:

    Folgende Situation:

    Wir haben folgenden Code:

    [autoit]

    GuiCreate("testfenster, test, test (abcd)", 10, 10, iniread("test.ini","test","test","test"),"test")

    [/autoit]

    Von diesem Befehl habe ich alle Parameter in einem String:

    [autoit]

    $String = '"testfenster, test, test (abcd)", 10, 10, iniread("test.ini","test","test","test"),"test"'

    [/autoit]

    Ich versuche nun diesen „Parameterstring“ auf ein Array aufzuteilen. Dabei sollte jedoch die Logik der Aufteilung (k.a. wie ich das sonst nennen soll ^^) erhalten bleiben.
    Das heißt aus dem $String sollte dann folgendes Array entstehen:

    [autoit]


    [0] "testfenster, test, test (abcd)"
    [1] 10
    [2] 10
    [3] iniread("test.ini","test","test","test")
    [4] "test"

    [/autoit]

    Ich zerbreche mir seit Tagen den Kopf wie man das am besten lösen könnte. :S
    Und mit RegEx bin ich ja nicht so begabt.

    Vielleicht könnt ihr mir da weiterhelfen.

    Danke im Voraus!! <3

  • Hi BugFix!

    Ich weiß leider nicht genau was du mit einer globalen Lösung meinst.
    Aber grundsätzlich soll das ganze "nur" für AutoIt sein. (Genauer gesagt brauch ich das für mein ISN-Studio)

    Und es reicht wenn die Parameter "so wie sie sind" in das Array gepackt werden. Es muss also z.b das iniread aus dem oberen Beispiel nicht weiter zerlegt werden.

  • Schaue mal, ob du damit was anfangen kannst:

    Spoiler anzeigen
    [autoit]


    #include <Array.au3>

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

    $String = '"testfenster, test, test (abcd)", 10, 10, iniread("test.ini","test","test","test"), "test"' & @CRLF
    $aResult = StringSplitW(StringRegExpReplace($String, '(",")', "|"), ",")

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

    _ArrayDisplay($aResult)

    [/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, $iWidthLen)
    ; Parameters ...........: $sString - string to split
    ; $sDelimiter - [optional] the delimter how to split the string
    ; $iWidthLen - [optional] length of the row (amount of columns - default is 256)
    ; Return values .......: Success - 2d array
    ; Error 1 - either $sString or $delimter is not set
    ; Error 2 - array width exceeded
    ; Error 3 - error splitting string
    ;
    ; Version .............: v0.95 build 2014-06-06 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 = ";", $sQuotationMark = '"', $sDummy = "¦", $iWidthLen = 256)
    If $sString = "" Or $sDelimiter = "" Then Return SetError(1, 0, 0)
    Local $chk, $iWidth, $i, $j, $k, $iLen, $iMax = 1, $iMaxWidth
    Local $aPos[1], $l = 0
    Local $aSplit = StringSplit(StringStripCR($sString), @LF)
    If @error Then Return SetError(3, 0, 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
    $sLine = StringRegExpReplace($aSplit[$k], '(?m)' & $sDelimiter & '(?=[^' & $sQuotationMark & ']*' & $sQuotationMark & '(?:[^' & $sQuotationMark & '\r\n]*' & $sQuotationMark & '[^' & $sQuotationMark & ']*' & $sQuotationMark & ')*[^' & $sQuotationMark & '\r\n]*$)', $sDummy)
    $chk = StringReplace($sLine, $sDelimiter, $sDelimiter)
    $iWidth = @extended
    If $iWidth > $iWidthLen Then Return SetError(2, 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]

    Das Array wird als 2D Array erstellt.

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    Einmal editiert, zuletzt von UEZ (13. November 2014 um 21:47)

  • Danke schon mal UEZ! Gefällt mir schon ganz gut.

    Bei meinem Beispiel oben funktioniert das ganze optimal. Ich hab aber weitere Strings versucht wo die aufteilung leider nicht ganz passt.

    zb:

    [autoit]


    $String = '"test()",iniread("test.ini",stringreplace("123","1234"),"test"),99' & @CRLF

    [/autoit]

    Hier wird der iniread Befehl auf 3 Spalten zerteilt. :(

    Optimal wäre 1 Parameter pro Zeile/Spalte. Somit kann ich später genau sagen dies ist zb. der 2te Parameter des Befehls. (und das sollte in dem Beispiel iniread("test.ini",stringreplace("123","1234"),"test") sein)

  • Kannst du nicht die Ini anders speichern, z.B. die Trennung der Zellen nicht Komma ist, sondern irgend ein anderes sinnvolles Zeichen?

    So wird das ziemlich kompliziert....

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

    • Offizieller Beitrag

    Mal ne andere Frage: Untersuchst du die Strings ausserhalb des Editors oder im Editor?
    Falls im Editor, du verwendest doch Scintilla (wie auch SciTE). Und da könntest du die Zeichen-Typ-Info heranziehen um die Kommapositionen für die Parametertrennung zu identifizieren.

  • UEZ: Das mit der ini war nur ein Beispiel. Und ja wie gesagt mein Kopf raucht auch schon ziehmlich wegen dem ganzen :P

    Ja das ganze läuft im ISN ab. Es soll in Version 0.97 einen Parameter Editor geben. Dort sollen dann die ganzen Parameter den jewailigen Befehlen zugeordnet werden. Darum das ganze ^^

    Hmm..daran hab ich auch schon gedacht es irgentwie über das Scintilla Control zu erledigen. Nur eben nicht wie.
    Hast du zufällig einen Plan wie das geht? Läuft das über die Styles ab die Scintilla verwendet?

    • Offizieller Beitrag

    Läuft das über die Styles ab die Scintilla verwendet?


    Genau, der Lexxer ordnet den Positionen im Editor die gefundenen Styles zu. Für au3 ist das Komma mit bei den Operatoren, also im 'style.au3.8'.
    Ein Problem bleibt allerdings weiterhin, dass Parameter wiederum selbst Funktionen mit Parametern sind. Da kannst du evtl. hier Anregungen holen: _String_Balanced --> Text zwischen korrespondierenden Klammern
    Nicht nur die Funktion selbst, sondern auch die Hinweise auf Rekursion mit RegEx in den Posts.

  • So ich denke ich habe es nun doch noch lösen können. (Bevor mein Kopf explodierte ^^)

    Vielleicht kann das ganze ja mal wer brauchen:

    [autoit]


    #include <String.au3>
    #include <Array.au3>

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

    Global $Leeres_Array[1] ;Leeres Array
    _ArrayDelete($Leeres_Array, 0) ;Für leeres Array

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

    $Test = '"testfenster, test, test (abcd)", 10, 10, iniread("test.ini","test","test","test"),"test"' & @CRLF
    ;~ $Test = '"test()",iniread("test.ini",stringreplace("123","1234"),"test"),99' & @CRLF
    Global $Fertiges_Parameter_Array = $Leeres_Array

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

    ;Parameterstring vorbereiten
    $array = _StringBetween($Test, '"', '"', 1)
    If IsArray($array) Then
    For $d = 0 To UBound($array) - 1
    If StringInStr($array[$d], ",") Then $Test = StringReplace($Test, $array[$d], StringReplace($array[$d], ",", "[COMMA]"))
    Next
    EndIf

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

    $array = _StringBetween ($Readen_Parameter_String, "'","'",1)
    if IsArray($array) then
    for $d = 0 to ubound($array)-1
    if StringInStr($array[$d],",") then $Readen_Parameter_String = StringReplace($Readen_Parameter_String,$array[$d],StringReplace($array[$d],",","[COMMA]"))
    next
    endif

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

    $Split_Array = StringSplit($Test, ",", 2)
    If IsArray($Split_Array) Then
    ;Bastle Parameterarray
    $durchlauf = 0
    $Parastring = ""
    $Klammer_auf = 0
    $Klammeraufcount = 0
    $Klammerzucount = 0
    For $durchlauf = 0 To UBound($Split_Array) - 1

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

    If StringInStr($Split_Array[$durchlauf], "(") Then
    StringReplace($Split_Array[$durchlauf], "(", "")
    $Klammeraufcount = @extended
    $Klammer_auf = $Klammer_auf + $Klammeraufcount
    EndIf

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

    If $Klammer_auf = 0 Then
    _ArrayAdd($Fertiges_Parameter_Array, StringStripWS ($Split_Array[$durchlauf],3))
    Else

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

    If StringInStr($Split_Array[$durchlauf], ")") Then
    StringReplace($Split_Array[$durchlauf], ")", "")
    $Klammerzucount = @extended
    $Klammer_auf = $Klammer_auf - $Klammerzucount
    If $Klammer_auf = 0 Then
    $Parastring = $Parastring & StringStripWS ($Split_Array[$durchlauf],3)
    Else
    $Parastring = $Parastring & StringStripWS ($Split_Array[$durchlauf],3) & ", "
    EndIf
    If $Klammer_auf = 0 Then
    _ArrayAdd($Fertiges_Parameter_Array, StringStripWS(StringReplace($Parastring,"[COMMA]",","), 3))
    $Parastring = ""
    EndIf
    Else
    $Parastring = $Parastring & StringStripWS(StringReplace($Split_Array[$durchlauf],"[COMMA]",","), 3) & ", "
    EndIf

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

    EndIf
    Next

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

    EndIf

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

    ;Prüfe ob Array korrekt erstellt wurde
    If $Klammer_auf <> 0 Then
    ;Syntaxfehler
    MsgBox(0, "Fehler", "Syntaxfehler!")
    Else
    ;OK
    _ArrayDisplay($Fertiges_Parameter_Array)
    EndIf

    [/autoit]


    -> Ja warscheidlich extrem kompliziert und umständlich...but hey! It work´s :P

    Und danke für eure Hilfe! Habt mir irgentwie den richtigen gedankenanstoss gegeben! ;)

    • Offizieller Beitrag

    Hi,

    ich habe mal einen anderen Lösungsansatz gewählt:
    - Finde alle Komma-Positionen
    - Finde alle Komma-Positionen innerhalb von Strings (Single-quotiert oder Double-quotiert)
    - Finde alle Komma-Positionen innerhalb von Funktionsaufrufen
    - Entfernen der "inside"-Kommapositionen durch Vergleich mit den anderen Positionen.
    Übrig bleiben die Kommapositionen, die tatsächliche Parameter trennen.

    Da die Suche nach balancierten Ausdrücken in AutoIt etwas hibbelig ist (und wir brauchen gleich 3 davon: '', "", () ), habe ich diesen Teil ausgelagert und ermittele das mittels Lua über Lua - Inline.

    Spoiler anzeigen
    [autoit]


    #include-once
    #include "LuaInline.au3"
    #include "Array.au3"

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

    _Lua_StartUp() ; == Initialisiert Lua, Shutdown automatisch bei AutoIt-Ende

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

    Global $sFunc = _
    "function GetParamCommaPos(_s)" & _
    " local tComma, tInComma, sComma, dbl = {}, {}, '', string.char(34)" & _
    " local function CheckPos(_ps, _pe)" & _
    " for i=1, #tComma do" & _
    " if (tComma[i] > _ps and tComma[i] < _pe) then tInComma[tComma[i]] = true end" & _
    " end" & _
    " end" & _
    " local tPattern = {'()(%b'..dbl..dbl..')()', '()(%b\'\')()', '()(%w+%b\(\))()'}" & _ ; == 'dblQuote', 'sglQuote', 'funcCall'
    " for pos_s, _ in _s:gmatch('()(,)') do" & _
    " table.insert(tComma, pos_s)" & _
    " end" & _
    " for i=1, #tPattern do" & _
    " for pos_s, _, pos_e in _s:gmatch(tPattern[i]) do" & _
    " CheckPos(pos_s, pos_e)" & _
    " end" & _
    " end" & _
    " for i=1, #tComma do" & _
    " if not tInComma[tComma[i]] then sComma = sComma..tostring(tComma[i])..',' end" & _
    " end" & _
    " return sComma:sub(1,#sComma-1)" & _
    "end"

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

    ;~ Parameter ist als Array zu übergeben
    Global $sStr = '"testfenster, test, test (abcd)", 10, 10, iniread("test.ini","test","test","test"),"test"'
    Global $aParam[1] = [$sStr]
    Global $aSplitPos = StringSplit(_Lua_RunFuncLocal($sFunc, 'GetParamCommaPos', $aParam, 'str'), ',')
    Global $aParam[$aSplitPos[0] +1], $n = 1, $sVal
    For $i = 1 To $aSplitPos[0]
    $sVal = StringMid($sStr, $n, $aSplitPos[$i]-$n)
    $aParam[$i-1] = StringStripWS($sVal, 1+2)
    $n = $aSplitPos[$i]+1
    Next
    $aParam[$aSplitPos[0]] = StringStripWS(StringTrimLeft($sStr, $aSplitPos[$aSplitPos[0]]) , 1+2)

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

    _ArrayDisplay($aParam)

    [/autoit]


    Wenn du das als feste Funktion nutzen möchtest, ist es effektiver die Lua Funktion als Lua-Skript mit anzuhängen und aus diesem Skript zu laden.
    "_Lua_RunFuncLocal" schreibt die Funktion auch nur in ein Temp-File und ruft darüber auf. Also verwende dann besser "_Lua_RunFuncFile".

    EDIT:

    Ich habe mal die Auslagerung in ein Lua-Skript erstellt.
    Eine Besonderheit: Das aufzurufende Lua-Skript muss mit absolutem Pfad aufgerufen werden, @ScriptDir bringt also nix. Deshalb ist der Pfad für das Lua-Skript als zusätzlicher Parameter enthalten.

    Spoiler anzeigen
    [autoit]


    #include "C:\Code_AutoIt\BEISPIELE\SplitParam.au3"
    #include "LuaInline.au3"
    #include <Array.au3>

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

    _Lua_StartUp()

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

    Local $s = '"testfenster, test, test (abcd)", 10, 10, iniread("test.ini","test","test","test"),"test"'
    Local $aRet = _SplitParameters($s, 'C:\Code_AutoIt\BEISPIELE')
    _ArrayDisplay($aRet)

    [/autoit]


    Die LuaInline.au3 sollte aber auf jeden Fall im Hauptskript geladen werden, ebenso das _Lua_StartUp(). Wenn man das auslagern würde in die SplitParam.au3, würde bei jedem Aufruf die lua.dll neu erstellt werden.