RegExp loop um XSD Schemata auszulesen

    • Offizieller Beitrag

    Hey,

    Wahrscheinlich ist es schon etwas spät, auf der anderen Seite muss ich auch zu geben, dass ich gewisse Defizite habe was Reguläre Ausdrücke angeht ;)

    Folgendes Problem: Ein riesiges Schemata (XSD Datei) soll in ein Array gelesen werden. Die Datei is wie folgt aufgebaut:

    Spoiler anzeigen

    Hier mein Versuch, ich will immer erst den namen des komplexen Typen, und dann alle namen und typen der unterelemente (können mal nur 1 mal aber auch 100 sein). Was mach ich falsch? Wenn ich statt dem + hinter der Klammer (?. ...) ein +? setze kommt das erste Element. Beim + nur das zweite Element ?(

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

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

    $sRead = '<xs:complexType name="SomeTypes">'&@CRLF & _
    '<xs:sequence>'&@CRLF & _
    '<xs:element name="SomeTag" type="SomeTagType"/>'&@CRLF & _
    '<xs:element name="SomeService" type="xs:boolean"/>'&@CRLF & _
    '</xs:sequence>'&@CRLF & _
    '</xs:complexType>'&@CRLF

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

    ;~ $aRegEx = StringRegExp($sRead,'(?ms)<xs:complexType name="(.*?)">.*?(?:<xs:element name="(.*?)" type="(.*?)"/>[^<]+)+.*?<\/xs:complexType>',3)
    $aRegEx = StringRegExp($sRead,'(?ms)<xs:complexType name="(.*?)">\r\n<xs:sequence>\r\n(?:<xs:element name="(.*?)" type="(.*?)"/>[^<]+)+<\/xs:sequence>\r\n<\/xs:complexType>',3)
    ConsoleWrite(UBound($aRegEx) & @LF)
    _ArrayDisplay($aRegEx)

    [/autoit]

    Danke im Voraus :)

    Gruß,
    Spider

  • Tut's nicht auch was einfaches?

    [autoit]

    #include <Array.au3>

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

    $sRead = '<xs:complexType name="cmplx1">' & @CRLF & _
    '<xs:sequence>' & @CRLF & _
    '<xs:element name="name1" type="type1"/>' & @CRLF & _
    '<xs:element name="nameN" type="typeN"/>' & @CRLF & _
    '</xs:sequence>' & @CRLF & _
    '</xs:complexType>' & @CRLF & _
    '<xs:complexType name="cmplxN">' & @CRLF & _
    '<xs:sequence>' & @CRLF & _
    '<xs:element name="name1" type="type1"/>' & @CRLF & _
    '<xs:element name="nameN" type="typeN"/>' & @CRLF & _
    '</xs:sequence>' & @CRLF & _
    '</xs:complexType>'

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

    $sPattern = '"(.*?)"' ; einfache Version
    ;~ $sPattern = '(?:name|type)="(.*?)"' ; ein wenig spezifischer
    ;~ $sPattern = '(?:xs:complexType name="(.*?)"|xs:element name="(.*?)" type="(.*?)")' ; naja ^^ --- Warum die Lücken im Array entstehen, keine Ahnung... :P

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

    $aRegEx = StringRegExp($sRead, $sPattern, 3)
    _ArrayDisplay($aRegEx)

    [/autoit]
    • Offizieller Beitrag

    Hey,

    Hehe, gute Idee :) Die letzte Version scheint ganz gut zu klappen, danke dir. Manchmal sieht man dem Wald vor lauter Bäumen nicht ;)

    Trotzdem würd ich gerne wissen, ob man es auch irgendwie mit meinem Lösungsweg hinbekommt? Stand oft vor der Frage, wie man vernünftig "loops" in RegExp hinbekommt.

    Gruß,
    Spider

    • Offizieller Beitrag

    Hi Spider,

    du kannst auch die ReplaceCallback-Funktion von Taz77 dazu mißbrauchen:

    Spoiler anzeigen
    [autoit]

    $string = '<xs:complexType name="cmplx1">' & @CRLF & _
    '<xs:sequence>' & @CRLF & _
    '<xs:element name="name1" type="type1"/>' & @CRLF & _
    '<xs:element name="nameN" type="typeN"/>' & @CRLF & _
    '</xs:sequence>' & @CRLF & _
    '</xs:complexType>' & @CRLF & _
    '<xs:complexType name="cmplxN">' & @CRLF & _
    '<xs:sequence>' & @CRLF & _
    '<xs:element name="name2" type="type2"/>' & @CRLF & _
    '<xs:element name="nameX" type="typeX"/>' & @CRLF & _
    '</xs:sequence>' & @CRLF & _
    '</xs:complexType>'

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

    $patt = '(?:complexType name="(.*?)">)|(?:<xs:element name="(.*?)")|(?: type="(.*?)")'

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

    Global $aMatch[1] = [0]
    _StringRegExpReplace_Callback($string, $patt, '_AddMatch("$1","$2","$3")')

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

    For $i = 1 To $aMatch[0]
    ConsoleWrite($aMatch[$i] & @LF)
    Next

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

    Func _AddMatch($1,$2,$3)
    $aMatch[0] += 1
    ReDim $aMatch[$aMatch[0]+1]
    If $1 <> '' Then
    $aMatch[$aMatch[0]] = $1
    ElseIf $2 <> '' Then
    $aMatch[$aMatch[0]] = $2
    ElseIf $3 <> '' Then
    $aMatch[$aMatch[0]] = $3
    EndIf
    EndFunc

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

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _StringRegExpReplace_Callback
    ; Description ...: Replaces ByRef all matches in a string by manipulation of the matches in a callback function
    ; Parameters ....: $sString String to manipulate
    ; ...............: $sPattern RegExp pattern for the match
    ; ...............: $sCallback Callback function as string, i.e. "MyCallback('$1')" -- '$1' represents the match
    ; .....optional..: $sBefore String to insert before the manipulated match
    ; .....optional..: $sAfter String to insert after the manipulated match
    ; Return values .: Success 1
    ; ...............: Failure 0 set error = 1
    ; Author ........: Taz77
    ; ===============================================================================================================================
    Func _StringRegExpReplace_Callback(ByRef $sString, $sPattern, $sCallback, $sBefore = '', $sAfter = '')
    $sString = Execute("'" & StringRegExpReplace(StringReplace($sString, "'", Chr(26), 0, 2), $sPattern, $sBefore & "'&" & $sCallback & "&'" & $sAfter) & "'")
    If @error Then
    ConsoleWrite('_StringRegExpReplace_Callback error! Pattern: "' & $sPattern & '", Callback: "' & $sCallback & '"' & @LF)
    Return SetError(1,0,0)
    EndIf
    $sString = StringReplace($sString, Chr(26), "'", 0, 2)
    Return 1
    EndFunc ;==>_StringRegExpReplace_Callback

    [/autoit]
    • Offizieller Beitrag

    Hey,

    Das ist ja mal eine geniale Funktion :thumbup: Ziemlich kreativ, danke dir BugFix. Musste es noch etwas ändern, da die Schemata sehr groß sind und der Execute Befehl maximal 4095 Zeichen ausführen kann, aber so klappt es:

    Spoiler anzeigen
    [autoit]


    $string = FileRead("GanzGroßesSchema.xsd")

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

    $patt = '(?:complexType name="(.*?)">)|(?:<xs:element name="(.*?)")|(?: type="(.*?)")'

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

    Global $aMatch[1] = [0]
    $aSS = StringSplit($string,"")
    For $i = 1 To $aSS[0] Step 3000
    $sBuffer = ""
    For $i2 = 0 To 2999
    If $i + $i2 > $aSS[0] Then ExitLoop
    $sBuffer &= $aSS[$i + $i2]
    Next
    _StringRegExpReplace_Callback($sBuffer, $patt, '_AddMatch("$1","$2","$3")')
    Next

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

    Func _AddMatch($1,$2,$3)
    $aMatch[0] += 1
    ReDim $aMatch[$aMatch[0]+1]
    If $1 <> '' Then
    ConsoleWrite(">"&$1 & @LF)
    $aMatch[$aMatch[0]] = $1
    ElseIf $2 <> '' Then
    ConsoleWrite("+"&$2 & @LF)
    $aMatch[$aMatch[0]] = $2
    ElseIf $3 <> '' Then
    ConsoleWrite("-"&$3 & @LF)
    $aMatch[$aMatch[0]] = $3
    EndIf
    EndFunc

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

    ; #FUNCTION# ====================================================================================================================
    ; Name ..........: _StringRegExpReplace_Callback
    ; Description ...: Replaces ByRef all matches in a string by manipulation of the matches in a callback function
    ; Parameters ....: $sString String to manipulate
    ; ...............: $sPattern RegExp pattern for the match
    ; ...............: $sCallback Callback function as string, i.e. "MyCallback('$1')" -- '$1' represents the match
    ; .....optional..: $sBefore String to insert before the manipulated match
    ; .....optional..: $sAfter String to insert after the manipulated match
    ; Return values .: Success 1
    ; ...............: Failure 0 set error = 1
    ; Author ........: Taz77
    ; ===============================================================================================================================
    Func _StringRegExpReplace_Callback(ByRef $sString, $sPattern, $sCallback, $sBefore = '', $sAfter = '')
    $sString = Execute("'" & StringRegExpReplace(StringReplace($sString, "'", Chr(26), 0, 2), $sPattern, $sBefore & "'&" & $sCallback & "&'" & $sAfter) & "'")
    If @error Then
    ConsoleWrite('_StringRegExpReplace_Callback error! Pattern: "' & $sPattern & '", Callback: "' & $sCallback & '"' & @LF)
    Return SetError(1,0,0)
    EndIf
    $sString = StringReplace($sString, Chr(26), "'", 0, 2)
    Return 1
    EndFunc ;==>_StringRegExpReplace_Callback

    [/autoit]

    Edit: Mhh, ist natürlich auch nicht schön, da hier der Buffer einfach abgeschnitten wird, aber das ist ja nicht weiter wild zu fixen ;)
    Edit 2: Mhh... So richtig zu frieden bin ich damit jetzt aber doch nicht mehr :S Schade, weil so wäre es eigentlich perfekt.. Mal schauen ob ich da noch ein Lösungsweg finde, falls jemand eine Idee hat, immer her damit :)

    Gruß,
    Spider

  • Ich hab eben schnell ne Funktion gebastelt welche das ganze in ne angenehmerer Datenstruktur einliest... :P

    Spoiler anzeigen
    [autoit]

    Local $sRawData = FileRead("test.xsd")

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

    $oFile = _ParseXSD($sRawData)

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

    For $ComplexType In $oFile ; alle ComplexTypes durchlaufen
    $oElementList = $oFile($ComplexType)
    ConsoleWrite($ComplexType & @LF)
    For $Element In $oElementList ; alle Elemente des Complextypes durchlaufen
    $name = $Element
    $type = $oElementList($Element)
    ConsoleWrite(@TAB & "name: " & $name & @TAB & "type:" & $type & @LF)
    Next
    Next

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

    Func _ParseXSD($sData)
    Local $oResult = ObjCreate("Scripting.Dictionary"), $oSubResult = ObjCreate("Scripting.Dictionary"), $aElements
    $aComplexTypes = StringRegExp($sRawData, '(?ms):complexType name="(.+?)">(.+?)<\/xs:complexType', 3)
    If @error Or Mod(UBound($aComplexTypes), 2) > 0 Then Return SetError(1)
    For $i = 0 To UBound($aComplexTypes) - 1 Step +2
    $aElements = StringRegExp($aComplexTypes[$i + 1], '"(.+?)"', 3)
    If @error Or Mod(UBound($aElements), 2) > 0 Then Return SetError(2)
    $oSubResult.RemoveAll
    For $y = 0 To UBound($aElements) - 1 Step +2
    $oSubResult.add($aElements[$y], $aElements[$y + 1])
    Next
    $oResult.add($aComplexTypes[$i], $oSubResult)
    Next
    Return $oResult
    EndFunc ;==>_ParseXSD

    [/autoit]

    LG
    Christoph :)

    • Offizieller Beitrag

    Hey Christoph,

    Gefällt mir auch gut :) Und da das Scripting Dictonary ja sogar schneller als Arrays sind, passt mir das ganz gut - danke dir :)

    Gruß,
    Spider