Grep Routine ?

Für allgemeine Fragen zur Programmierung mit PureBasic.
TWELVE
Beiträge: 72
Registriert: 01.04.2007 18:33

Grep Routine ?

Beitrag von TWELVE »

Guten Abend zusammen,

ich benötige etwas Hilfe mit einer neuen Funktion, die ich einbauen will.

Aufgabenstellung:
------------------------

Ich will Zeilen in Logfiles nach dem Auftreten bestimmter Strings durchsuchen und dann alle Zeilen mit dem Suchbegriff zusammenstellen ( die Textzeilen, nicht die Zeilennummern).
Exakt das, was grep auf einem Linux System macht.

Also z.B.:

grep "error" mylog.log

Gleiches geht aber z.B. auch mit Notepad++ ( "Find all in current document")

In meiner Applikation ist das Logfile bereits in ein EditorGadget eingelesen, wenn der Grep-Filter auf den darin befindlichen Text angewendet werden soll.Das Resultat der Grep-Funktion wird
dann wieder in das EditorGadget kopiert.

Zeilennummern der Übereinstimmungen wären ein netter Nebeneffekt, interessieren mich aber im Moment eher weniger.

Ich habe erstmal klassisch angefangen, mit Findstring und ohne Tricks:

Code: Alles auswählen

