Mein bisheriger Code:
'Prüfung auf unzulässige Zeichen ASCII-Wert bis &h20
for j=0 to 31
if InStr(s,chr(j))>0 then
if j=9 then //TAB
MeldungHinzu("Die Zeile enthält ein unzulässiges Tabulator Zeichen")
else
MeldungHinzu("Die Zeile enthält ein unzulässiges Zeichen.")
end
end
next
Heute eine Datei mit über 13 Millionen Zeilen überprüft und erkannt, dass ich hier Optimierungsbedarf habe
Der Hinweis auf Tab könnte auch raus, das hält nur auf.
Vor das letzte “end” könnte ich noch ein Exit setzen, hilft aber wenig, da die Routine eher selten überhaupt Fehler feststellt.
Hat jemand eine deutlich schnellere Lösung?
Vielleicht Irgendwas mit Memoryblock oder so?
du könntest alternativ auch erstmal folgendes prüfen. Jedes Zeichen durchgehen und prüfen, ob der der Wert <32 ist. Und in dieser Methode alle #pragma backgroundtask usw. ausschalten. Könnte mir vorstellen, dass es schneller ist als für jedes gesuchte Zeichen die Instr-Funktion aufzurufen.
Spielt Unicode eine Rolle bzw. welches Encoding haben die Zeichen?
Wow. Das bringt einiges!
Mein Problem ist, dass der zu prüfende Text UTF-8 codiert ist. Damit kommt eine byteweise Prüfung wohl doch nicht in Frage.
Ich präzisiere mein Problem sicherheitshalber mal.
Gegeben ist eine Textdatei, die zeilenweise auf Einhaltung von Vorgaben überprüft werden soll. Eine Vorgabe ist: Keine Zeichen mit einem Wert <&h20
Mein Favorit wäre derzeit Split in abgewandelter Variante.
Allerdings ist die Variante langsamer als meine bisherige.
Vorher (712Ticks):
for i=0 to 150000
s=Zeilen(i)
'Prüfung auf unzulässige Zeichen ASCII-Wert bis &h20
for j=0 to 31
if InStr(s,chr(j))>0 then
if j=9 then //TAB
MeldungHinzu
else
MeldungHinzu
end
end
next
next
Nachher (900Ticks):
for i=0 to 150000
s=Zeilen(i)
'Prüfung auf unzulässige Zeichen ASCII-Wert bis &h20
for j=0 to 31
parts=s.Split(chr(j))
if parts.Ubound>1 then
MeldungHinzu
exit
end
next
next
Das hat also nichts gebracht. Schade.
Ich glaube, auf die Weise wird das nichts. Binärprüfung wird wegen UTF8 wohl auch ausfallen wegen der variablen Größe der einzelnen Zeichen. Oder habe ich etwas übersehen?
Ich habe mal vor ewigen Zeiten ein Regex gemacht, um non-printable Zeichen herauszufischen:
dim theString as String
dim searchpattern as String = "[^\x{0009}\x{000A}\x{000D}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFF}]+"
dim rx as RegExMBS = New RegExMBS
if rx.Compile(searchPattern) and rx.Study() then
theString = rx.ReplaceAll(theXML.ToString, "")
end if
Vielleicht mit Replace/ReplaceAll jedes unzulässiges Zeichen ersetzen und die Längen der Strings vergleichen.
Das kann man auf den ganzen Block anwenden oder Teil Abschnitte oder je Zeile.
Mein Problem ist, dass ich nicht wissen möchte, ob in der Gesamtdatei ein Zeichen kleiner &h20 ist sondern auch noch die Zeile angeben möchte.
Mir ist das auch erst aufgefallen, als sich ein Anwender mit einer Datei mit über 14Millionen Zeilen ankam.
Damit habe ich dann immer eine Schleife, die ich 14Millionen mal durchlaufen muss. Jedes Mal mit einer Prüfung auf unzulässige Zeichen. Derzeit mit jedem der 32 Zeichen separat.
Eine Reduzierung auf einen Durchlauf mit RegEx könnte da durchaus helfen.
Werde ich mal testen
Tolle Ideen alles!
Könntest du ein Testprojekt (aktueller, langsamer Stand) mit Testdaten zur Verfügung stellen, Stefan? Dann könnte man etwas spielen…
'Prüfung auf unzulässige Zeichen ASCII-Wert bis &h1F (ohne &0hA und &h0D)
dim i,bis,j,t as integer
dim theString,s,Messung(-1),Meldung(-1) as String
dim searchpattern as String = "[\x{0000}\x{0001}\x{0002}\x{0003}\x{0004}\x{0005}\x{0006}\x{0007}\x{0008}\x{0009}\x{000B}\x{000C}\x{000E}\x{000F}\x{0010}\x{0011}\x{0012}\x{0013}\x{0014}\x{0015}\x{0016}\x{0017}\x{0018}\x{0019}\x{001A}\x{001B}\x{001C}\x{001D}\x{001E}\x{001F}]"
dim rx as RegExMBS = New RegExMBS
if rx.Compile(searchpattern) then
bis=UBound(Zeilen)
t=System.Ticks
for i=0 to 150000//bis
s=Zeilen(i)
if rx.Execute(s,0)>0 then
Meldung.append "Die Zeile "+str(i)+" enthält ein unzulässiges Zeichen."
end
next
end if
Messung.append system.Ticks-t
Mein Test ergab für meine bisherigen Varianten etwa 900 bzw. 650 Ticks. Mit der RegEx Variante bin ich unter 50 Ticks gekommen. Das ist eine Verbesserung um mehr als Faktor 10.
Zum testen kann man den Code etwas abwandeln und folgendes einfügen:
dim Zeilen(150000) as String
Zeilen(5)="w"+chr(9)+"w"
Zeilen(15)="w"+chr(11)+"w"
Zeilen(25)="w"+chr(18)+"w"
Zeilen(35)="w"+chr(19)+"w"
Zeilen(45)="w"+chr(2)+"w"
Zeilen(55)="w"+chr(3)+"w"
Zeilen(65)="w"+chr(4)+"w"
Zeilen(75)="w"+chr(5)+"w"
Zeilen(85)="w"+chr(6)+"w"
Zeilen(95)="w"+chr(7)+"w"
Zeilen(115)="w"+chr(8)+"w"
Zeilen(225)="w"+chr(14)+"w"
Zeilen(335)="w"+chr(15)+"w"
Dank an alle, die hier mit Tipps beigesteuert haben!
Ja, aber separat, weil ich da in der Meldung auf TAB hinweise. Das kommt häufiger vor und das möchte ich dann dem Anwender so melden, dass er es möglichst versteht.
Hätte ich für das Beispiel wieder reinnehmen sollen, habe ich vergessen.
Die Zeilen können unterschiedlich lang sein. Vorgesehen war mal (1995) maximal knapp 256 Zeichen. Das beachte ich aber nicht. Somit können die Zeilen auch deutlich länger sein.