Funktion StringReplace ist schrecklich langsam

  • Ich habe eine String ($feld), der 24 MByte lang ist.

    In diesen möchte ich tausende kleine Strings einfügen (überschreiben). Da aber mit der Funktion StringReplace jedesmal der gesamte String $feld umgespeichert wird, dauert der Prozess ewig lange. Das umspeichern ist gar nicht notwendig, da $feld sich ja in seiner Länge nicht ändert.

    Gibt es eine Möglichkeit, die kleinen Strings an Ort und Stelle einzufügen, ohne dass $feld umgespeichet wird?

    Für einen guten Rat wäre ich sehr dankbar!

  • Du könntest auch mal versuchen die StringReplace Funktion selber zu schreiben und dann die Änderungen direkt an String machen. Du bist aber natürlich 1000mal schneller wenn du eine C-Dll nimmst.

    Gruss Shadowigor

  • Sind die Änderungen immer an festen Positionen?

    Wenn ja, müsste es doch möglich sein, diesen String einmalig als Datei zu speichern und dann per FileSetPos und Filewrite(..., Binary(...)) zu speichern.
    Oder dann gleich per WinAPI..müsste rein theoretisch alles schneller sein ?!?

    Nur so als Denkanstoss.

  • Hi,

    Zitat

    Du bist aber natürlich 1000mal schneller wenn du eine C-Dll nimmst.

    Zitat

    Oder dann gleich per WinAPI..

    Stringreplace() ist der Wrapper der entsprechenden C(++)-Funktionen ((strlen, strstr, strcpy and strcat)), WinAPI stellt keine Stringreplace-Funktionen zur Verfügung.

    24MB ist doch keine Größe, zeig doch mal das Stück Code, dass so langsam laufen soll.

    Btw, sämtliche Stringfunktionen laufen wesentlich schneller, wenn man den casesense-Parameter richtig setzt. D.H. im Falle von Casesensitivem Suchen/Ersetzen ist die 1 zu verwenden -> schneller.
    Weiterhin macht es Sinn, die Suche einzuschränken, wenn man z.B. weiss, dass der String erst in der 2. Hälfte der Daten steckt, dann hat man nur die halbe Suchzeit.
    Suche per Stringinstr() und ersetzen per StringMid() ist auch eine Alternative, da extrem schnell (wenn casesensitiv)

    Zitat

    In diesen möchte ich tausende kleine Strings einfügen (überschreiben).

    Um immer an der gleichen Possition "einzufügen" (überschreiben der dort liegenden Daten) würde ich die Positionen in ein Feld(Array/struct) schreiben und dann per StringMid()/dllstructsetdata() ersetzen.

  • Andy
    Du bist trotzdem in C schneller, da du einen C-String bzw. Char-Array nutzen kannst.
    Denn bei den AutoIt-Stringfunktionen wird "ByVal" gearbeitet, sprich (Wie bereits erwähnt) der String wird immer neu erstellt und gespeichert.
    In C bearbeitest du einfach die einzelnen Char-Elemente z.B. in einer For oder mit den C-Eigenen Stringmethoden.

    Oder habe ich da einen Denkfehler?

    chess

  • Andy
    Du bist trotzdem in C schneller, da du einen C-String bzw. Char-Array nutzen kannst.
    Denn bei den AutoIt-Stringfunktionen wird "ByVal" gearbeitet, sprich (Wie bereits erwähnt) der String wird immer neu erstellt und gespeichert.
    In C bearbeitest du einfach die einzelnen Char-Elemente z.B. in einer For oder mit den C-Eigenen Stringmethoden.

    Oder habe ich da einen Denkfehler?

    chess

    Der DLLCall kostet auch noch Zeit ;)

  • chesstiger ,
    die Frage ist, wie man "langsam" definiert^^
    Natürlich hast du recht, eine "Kopie" des Strings benötigt auch etwas Zeit.
    Du kannst es ja gerne mal in C ausprobieren. Ich schätze aber, da das Ziel immer an derselben Speicheradresse liegt und somit dafür kein neuer Speicher alloziert werden muss, dass das Memcopy() auch bei 10000 aufrufen nicht sehr viel ausmacht ;)
    In Assembler habe ich ein Memcopy() per SSE (kopiert 128Bits = 16 Bytes = 4 Dwords pro Speicherzugriff) geschrieben, das ist ca. 2x so schnell wie eine von mir untersuchte C-Funktion (die nur byte- bzw. word-weise kopiert). Aber wie gesagt, daran liegt es sicher nicht....

    Und um das umkopieren zu sparen, mein Tip mit der struct ;) und dllstructsetdata()

  • Recht vielen Dank für Euere Antworten.

    Ein Aufruf von StringReplace benötigt ca 0.05 Sekunden, wie man an untenstehendem Programm nachvollziehen kann.
    Bei 10.000 Eintagungen und mehr kommt da eine erhebliche Zeit zusammen.
    Ich habe bisher noch nicht mit einer DLL gearbeitet. Wie muss ich das anstellen? Für eine Anleitung wäre ich sehr dankbar!

    ; ==========================================================================
    Global $Protokoll1=2 ; =2, dann wird protokolliert
    ; =0, dann wird nicht protokolliert
    $ProgrammName="Test" ; für Fehlerausschriften
    Protokoll("Anfang")
    ;-------------------------------------------------------------------------
    Local $feld1=""
    Local $feld2=""
    For $i=0 To 1000
    $feld1=$feld1&"123456789012345678901234"
    Next
    For $i=0 To 1000
    $feld2=$feld2&$feld1
    Next
    Protokoll("vor1")
    Protokoll("vor2")
    StringReplace($feld2,1000,"abcdefg")
    Protokoll("nach1")
    Protokoll("nach2")

    Func Protokoll($protstelle)
    Global $protbegin
    Local $protpfad,$protdif, $protfile
    $protpfad=EnvGet("AU")&"\Protokoll.txt"
    If $Protokoll1<>0 Then
    If $Protokoll1=2 Then
    $protbegin = TimerInit()
    $protdif=0
    FileDelete($protpfad)
    Else
    $protdif = TimerDiff($protbegin)
    Endif
    $protfile = FileOpen($protpfad, $Protokoll1)
    If $protfile=-1 Then xbox("Fehler open Protokoll")
    If $Protokoll1=2 Then
    FileWrite($protfile,"Programmname: "&$Programmname& @CRLF)
    FileWrite($protfile, @YEAR&"/"&@MON&"/"&@MDAY&" "&@HOUR&"/"&@MIN&"/"&@SEC& @CRLF)
    Endif
    FileWrite($protfile, StringLeft ($protdif/1000,8)&" "&$protstelle & @CRLF)
    FileClose($protfile)
    $Protokoll1=1
    If StringMid($protstelle,1,7)="**copy:" Then
    Run(@ComSpec & " /c " & 'copy ""'&$protpfad&'"" '&StringMid($protstelle,8))
    EndIf
    Endif
    EndFunc
    ; ==========================================================================

  • Vielleicht ist sowas ja schneller. Hab aber keine Ahnung wie die Ersetzung aussehen soll, also ob nach Zeichen oder Wörtern gesucht werden soll oder einfach bestimmte regelmäßige Positionen ausgetauscht werden sollen.

    Spoiler anzeigen
    [autoit]

    #include <WinAPI.au3>

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

    Global $sFileOrig = @ScriptDir & "\Test.txt"
    Global $iFileSize = FileGetSize($sFileOrig)

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

    Global $hFile = _WinAPI_CreateFile($sFileOrig, 2, 2)

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

    Global $tBuffer = DllStructCreate("char[" & $iFileSize + 1 & "]")
    Global $iRead, $iWritten

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

    _WinAPI_ReadFile($hFile, DllStructGetPtr($tBuffer), $iFileSize, $iRead)
    _WinAPI_CloseHandle($hFile)

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

    Global $iAbstand = 10

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

    For $i = 1 To $iFileSize Step $iAbstand
    DllStructSetData($tBuffer, 1, "a", $i)
    DllStructSetData($tBuffer, 1, "b", $i + 1)
    DllStructSetData($tBuffer, 1, "c", $i + 2)
    Next

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

    Global $sFileNew = @ScriptDir & "\TestNew.txt"
    Global $hFileNew = _WinAPI_CreateFile($sFileNew, 1)
    _WinAPI_WriteFile($hFileNew, DllStructGetPtr($tBuffer), $iRead, $iWritten)
    _WinAPI_CloseHandle($hFileNew)

    [/autoit]