Zur Navigation

Sprachweiche [6]

mit weiteren seltsamen RegEx-Problemen

51 Jörg Kruse

Aber woran würde das Skript einen kaputten Eintrag erkennen? Sie stehen scheinbar wild durcheinander.

Ja genau daran. Wenn sich in einem Eintrag geschweifte Klammern befinden, und diese nicht in der Reihenfolge {}{}{} angeordnet sind, ist der Eintrag kaputt.

Darum habe ich den Verdacht, daß das Durcheinander irgendwie automatisiert zustande gekommen ist.

Herauszufinden, wie diese Fehler zustandegekommen sind, ist denke ich aufwendiger, zumal dann, wenn einem der entsprechende SourceCode fehlt

20.10.2015 17:15

52 Ranma (Gast)

Normalerweise würde ich das Komma als Delimiter verwenden und die verschiedenen Möglichkeiten der Übersetzung in ein Array aufspalten. Daraus mache ich dann Paare aus Wörtern und deren Übersetzungen. Aber hier befinden sich Kommata in den geschweiften Klammern, nach oder vor solchen kaputten Einträgen und natürlich zwischen den verschiedenen Übersetzungen.

Wären innerhalb der geschweiften Klammern andere Delimiter verwendet worden, dann wäre das Zerteilen und Zusammensetzen ziemlich einfach. Nur { oder nur } in einem Teileintrag zu finden ist zwar leicht, aber das gibt nur einen schwachen Hinweis darauf, wie das dann zusammengesetzt werden sollte.

Es ginge sicher mit preg_replace() aber das ist die schwierigste Anwendung von Regulären Ausdrücken. Um mit denen etwas zu finden, braucht man nur wenige Eigenschaften identifizieren. Aber um damit etwas zu ersetzen, muß der Ausdruck ganz genau passen.

Außerdem wird oft geraten, wo möglich andere Funktionen zu verwenden, weil die RegEx-Maschine ein Skript langsam macht. Das dürfte sich bei einer Wörterliste mit vielen tausend Einträgen auswirken. Darum würde ich preg_replace() hier gerne vermeiden.
Ranma

21.10.2015 02:16

53 Ranma (Gast)

Ich habe einige Fehler in der Wörterliste per Hand behoben. Wäre nicht ausgerechnet die Liste für Englisch betroffen, dann hätte ich mich das vielleicht nicht getraut und hätte doch auf die Forensik bestehen müssen. Es waren wohl mehrere Typen von Fehlern enthalten und einer ist noch übrig, weil davon hunderte von Einträgen betroffen sind. Irgendwie habe ich es geschafft, die Einträge in ein Array zu füllen und zwar so, daß erst in einem Element das Ende des Eintrages kommt, dann im folgenden Element der Anfang des Eintrages und danach in weiteren solchen Zweiergruppen die weiteren Einträge.

Meine Versuche die Elemente aus dem Array passend anzuordnen und zusammenzusetzen, führten allerdings dazu, daß intakte Elemente zerlegt wurden. Eigentlich hätten die garnicht betroffen sein dürfen oder wenigstens im Array enthalten sein, so daß ich gewarnt gewesen wäre.

Ich habe versucht in den intakten Einträgen , durch ; zu ersetzen, damit ich den Array durch explode(',',$Eintrag) erstellen konnte. Dafür mußte ich nur noch nach substr_count($Teileintrag,'{')!=substr_count($Teileintrag,'}') suchen.

Daß mir das sogar eine Sortierung erspart haben sollte, erschien mir zwar merkwürdig, aber wenn es über print_r() so ausgegeben wird, dann hätte ich meinem Verständnis nach mit dem Array auch weiterarbeiten können sollen.
Ranma

23.10.2015 19:28

54 Ranma (Gast)

Nun glaubte ich eine Möglichkeit zum umsortieren gefunden zu haben (nachdem einige andere Versuche schon versagt hatten):

$helfer1=array(); $helfer2=array();
while(isset($Kaputt)and count($Kaputt)>0):
// Die Bedingung sollte auf jeden Fall helfen, obwohl ich nicht weiß,
// was array_slice() zurückliefern wird, wenn das Array aufgebraucht ist.
  $helfer2=array_slice($Kaputt,0,2);
  $helfer1[]=$helfer2[1];
  $helfer1[]=$helfer2[0];
endwhile;