Procedure.s GrepText(text.s, greptext.s)
   
 GrepStartTime = ElapsedMilliseconds()   
   
 searchstart = 1  
    
 While FindString(text.s, greptext.s, searchstart) ; search the grep string
  
                  
             StartPos = FindString(text.s,greptext.s, searchstart)
             EndPos = FindString( text.s, #CRLF$, Startpos)
             
             
              For pos = StartPos To 1 Step -1
                                              
                startofline = FindString(text.s, #CRLF$, pos) 
               
                  If startofline >0 And startofline < EndPos
                 
                     startofline+2
                 
                       Length =  Endpos - startofline
                         grepline.s = grepline.s + Mid(text.s, startofline, Length) + #CRLF$    
                          searchstart = EndPos+1
                    Break
             
                EndIf             
             
             Next pos
               
   Wend
   
     ElapsedTime.d = ElapsedMilliseconds()-GrepStartTime 
   
     ElapsedTime.d = ElapsedTime.d/1000
   
    Debug "Grep process took "+ElapsedTime.d+" seconds to find "+match+" Matches in "+ListSize(LogList())+" Lines"
   
 
 ProcedureReturn grepline.s  
   
EndProcedure  
 
text.s enthält das komplette Logfile und greptext.s den String, auf den gefiltert wird ( "gegrepped" wird).

Also ich suche den Suchstring, dann das darauf folgende CRLF, um das Zeilenende zu bekommen.Dann such ich mit einer Schleife rückwärts nach dem
vorherigen CRLF, um den Zeilenanfang zu bekommen.

Dass funktioniert, ist aber für meine Anwendung ( Logfiles mit mehreren MBytes, in extremen Fällen auch mal 100Mbyte, mehrere tausend Zeilen) viiiiieeeeel zu langsam.

Für ein gegebenes Logfile mit + 4.5 Mbyte und 50241 Zeilen benötigt diese Routine 245 Sekunden, um 2700 Matches zu finden und in einen String zusammenzustellen.

Also habe ich mal einen anderen Ansatz probiert:

Code: Alles auswählen

Global NewList LogList.s()

Procedure.s FastGrepText(LogFileName.s, greptext.s)
  
  FastGrepStartTime = ElapsedMilliseconds() ; ermittelt die aktuelle Zeit
  
  FileHandle_log = ReadFile(#PB_Any, LogFileName.s)

If FileHandle_log
  While Not Eof(FileHandle_log)
    AddElement(LogList()) : LogList() = ReadString(FileHandle_log)
  Wend
  CloseFile(FileHandle_log)
EndIf

  
  
  

If ListSize(LogList())
  
  ForEach LogList()
    If FindString(LogList(), greptext.s)
       match+1
      
      greppedText.s +LogList()+#CRLF$ 
        
      EndIf
  Next
  
EndIf



     ElapsedTime.d = ElapsedMilliseconds()-FastGrepStartTime 
   
     ElapsedTime.d = ElapsedTime.d/1000
   
     Debug "FastGrep process took "+ElapsedTime.d+" seconds to find "+match+" Matches in "+ListSize(LogList())+" Lines"

     
     ClearList(LogList())      
     
     ProcedureReturn greppedText.s
  
EndProcedure  


Das Logfile wird eingelesen und dabei über ReadString() eine List befüllt.Da jedes Element der List nun eine Zeile des Logfiles ist, ist es einfach, auf Zeilenbasis zu arbeiten.
Die gleiche Suche dauert damit nur 0.85 Sekunden, was schon sehr viel schneller ist.Allerdings ist so, dass ich das Logfile eigentlich bereits vorher schon eingelesen habe
und der komplette Inhalt in einem EditorGadget existiert, wenn die Grep-Routine aufgerufen wird.Daher war der natürliche Ansatz, den Text des EditorGadgets zu nehmen und zu
durchsuchen, was aber eben um Größenordnungen zu langsam ist.

Das erneute Einlesen des Logfiles wäre akzeptabel für mich, da es verglichen mit der Suche wenig Zeit kostet, ist aber suboptimal, da die Zeit mit Größe des Files auch skaliert..Außerdem ist selbst die Routine mit der List immer noch zu langsam.Bei gleichem Logfile wie oben dauert das finden von 28.774 Matches dann bereits 71 Sekunden.

Bei Notepad++ ist das schweineschnell, da dauert die Suche mit den 28.000 Matches keine Sekunde.

Meine App dreht sich um das Anschauen von Logfiles und ich habe vieles auf Geschwindigkeit optimiert ( innerhalb meiner Möglichkeiten ) , ich finde warten auf eine Software
nervig, wie die meisten von uns.

ich habe natürlich erstmal ein paar Tage das Forum durchforstet, interessante Dinge gefunden, aber nichts, was auf meine Aufgabenstellung genau paßt.

Hat jemand eine Idee, wie man es schneller machen kann ?
Benutzeravatar
Kiffi
Beiträge: 10621
Registriert: 08.09.2004 08:21
Wohnort: Amphibios 9

Re: Grep Routine ?

Beitrag von Kiffi »

PureBasic ist beim Verknüpfen von Strings leider keine Rakete. Deswegen wird der Löwenanteil der Laufzeit wohl in der Zeile

Code: Alles auswählen

greppedText.s +LogList()+#CRLF$ 
verschwendet. Wesentlich schneller wäre es, die gefundenen Zeilen direkt in Dein EditorGadget einzutragen:

Code: Alles auswählen

[...]
Line.s = ReadString(FileHandle_log)
If FindString(Line, greptext.s)
  AddGadgetItem(DeinEditorGadget, -1, Line)
EndIf
[...]
Grüße ... Peter
Hygge
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Grep Routine ?

Beitrag von STARGÅTE »

Ein weiterer Ansatz wäre reguläre Ausdrücke zu verwenden.
Pure Basic bietet hier selbst eine Library an: RegularExpression

Dort kannst du ein regulären Ausdruck erstellen, um einen bestimmten String zu suchen und die Zeilen ausgeben lassen:

Code: Alles auswählen

Enumeration
	#RegularExpression
EndEnumeration

Define Text.s = "(Sun Sep 13 23:02:05 2009): Beginning Wbemupgd.dll Registration"+#CRLF$+"(Sun Sep 13 23:02:05 2009): Current build of wbemupgd.dll is 5.1.2600.2180 (xpsp_sp2_rtm.040803-2158)"+#CRLF$+"(Sun Sep 13 23:02:05 2009): Beginning Core Upgrade"+#CRLF$+"(Sun Sep 13 23:02:05 2009): Beginning MOF load"+#CRLF$+"(Sun Sep 13 23:02:05 2009): Processing C:\WINDOWS\system32\WBEM\cimwin32.mof"+#CRLF$+"(Sun Sep 13 23:02:09 2009): Processing C:\WINDOWS\system32\WBEM\cimwin32.mfl"+#CRLF$+"(Sun Sep 13 23:02:12 2009): Processing C:\WINDOWS\system32\WBEM\system.mof"+#CRLF$+"(Sun Sep 13 23:02:16 2009): Processing C:\WINDOWS\system32\WBEM\evntrprv.mof"+#CRLF$+"(Sun Sep 13 23:02:16 2009): Processing C:\WINDOWS\system32\WBEM\hnetcfg.mof"+#CRLF$+"(Sun Sep 13 23:02:16 2009): Processing C:\WINDOWS\system32\WBEM\sr.mof"+#CRLF$+"(Sun Sep 13 23:02:16 2009): Processing C:\WINDOWS\system32\WBEM\dgnet.mof"+#CRLF$+"(Sun Sep 13 23:02:16 2009): Processing C:\WINDOWS\system32\WBEM\whqlprov.mof"+#CRLF$+"(Sun Sep 13 23:02:16 2009): Processing C:\WINDOWS\system32\WBEM\ieinfo5.mof"+#CRLF$+"(Sun Sep 13 23:02:17 2009): MOF load completed."+#CRLF$+"(Sun Sep 13 23:02:17 2009): Beginning MOF load"+#CRLF$+"(Sun Sep 13 23:02:17 2009): MOF load completed."+#CRLF$+"(Sun Sep 13 23:02:17 2009): Core Upgrade completed."+#CRLF$+"(Sun Sep 13 23:02:17 2009): Wbemupgd.dll Service Security upgrade succeeded."+#CRLF$+"(Sun Sep 13 23:02:17 2009): Beginning WMI(WDM) Namespace Init"+#CRLF$+"(Sun Sep 13 23:02:20 2009): WMI(WDM) Namespace Init Completed"+#CRLF$+"(Sun Sep 13 23:02:20 2009): ESS enabled"+#CRLF$+"(Sun Sep 13 23:02:20 2009): ODBC Driver <system32>\wbemdr32.dll not present"+#CRLF$+"(Sun Sep 13 23:02:20 2009): Successfully verified WBEM OBDC adapter (incompatible version removed if it was detected)."+#CRLF$+"(Sun Sep 13 23:02:20 2009): Wbemupgd.dll Registration completed."
Define Suche.s = "load"

Debug "Logdatei:"
Debug Text

Debug ""
Debug "Suche nach: "+Suche

Debug ""
Debug "Funde:"
If CreateRegularExpression(#RegularExpression, ".*"+Suche+".*")
	If ExamineRegularExpression(#RegularExpression, Text)
		While NextRegularExpressionMatch(#RegularExpression)
			Debug RegularExpressionMatchString(#RegularExpression)
		Wend
	EndIf
EndIf
Im übrigen steigt die Zeit so dramatisch bei dir an, weil du diesen String (der sehr lang wird) immer wieder um etwas erweiterst.
Damit meine ich, dass Codes wie: "greppedText.s +LogList()+#CRLF$" bei langen Strings sehr lange dauern kann.
Hier könnte man vielleich CopyMemoryString()
um diesen ErgebnisString schneller zusammen zu stellen, indem vorher schon ein gewisser großer Platz reserviert wird.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Benutzeravatar
Sicro
Beiträge: 955
Registriert: 11.08.2005 19:08
Kontaktdaten:

Re: Grep Routine ?

Beitrag von Sicro »

Code: Alles auswählen

LogFile$ = GetTemporaryDirectory() + "LogFile.txt"

; Logdatei erstellen
If Not CreateFile(0, LogFile$)
  MessageRequester("", "Error: CreateFile()", #PB_MessageRequester_Error)
  End
EndIf
For i = 1 To 50000
  Select Random(1)
    Case 0 : WriteStringN(0, "Das ist ein Text.")
    Case 1 : WriteStringN(0, "Das ist NOCH ein Text.")
  EndSelect
Next
CloseFile(0)

If Not OpenWindow(0, #PB_Ignore, #PB_Ignore, 400, 400, "Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  MessageRequester("", "Error: OpenWindow()", #PB_MessageRequester_Error)
  End
EndIf

EditorGadget(0, 0, 0, WindowWidth(0), WindowHeight(0) - 80)
ButtonGadget(1, 100, 330, 200, 25, "Suche 'NOCH'")

; Logdatei einlesen
If Not ReadFile(0, LogFile$)
  MessageRequester("", "Error: ReadFile()", #PB_MessageRequester_Error)
  End
EndIf
SetGadgetText(0, ReadString(0, #PB_File_IgnoreEOL))
CloseFile(0)

Global MemoryStringLength
Procedure.i AddString(*Memory, String$)
  Protected StringLength = StringByteLength(String$)
  Protected *NewMemory
  
  If *Memory = 0 Or MemorySize(*Memory) <= (MemoryStringLength + StringLength)
    *NewMemory = ReAllocateMemory(*Memory, MemoryStringLength + StringLength + 1024*1024)
    If *NewMemory = 0
      ProcedureReturn #False
    EndIf
    *Memory = *NewMemory
  EndIf
  PokeS(*Memory + MemoryStringLength, String$)
  MemoryStringLength + StringLength
  
  ProcedureReturn *Memory
EndProcedure
Procedure$ GetString(*Memory)
  ProcedureReturn PeekS(*Memory, MemoryStringLength / SizeOf(Character))
EndProcedure

Repeat
  Event = WaitWindowEvent()
  If Event = #PB_Event_Gadget And EventGadget() = 1
    
    ; Zeichenfolge 'NOCH' suchen
    StartTime = ElapsedMilliseconds()
    Count = CountGadgetItems(0)
    For i = 0 To Count
      EntryText$ = GetGadgetItemText(0, i)
      If FindString(EntryText$, "NOCH")
        *Result = AddString(*Memory, "Gefunden: " + EntryText$ + #CRLF$)
        If *Result = 0
          FreeMemory(*Memory)
          MessageRequester("", "Error: AddString()")
          End
        EndIf
        *Memory = *Result
      EndIf
    Next
    SetGadgetText(0, GetString(*Memory))
    EndTime = ElapsedMilliseconds()
    AddGadgetItem(0, -1, #CRLF$ + "Benötigte Zeit: " + Str(EndTime-StartTime) + " ms")
    
  EndIf
Until Event = #PB_Event_CloseWindow
Bild
Warum OpenSource eine Lizenz haben sollte :: PB-CodeArchiv-Rebirth :: Pleasant-Dark (Syntax-Farbschema) :: RegEx-Engine (kompiliert RegExes zu NFA/DFA)
Manjaro Xfce x64 (Hauptsystem) :: Windows 10 Home (VirtualBox) :: Neueste PureBasic-Version
TWELVE
Beiträge: 72
Registriert: 01.04.2007 18:33

Re: Grep Routine ?

Beitrag von TWELVE »

Kiffi hat geschrieben:PureBasic ist beim Verknüpfen von Strings leider keine Rakete. Deswegen wird der Löwenanteil der Laufzeit wohl in der Zeile

Code: Alles auswählen

greppedText.s +LogList()+#CRLF$ 
verschwendet. Wesentlich schneller wäre es, die gefundenen Zeilen direkt in Dein EditorGadget einzutragen:

Code: Alles auswählen

[...]
Line.s = ReadString(FileHandle_log)
If FindString(Line, greptext.s)
  AddGadgetItem(DeinEditorGadget, -1, Line)
EndIf
[...]
Grüße ... Peter
Das ist leider noch viel langsamer als meine erste Grep-Routine, nämlich 721 Sekunden für die 2700 Matches aus obigem Beispiel....
Meiner Meinung nach kann der Zugriff auf ein Gadget nicht schneller sein, als Text in einen String zu schreiben.

Wenn ich die Finds in einen String schreibe und dann den String ins EditorGadget, ist die Geschwindigkeit ziemlich genau vergleichbar mit der List Methode
( 0.9 / 75 Sekunden).Bei der Anzahl der Zeilen habe ich oben eine Stelle unterschlagen, es sind 502411.

Code: Alles auswählen

Procedure.s FastGrepText(LogFileName.s, greptext.s)
  
  FastGrepStartTime = ElapsedMilliseconds() ; ermittelt die aktuelle Zeit
  

  FileHandle_log = ReadFile(#PB_Any, LogFileName.s)

If FileHandle_log
  While Not Eof(FileHandle_log)
    Line.s = ReadString(FileHandle_log)
    readcount+1
If FindString(Line.s, greptext.s)
  greppedText.s + Line.s + #CRLF$
  match+1
EndIf  
  
    
  Wend
  CloseFile(FileHandle_log)
EndIf
  

     ElapsedTime.d = ElapsedMilliseconds()-FastGrepStartTime 
   
     ElapsedTime.d = ElapsedTime.d/1000
   
     PrintX("FastGrep process took "+ElapsedTime.d+" seconds to find "+match+" Matches in "+readcount+1+" Lines",12)

     
         
     ProcedureReturn greppedText.s



Die Idee hinter der List war auch, diese mehrfach verwenden zu können.Das Einlesen des Files inkl. der AddElement der List dauert nur rund eine Sekunde, aber dann das
ForEach LogList() die restlichen 70 Sekunden.Bringt also defacto nichts, wenn ich das gleiche Ergebnis beim Einlesen mit FindString() erreichen kann.

Danke für Deinen Vorschlag, das hat mich wieder auf neue Ideen gebracht...
TWELVE
Beiträge: 72
Registriert: 01.04.2007 18:33

Re: Grep Routine ?

Beitrag von TWELVE »

STARGÅTE hat geschrieben:Ein weiterer Ansatz wäre reguläre Ausdrücke zu verwenden.
Pure Basic bietet hier selbst eine Library an: RegularExpression

Dort kannst du ein regulären Ausdruck erstellen, um einen bestimmten String zu suchen und die Zeilen ausgeben lassen:

...

Im übrigen steigt die Zeit so dramatisch bei dir an, weil du diesen String (der sehr lang wird) immer wieder um etwas erweiterst.
Damit meine ich, dass Codes wie: "greppedText.s +LogList()+#CRLF$" bei langen Strings sehr lange dauern kann.
Hier könnte man vielleich CopyMemoryString()
um diesen ErgebnisString schneller zusammen zu stellen, indem vorher schon ein gewisser großer Platz reserviert wird.
Gute Idee ! Ich hatte mir die Regex auch schon angesehen, dann aber wieder verworfen.
Aktuell verstehe ich nicht, warum das funktioniert.Warum gibt Regex die komplette Zeile der Fundstelle zurück ?
Das geht für mich aus der Hilfe nicht hervor.

Leider muß ich das

Debug RegularExpressionMatchString(#RegularExpression)

dann noch mit einer Stringoperation erweitern, um meinen gefilterten Text zu bekommen.Dadurch wird es wieder
langsamer, aber ist mit 58 Sekunden für die obige Suche mit 28.000 Matches bisher noch die schnellste Variante.
( den Vorschlag von Sicro muß ich erst noch testen).Dennoch immer noch viel zu langsam.

Auch habe ich den merkwüdigen Effekt, dass die Regex Suche pro Programmlauf immer nur beim ersten Mal was findet.
Die Ursache dafür habe ich bisher nicht herausgefunden.

Wenn ich die Methode mit der List() und die mit Regex ohne die Stringoperation nur mit einem Debug Statement
als exe kompiliert teste, dann braucht die Regex Methode 0.46 Sekunden für 28.000 matches in 50.000 Loglines,
während die List-Methode nur 0.101 Sekunden benötigt, inklusive erneutem Einlesen des Logfiles.

Wie Du schon richtig sagst, scheint die Zeit hauptsächlich beim Erzeugen des Ergebnisstrings verbraten zu werden.
Ich weiß nur leider nicht, wie ich das massiv beschleunigen kann, ich kenne mich mit den Memory-Funtionen zu wenig aus.
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Grep Routine ?

Beitrag von ts-soft »

Nur als Anregung: Wie wäre es mit Streaming? Den Text in ein EditorGadget streamen und während dessen,
im Callback auswerten? So wird immer nur ein Teil des Textes durchsucht, der Teil der gerade ins EditorGadget geschrieben
wird.

Hab leider keine Code-Beispiele und finde nur uralte im CodeArchivViewer. Aber vom Prinzip sollte das eine der schnellsten
Methoden sein. Zumindest das Streamen in ein EditorGadget ist immer noch die schnellste Methode um seinen Text da rein
zu bekommen :wink:

Gruß
Thomas
PureBasic 5.73 LTS | SpiderBasic 2.30 | Windows 10 Pro (x64) | Linux Mint 20.1 (x64)
Nutella hat nur sehr wenig Vitamine. Deswegen muss man davon relativ viel essen.
Bild
TWELVE
Beiträge: 72
Registriert: 01.04.2007 18:33

Re: Grep Routine ?

Beitrag von TWELVE »

Ich habe gerade mal STARGÅTEs Ansatz mit CopyMemoryString() ausprobiert, das ist sauschweineschnell :-)

Für die 28.000 Matches nur 0.451 Sekunden mit Ergebnis im Zielstring unter Verwendung der Regex Suche:

Code: Alles auswählen



; text.s enthält den kompletten Log
; greptext.s enthält den Suchbegriff 

*Buffer = AllocateMemory(7000000)
  *Pointer = *Buffer

myregex = CreateRegularExpression(#PB_Any, ".*"+greptext.s+".*")  

If myregex
   If ExamineRegularExpression(myregex, text.s)
      While NextRegularExpressionMatch(myregex)
        ;grepline.s + RegularExpressionMatchString(myregex)
        CopyMemoryString(RegularExpressionMatchString(myregex), @*Pointer)
        
        ;Debug RegularExpressionMatchString(myregex)
         ;match+1
      Wend
   EndIf
 EndIf 
 
 grepline.s = PeekS(*Buffer)
Mit der List() Methode dauert es nur 0.114 Sekunden :-)

Das ist genau das, was ich gesucht habe, eine dramatische Beschleunigung !!!

AllocateMemory(7000000) ist nur zum testen, da ich nicht wußte, wie ich die benötigte Memorylänge ermitteln kann.

Das hier hat nicht funktioniert:

Len = MemoryStringLength(*text)

Gibt Länge Null zurück.

Der Nachteil an der List() Methode ist, dass es so nur mit dem erneuten Einlesen des Logfiles funktioniert, daher hätte ich
ganz gerne, wenn das mit dem Regex auch bei mehrfacher Suche funktioniert.

Damit hätte ich noch zwei Probleme:

- wie ermittle ich die benötigte Speichergröße ?
- Regex funktioniert nur beim ersten Aufruf
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Grep Routine ?

Beitrag von STARGÅTE »

Also maximale Speichergröße hätte ich jetzt die Byte-Länge der Logdatei genommen, da es ja nur so viele Matches geben kann, wie es maximal Text gibt.

>> "Regex funktioniert nur beim ersten Aufruf"
Das verstehe ich nicht, wie meinst du das?
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
TWELVE
Beiträge: 72
Registriert: 01.04.2007 18:33

Re: Grep Routine ?

Beitrag von TWELVE »

STARGÅTE hat geschrieben:Also maximale Speichergröße hätte ich jetzt die Byte-Länge der Logdatei genommen, da es ja nur so viele Matches geben kann, wie es maximal Text gibt.

>> "Regex funktioniert nur beim ersten Aufruf"
Das verstehe ich nicht, wie meinst du das?
Wenn ich den Regex Konstrukt in meiner Grep Proc aufrufe, klappt das nur beim ersten Mal.Bei allen weiteren Aufrufen der Grep Proc
scheint das Erzeugen der Regex dann fehlzuschlagen:

If CreateRegularExpression(#RegularExpression, ".*"+Suche+".*")

Damit kommt die Grep Proc dann ohne Ergebnis zurück.

Ich habe verschiedenes probiert, Erzeugung mit #PB_Any und FreeRegularExpression(myregex) an Ende der Procedur, aber
hat nichts geändert.


Edit: muß mich korrigieren, die Regex wird jedesmal erfolgreich erzeugt, und auch das If ExamineRegularExpression
wird als true abgearbeitet, daher schein es so, dass While NextRegularExpressionMatch(myregex) keine Matches mehr findet.
Antworten