Ini ohne Größenbeschränkung

    • Offizieller Beitrag

    Da diese Frage aufgetaucht war, habe ich mal eine Alternative auf Tag-Basis erstellt mit allen bekannten Ini-Funktionen in exakt derselben Funktionalität.
    Ich habe hierbei bewußt auf die Verwendung von RegEx verzichtet um größtmögliche Vielfalt in Schlüssel und Wert zuzulassen.
    Vielleicht kann es jemand brauchen.

    Spoiler anzeigen
    [autoit]

    #include-once
    #include <File.au3>
    #include <String.au3>

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

    #cs ===== Funktionsliste =======================================================

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

    Erstellen / Verwalten von Schlüssel-Wert-Paaren ohne INI-Größenlimit

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

    Musterdatei:
    <sec>Sektion 1
    <key>Schlüssel 1_1<val>Wert 1_1</val></key>
    <key>Schlüssel 1_2<val>Wert 1_2</val></key>
    <key>Schlüssel 1_3<val>Wert 1_3</val></key>
    <key>Schlüssel 1_4<val>Wert 1_4</val></key>
    </sec>

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

    Funktionen sind identisch zu den bekannten Standard-Ini-Funktionen

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

    _IniEx_ReadSectionNames
    _IniEx_ReadSection
    _IniEx_Read
    _IniEx_RenameSection
    _IniEx_Write
    _IniEx_WriteSection

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

    #ce ============================================================================

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

    ;===============================================================================
    ; Function Name..: _IniEx_ReadSectionNames
    ; Description....: Liest die Sektionsnamen der IniEx-Datei aus
    ; Parameter(s)...: $sPath Pfad zur IniEx-Datei
    ; Return Value(s): Erfolg Array mit den Sektionsnamen, Anzahl in Array[0]
    ; Fehler 0 @error 1 - Datei nicht gefunden
    ; Author(s)......: BugFix ([email='bugfix@autoit.de'][/email])
    ;===============================================================================
    Func _IniEx_ReadSectionNames($sPath)
    If Not FileExists($sPath) Then Return SetError(1, 0, 0)
    Local $sRead = FileRead($sPath)
    Local $aSec = _StringBetween($sRead, '<sec>', @CRLF)
    Local $aOut[UBound($aSec) + 1]
    $aOut[0] = UBound($aSec)
    For $i = 0 To UBound($aSec) - 1
    $aOut[$i + 1] = $aSec[$i]
    Next
    Return $aOut
    EndFunc ;==>_IniEx_ReadSectionNames

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

    ;===============================================================================
    ; Function Name..: _IniEx_ReadSection
    ; Description....: Liest die angegebene Sektion aus
    ; Parameter(s)...: $sPath Pfad zur IniEx-Datei
    ; $sSection Sektionsname
    ; Return Value(s): Erfolg 2D-Array, Array[0][0] enthält Anzahl, [n][0]=Key, [n][1]=Value
    ; Fehler 0 @error 1 - Datei nicht gefunden
    ; Author(s)......: BugFix ([email='bugfix@autoit.de'][/email])
    ;===============================================================================
    Func _IniEx_ReadSection($sPath, $sSection)
    If Not FileExists($sPath) Then Return SetError(1, 0, 0)
    Local $fInSection = False, $aIni, $iKeys = 0, $aPairs[1][2] = [[$iKeys]], $aKeyVal, $sKeyVal
    _FileReadToArray($sPath, $aIni)
    For $i = 1 To $aIni[0]
    If Not $fInSection And $aIni[$i] <> '<sec>' & $sSection Then ContinueLoop
    $fInSection = True
    If $aIni[$i] = '</sec>' Then ExitLoop
    If StringLeft($aIni[$i], 5) = '<key>' Then
    $aKeyVal = _StringBetween($aIni[$i], '<key>', '</val></key>')
    If @error Then ContinueLoop
    $aKeyVal = StringSplit($aKeyVal[0], '<val>', 3)
    $iKeys += 1
    ReDim $aPairs[$iKeys + 1][2]
    $aPairs[0][0] = $iKeys
    $aPairs[$iKeys][0] = $aKeyVal[0]
    $aPairs[$iKeys][1] = $aKeyVal[1]
    EndIf
    Next
    Return $aPairs
    EndFunc ;==>_IniEx_ReadSection

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

    ;===============================================================================
    ; Function Name..: _IniEx_Read
    ; Description....: Liest den Wert für den Schlüssel einer Sektion aus
    ; Parameter(s)...: $sPath Pfad zur IniEx-Datei
    ; $sSection Sektionsname
    ; $sKey Schlüsselname
    ; $sDefault Defaultwert, wenn Key nicht gefunden
    ; Return Value(s): Erfolg Wert des Schlüssels
    ; Fehler 0 @error 1 - Datei nicht gefunden
    ; Author(s)......: BugFix ([email='bugfix@autoit.de'][/email])
    ;===============================================================================
    Func _IniEx_Read($sPath, $sSection, $sKey, $sDefault = 'Default')
    If Not FileExists($sPath) Then Return SetError(1, 0, 0)
    Local $fInSection = False, $aIni, $iKeys = 0, $aPairs[1][2] = [[$iKeys]], $aKeyVal, $sKeyVal
    _FileReadToArray($sPath, $aIni)
    For $i = 1 To $aIni[0]
    If Not $fInSection And $aIni[$i] <> '<sec>' & $sSection Then ContinueLoop
    $fInSection = True
    If $aIni[$i] = '</sec>' Then ExitLoop
    If StringLeft($aIni[$i], 5) = '<key>' Then
    $aKeyVal = _StringBetween($aIni[$i], '<key>', '</val></key>')
    If @error Then ContinueLoop
    $aKeyVal = StringSplit($aKeyVal[0], '<val>', 3)
    If $aKeyVal[0] = $sKey Then Return $aKeyVal[1]
    EndIf
    Next
    Return $sDefault
    EndFunc ;==>_IniEx_Read

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

    ;===============================================================================
    ; Function Name..: _IniEx_RenameSection
    ; Description....: Umbenennen einer Sektion
    ; Parameter(s)...: $sPath Pfad zur IniEx-Datei
    ; $sSection Sektionsname
    ; $sNewSection neuer Sektionsname
    ; $fOverwrite falls sNewSection bereits besteht wird Inhalt gelöscht, Standard=False
    ; Return Value(s): Erfolg 1
    ; Fehler 0 @error 1 - Datei nicht gefunden
    ; 2 - Sektionsname existiert nicht
    ; 3 - Sektion existiert aber Überschreib-Flag nicht gesetzt
    ; Author(s)......: BugFix ([email='bugfix@autoit.de'][/email])
    ;===============================================================================
    Func _IniEx_RenameSection($sPath, $sSection, $sNewSection, $fOverwrite = False)
    If Not FileExists($sPath) Then Return SetError(1, 0, 0)
    Local $fExists = False, $fInSection = False, $aIni, $iFirstLine = 0, $iLastLine
    _FileReadToArray($sPath, $aIni)
    For $i = 1 To $aIni[0] ; check ob $sNewSection bereits existiert
    If $aIni[$i] = '<sec>' & $sNewSection Then
    $fExists = True
    ExitLoop
    EndIf
    Next
    For $i = 1 To $aIni[0]
    If Not $fInSection And $aIni[$i] = '<sec>' & $sSection Then ; Sektion Startzeile suchen
    $fInSection = True
    $iFirstLine = $i
    ContinueLoop
    EndIf
    If $fInSection And Not $fExists Then ExitLoop ; $sNewSection existiert noch nicht >> Endzeile braucht nicht ermittelt werden
    If $fInSection And $aIni[$i] = '</sec>' Then ; Sektion Endzeile suchen
    $iLastLine = $i
    $fInSection = False
    ExitLoop
    EndIf
    Next
    If $iFirstLine = 0 Then Return SetError(2, 0, 0) ; Sektionsname nicht enthalten
    If Not $fExists Then Return _FileWriteToLine($sPath, $iFirstLine, '<sec>' & $sNewSection, 1)
    If $fExists And Not $fOverwrite Then
    Return SetError(3, 0, 0) ; $sNewSection existiert aber Überschreib-Flag nicht gesetzt
    Else ; alte Sektion kpl. löschen
    For $i = $iLastLine To $iFirstLine Step -1
    _FileWriteToLine($sPath, $iFirstLine, '', 1)
    Next
    EndIf
    _FileReadToArray($sPath, $aIni)
    For $i = 1 To $aIni[0]
    If Not $fInSection And $aIni[$i] = '<sec>' & $sNewSection Then ; Sektion Startzeile suchen
    $fInSection = True
    $iFirstLine = $i
    ContinueLoop
    EndIf
    If Not $fExists Then ExitLoop ; $sNewSection existiert noch nicht >> Endzeile braucht nicht ermittelt werden
    If $fInSection And $aIni[$i] = '</sec>' Then ; Sektion Endzeile suchen
    $iLastLine = $i
    $fInSection = False
    ExitLoop
    EndIf
    Next
    For $i = $iLastLine - 1 To $iFirstLine + 1 Step -1 ; Sektion mit selbem Namen, wie $sNewSection leeren
    _FileWriteToLine($sPath, $i, '', 1)
    Next
    Return 1
    EndFunc ;==>_IniEx_RenameSection

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

    ;===============================================================================
    ; Function Name..: _IniEx_Write
    ; Description....: Schreibt einen neuen Wert in einen Schlüssel oder fügt ein neues Schlüssel-Wert-Paar ein
    ; Parameter(s)...: $sPath Pfad zur IniEx-Datei
    ; $sSection Sektionsname
    ; $sKey Schlüsselname
    ; $sValue Wert
    ; Return Value(s): Erfolg Letzter Wert des Schlüssels, bei Neuanlage Leerstring
    ; Fehler 0 @error 1 - Datei nicht gefunden
    ; 2 - Sektion existiert nicht
    ; Author(s)......: BugFix ([email='bugfix@autoit.de'][/email])
    ;===============================================================================
    Func _IniEx_Write($sPath, $sSection, $sKey, $vValue)
    If Not FileExists($sPath) Then Return SetError(1, 0, 0)
    Local $fInSection = False, $aIni, $aKeyVal, $vOldVal = '', $fKeyNew = False, $iKeyLine = 0
    _FileReadToArray($sPath, $aIni)
    For $i = 1 To $aIni[0]
    If Not $fInSection And $aIni[$i] = '<sec>' & $sSection Then ; Sektion Startzeile suchen
    $fInSection = True
    ContinueLoop
    EndIf
    If $fInSection And StringLeft($aIni[$i], 5) = '<key>' Then
    $aKeyVal = _StringBetween($aIni[$i], '<key>', '</val></key>')
    $aKeyVal = StringSplit($aKeyVal[0], '<val>', 3)
    If $aKeyVal[0] = $sKey Then
    $iKeyLine = $i
    $vOldVal = $aKeyVal[1]
    ExitLoop
    EndIf
    EndIf
    If $fInSection And $aIni[$i] = '</sec>' Then ; Sektionsende
    If $iKeyLine = 0 Then
    $fKeyNew = True
    $iKeyLine = $i
    EndIf
    ExitLoop
    EndIf
    Next
    If Not $fInSection Then Return SetError(2, 0, 0) ; Sektion existiert nicht
    If $fKeyNew Then
    _FileWriteToLine($sPath, $iKeyLine, '<key>' & $sKey & '<val>' & $vValue & '</val></key>')
    Else
    _FileWriteToLine($sPath, $iKeyLine, '<key>' & $sKey & '<val>' & $vValue & '</val></key>', 1)
    EndIf
    Return $vOldVal ; Rückgabe alter Wert
    EndFunc ;==>_IniEx_Write

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

    ;===============================================================================
    ; Function Name..: _IniEx_WriteSection
    ; Description....: Schreibt eine Sektion in die IniEx-Datei
    ; Parameter(s)...: $sPath Pfad zur IniEx-Datei
    ; $sSection Sektionsname
    ; $vData String (Schlüssel=Wert, mehrere mit @LF trennen) oder Array [n][0]= Schlüssel, [n][1]= Wert
    ; $iIndex Falls Array übergeben, der Startindex (Default=1)
    ; Return Value(s): Erfolg 1
    ; Fehler 0 @error 1 - Datei nicht gefunden
    ; 2 - kein 2D-Array übergeben
    ; Author(s)......: BugFix ([email='bugfix@autoit.de'][/email])
    ;===============================================================================
    Func _IniEx_WriteSection($sPath, $sSection, $vData, $iIndex = 1)
    If Not FileExists($sPath) Then Return SetError(1, 0, 0)
    Local $aIni, $aTmp, $aSplit, $aData[1][2] = [[0]], $sSectionData
    Local $fInSection = False, $iFirstLine = 0, $iLastLine, $sRead, $fh
    If Not IsArray($vData) Then ; Daten in Array packen
    $aTmp = StringSplit($vData, @LF, 1)
    ReDim $aData[$aTmp[0] + 1][2]
    $aData[0][0] = $aTmp[0]
    For $i = 1 To $aTmp[0]
    $aSplit = StringSplit($aTmp[$i], '=')
    $aData[$i][0] = $aSplit[1]
    $aData[$i][1] = $aSplit[2]
    Next
    Else
    If UBound($aData, 2) <> 2 Then Return SetError(2, 0, 0)
    $aData = $vData
    EndIf
    _FileReadToArray($sPath, $aIni)
    For $i = 1 To $aIni[0] ; check ob $sSection bereits existiert
    If $aIni[$i] = '<sec>' & $sSection Then
    $iFirstLine = $i
    $fInSection = True
    EndIf
    If $fInSection And $aIni[$i] = '</sec>' Then ; Sektion Endzeile suchen
    $iLastLine = $i
    $fInSection = False
    EndIf
    Next
    $sSectionData = '<sec>' & $sSection & @CRLF ; zu schreibenden Sektions-String zusammenstellen
    For $i = $iIndex To UBound($aData) - 1
    $sSectionData &= '<key>' & $aData[$i][0] & '<val>' & $aData[$i][1] & '</val></key>' & @CRLF
    Next
    $sSectionData = $sSectionData & '</sec>'
    If $iFirstLine > 0 Then ; Sektion besteht bereits
    For $i = $iLastLine To $iFirstLine + 1 Step -1
    _FileWriteToLine($sPath, $iFirstLine, '', 1)
    Next
    Return _FileWriteToLine($sPath, $iFirstLine, $sSectionData, 1) ; Sektion neu schreiben
    Else ; neue Sektion am Ende anfügen
    $fh = FileOpen($sPath, 1)
    FileWrite($fh, @CRLF & $sSectionData)
    Return FileClose($fh)
    EndIf
    EndFunc ;==>_IniEx_WriteSection

    [/autoit]
    • Offizieller Beitrag

    Ich verstehe deine Frage nicht. Natürlich mußt du per Referenzpunkt die Inhalte separieren. Wenn du das mit RegEx löst mußt du für alle Eventualitäten maskieren. Dadurch wird das Pattern greedy und der Geschwindigkeitsvorteil gegenüber Stringoperationen ist dahin. ;)

  • Naja, du sagtest dass du auf RegExp verzichtet hast. Intern ist aber jeder _StringBetween ein RegExp - und zwar auch noch einer mit viel Backtracking. (.+?) ist nämlich ziemlich übel für die RegExp Performance, wenn wir schon von Performance reden.

    Code
    <key>(.+?)<val>


    autoit.de/wcf/attachment/12721/

    Code
    <key>([^<]+)<val>


    autoit.de/wcf/attachment/12722/

    Die zweite Variante (die so nicht von _StringBetween verwendet wird) ist wesentlich schneller - das wirkt sich besonders bei langen "Schlüssel"- oder "Wert"-Einträgen aus. Was passiert, wenn da dann auch noch 5.000 solcher langen Schlüssel oder Werte in einer Datei rumlungern, brauch ich dir ja nicht sagen. ;)

    Und das maskieren selbst ist mit \Q und \E leicht erledigt.
    Wenn du also schon auf RegExp verzichtest, dann auch bitte ganz :P

    Und wenn du es dir anders überlegst - Der RegExpBuddy liefert im "Debug"-Tab eine gute Anlaufstelle zur Performanceoptimierung.


    Achso - das soll jetzt in keinster Weise frech o.ä. rüberkommen. Ich will nur helfen ^^

    • Offizieller Beitrag

    Achso - das soll jetzt in keinster Weise frech o.ä. rüberkommen. Ich will nur helfen


    :D He, so schnell bin ich nicht angepißt. Hier hatte ich einfach verpennt mal in _StringBetween selbst reinzuschauen, denn die macht genau das, was ich vermeiden wollte. :huh:
    Naja, irgendwie bin ich wohl auch nur ein Mensch. :whistling:

  • Ich dachte immer es sei selbstverständlich dass _StringBetween RegEx ist :D
    Danke dass du auch nur ein Mensch bist :)

    Es gibt sehr viele Leute, die glauben. Aber aus Aberglauben.
    - Blaise Pascal