Aber das führte nur zu einer Fehlermeldung. Die Zeile
$helfer1[]=$helfer2[1];
ist in meinem Skript Zeile 469. Es sind hauptsächlich deswegen so viele Zeilen, weil meine Überlegungen zu diesem und den früheren Fehlern als Kommentare in der Datei stehen. Dieser macht mich allerdings ganz besonders ratlos:


Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 16777216 bytes) in /opt/lampp/htdocs/WortlistenZuDb.php on line 469

Es scheinen weniger Bytes als erlaubt nicht eingefügt werden zu können, es sei denn, daß sich allocate nur auf die nicht mehr unterzubringenden Bytes bezieht. In dem Fall könnte ich wahrscheinlich den Auslagerungsspeicher erhöhen (falls ich das Teil dafür finde), aber ich glaube nicht, daß der Speicher an der Stelle hätte zu gering sein dürfen. Schließlich sollte die fragliche Anweisung lediglich ein Array mit zwei Elementen füllen und viel größere Arrays haben bisher nicht zu so einem Problem geführt.

Falls die Anweisung nicht, wie ich mir das eigentlich denke, ein Array mit zwei Elementen füllt, was macht sie dann?
Falls die Anweisung doch macht, was ich von ihr erwarte, wie kann es dann zu dem Fehler kommen?
Ranma

24.10.2015 03:47

55 Jörg Kruse

while(isset($Kaputt)and count($Kaputt)>0):

Die Schleife enthält keine Abbruchbedingung, deswegen läuft sie endlos weiter und verbraucht irgendwann mehr als das angegebene Speicherlimit von 134 Megabyte

PS:

array_slice() arbeitet nur mit einer Kopie von $Kaputt - das Original-Array ändert sich nicht

24.10.2015 13:30 | geändert: 24.10.2015 13:43

56 Ranma (Gast)

Ja, ich bin einfach davon ausgegangen, daß array_slice() ähnlich funktioniert wie array_shift() oder array_pop(); zwei Nachbehandlungen mit array_shift() haben das Problem gelöst. Ich mache jetzt aus den beiden Hälften eines kaputten Eintrages jeweils einen einzigen reparierten Eintrag. Jetzt rätsele ich, wie ich die kaputten Einträge im Haupt-Array durch die reparierten austauschen kann. Ich kann sie dort zwar mittels array_intersect() oder array_diff() finden, aber nicht austauschen...
Ranma

25.10.2015 00:38

57 Ranma (Gast)

Ein Vergleich aller Array-Element durch substr_count() innerhalb zweier verschachtelter FOREACH-Schleifen, erlaubt das identifizieren der Elemente, die untereinander ausgetauscht werden sollen. Das dauert nur ziemlich lange, darum hätte ich das gerne vermieden. Andererseits muß das Skript nur einmal richtig funktionieren, daher ist es nicht so schlimm.

Das Schlimme ist, daß das Skript auch mit korrigierten Einträgen nicht richtig funktioniert. Es bricht wegen Zeitüberschreitung ab. Das erst nach über hunderttausend Einträgen in die Datenbank, die auch schon lange gedauert hatten, jedenfalls viel länger als das Zeitlimit von 30 Sekunden aus der Fehlermeldung.

Nach über hunderttausend Einträgen muß ich doch eigentlich davon ausgehen, daß das Skript im Großen und Ganzen (es schneidet an manchen Stellen noch Buchstaben von Wörtern ab) funktioniert? Kann ein funktionierendes Skript einfach so abbrechen?

Manche Versuche lieferten Fehlermeldungen aus einer Schleife, das ging immer sehr schnell. Könnte INSERT IGNORE das Skript ausbremsen? Aber die Kombination aus zwei Datenbankspalten ist der PRIMARY KEY, also dürften viele gleiche Einträge nichtmal wahrscheinlich sein...
Ranma

25.10.2015 22:24

58 Jörg Kruse

Das Schlimme ist, daß das Skript auch mit korrigierten Einträgen nicht richtig funktioniert. Es bricht wegen Zeitüberschreitung ab. Das erst nach über hunderttausend Einträgen in die Datenbank, die auch schon lange gedauert hatten, jedenfalls viel länger als das Zeitlimit von 30 Sekunden aus der Fehlermeldung.

Dei 30 Sekunden beziehen sich nur auf die reine Laufzeit des Scriptes, siehe diesen Hinweis im PHP-Manual:

Die set_time_limit()-Funktion und die max_execution_time Konfigurationsdirektive beschränken nur die Ausführungszeit des Skripts selbst. Zeit die für Aktivitäten außerhalb des Skripts aufgebracht wird wie z.B. die Ausführung von Systemaufrufen mit system(), Streamoperationen, Datenbankabfragen usw. werden nicht in die Berechnung der Ausführungszeit mit einbezogen.

Die Datenbank als externe Anwendung sollte hier also nicht das Problem sein.

Kann ein funktionierendes Skript einfach so abbrechen?

Ja, wenn das in der php.ini definierte Limit max_execution_time überschritten ist

26.10.2015 14:36 | geändert: 26.10.2015 14:38

59 Ranma (Gast)

Ich habe das Problem zu lösen versucht, indem ich mir eine andere Wörterliste für Englisch besorgt habe. Die TU Chemnitz hat nämlich auch eine unter die GPL gestellt. Obwohl ich beim Einlesen die zusätzlichen, in eckigen oder geschweiften Klammern hinter dem Wort befindlichen, Informationen noch garnicht extrahiert habe, sondern alle Einträge einfach in die Datenbank abladen wollte, kam es wieder zu einem Abbruch wegen Zeitüberschreitung. Jetzt verstehe ich das erst. Nur weil es sehr viele Zeilen PHP und darunter nur einmal mysqli_stmt_execute() sind, muß der Rest des Skriptes nicht langsamer sein. Die Kommunikation zwischen Skript und Datenbank nimmt anscheinend sehr viel Zeit in Anspruch. Darum bricht das Skript dann nach ungefähr zwei Stunden wegen Überschreitung der 30 Sekunden ab. Das sieht nur unlogisch aus, ist es aber nicht.

Jedenfalls habe ich als nächstes versucht, den letzten verarbeiteten Eintrag in der Liste zu finden und dann versucht ziemlich am Anfang des Skriptes mittels
$Restdaten=array_slice($Rohdaten,array_search('Letzter Eintrag',$Rohdaten));
dafür zu sorgen, daß nur noch der Rest verarbeitet wird. Das hat aber auch nicht geklappt. Jetzt sind hunderttausende Einträge mindestens doppelt in der Datenbanktabelle, aber am Ende fehlt jedesmal ungefähr ein Fünftel der Einträge. Das Skript brach wieder wegen der 30 Sekunden ab. Das finde ich aber nun tatsächlich unlogisch. Ich muß zwar bei Array-Funktionen und String-Funktionen immer erst nachschauen, welcher Parameter nach vorne und welcher nach hinten kommt, aber genau darum bin ich mir auch sicher, array_slice() und array_search() richtig verwendet zu haben.

Laut dem PHP-Manual dient das Limit von 30 Sekunden (wie übrigens auch die Speicherbegrenzung) dazu, schlampige geschriebene Skripte aufzuhalten. Schlampig wollte ich mein Skript eigentlich nicht machen. Aber ich glaube, ich kann da nichts dafür, wenn die einzulesende Datei einfach so lang ist. Ist set_time_limit() eine normale PHP-Funktion? Die könnte man dann bei jedem schlampigem Skript einfach an den Anfang (ich nehme an, da gehört sie hin, falls das eine PHP-Funktion ist) setzen und das widerspricht doch etwas dem Konzept damit schlampige Skripte verhindern zu wollen.
Ranma

30.10.2015 01:43

60 Jörg Kruse

Ist set_time_limit() eine normale PHP-Funktion? Die könnte man dann bei jedem schlampigem Skript einfach an den Anfang (ich nehme an, da gehört sie hin, falls das eine PHP-Funktion ist) setzen und das widerspricht doch etwas dem Konzept damit schlampige Skripte verhindern zu wollen.

Das ist aber nicht überall möglich, wenn z.B. (durch den Webhoster) der Safe Mode aktiviert ist, hat diese Funktion keinen Effekt.

Abgesehen davon schreibt niemand durchweg perfekte Scripts, und wenn durch eine vorher nicht absehbare Nebenbedingung ein Script "Amok läuft", dann ist es schon von Vorteil, wenn dieses spätestens nach 30 Sekunden abgebrochen wird. In den meisten Fällen laufen die PHP-Scripte ja unaufbeaufsichtigt, d.h. sie werden nicht vom Programmier sondern von normalen Besuchern der Webseite angestoßen.

In deinem Fall kann es aber durchaus Sinn machen, die Funktion set_time_limit() in dem betreffenden Script einzusetzen. Und ja, ich würde sie dann ganz an den Anfang des Scriptes einbauen.

30.10.2015 16:48