simplexml out of memory

  • simplexml out of memory

    Hallo Leute,

    ich hoffe es kann mir jemand für folgendes Problem einen Lösungsansatz bieten:
    Für mein Projekt möchte ich Inhalte aus mir zur Verfügung gestellten XML-Datein einbinden. Die XML-Datei lade ich mir auf meinen Server kein Problem, jedoch möchte ich nun mit simplexml_load_file die XML Datei lesen und die Inhalte ide ich benötige auslesen.
    Das Problem: Die Datei ist z.B. 23 MB groß, nach wenigen Sekunden bekomme ich die Meldung "parser error : out of memory error".
    Anscheinend probiert simplexml_load die gesamte Datei einzulesen und das macht der Server nicht mit.
    Kann man das Problem überhaupt mit simplexml lösen? Wie machen das andere die solche XML-Dateien verwenden.

    Für Hilfe von euch wäre ich sehr dankbar, ich habe zu diesem Thema leider kaum etwas finden können. :cry:

    Danke für eure Mühe
    dreimalsieben
  • Hi, das ist kein Problem mit simplexml, sondern schlichtweg eine PHP Einstellung.
    Wenn du auf einem Shared Server bist ist es doch klar, dass es schlecht für die anderen Kunden ist, wenn du mit solch großen Dateien hantierst.

    Befindest du dich auf einem Shared Server wirst du die Dateien also splitten müssen.
    Besitzt du einen eigenen Server kannst du dir mehr Arbeitsspeicher verpassen, indem du in der php.ini das memory_limit = 8M auf ein paar mehr MB erhöhst.

    Grundsätzlich kostet die Verwaltung der Daten mehr Arbeitsspeicher als die Daten wirklich groß sind. Verwende daher besser mal ~64MB.
  • Hi d0nUt,

    danke schon mal für die Antwort. Leider handelt es sich um ein bereitgestellten Webserver eines bekannten Anbieters. Daher denke ich, wird es nicht zuviele Möglichkeiten geben den virtuellen Speicher zu erhöhen, zusätzlich möchte ich für Seitenbesucher die mögliche Peformance auch nicht einschränken. Ich habe gerade mal nochmal nachgesehen. In der PHP.ini steht anscheinend 128M, eigentlich kommt mir das viel vor, jedoch scheint es für die Datei trotzdem nicht zu reichen!

    Daher scheint für mich der einzig mögliche Vorgang das Splitten der Datein. Hast Du damit Erfahrungen? Gibt es eine einfache Möglichkeit, das z.B. via PHp autoamtisiert auf dem Server zu bewerkstelligen.

    Danke für deine Mühe,
    Greetings
    dreimalsieben
  • Wenn du die Dateien streng splittest (nach Zeilen/Dateigröße/...) hast du das Problem, dass du keinen gültigen DOM Baum mehr hast um ihn mit simpleXML zu parsen.

    Quellcode

    1. <root>
    2. <node>1</node>
    3. <node>2</node>
    4. <node>...</node>
    5. <node>1000000</node>
    6. </root>

    zu

    Quellcode

    1. <root>
    2. <node>1</node>
    3. <node>2</node>


    Wenn die Struktur einheitlich ist, würde ich sie einfach mit [phpdoc]fscanf[/phpdoc] einlesen.

    Quellcode

    1. <?php
    2. $handle = fopen ("bigfile.xml","r");
    3. while ($node = fscanf ($handle, "<node><a>%s</a><b>%s</b></node>")) {
    4. list ($a, $b) = $node;
    5. //Jetzt direkt in der Datenbank speichern
    6. }
    7. fclose($handle);
    8. ?>


    Oder eben mit regulären Ausdrücken arbeiten.

    Quellcode

    1. <?php
    2. $handle = fopen ("bigfile.xml","r");
    3. while ($node = fscanf ($handle, "<node>%s</node>")) {
    4. preg_match($node, "<a>(.+)</a>.*<b>(.+)</b>", $node[0], $result);
    5. list ($a, $b) = $result;
    6. //Jetzt direkt in der Datenbank speichern
    7. }
    8. fclose($handle);
    9. ?>
  • Okay,

    ich würde es dann so umsetzen, dass ich die Daten als einzelne Datein ausgebe und nicht direkt in der Datenbank speicher (Da meine Datenbank nicht groß genug ist für alle XML-Daten). Leider ist dies natürlich alles sehr umständlich für ein eigentlich simples Ziel.
    Als Alternative fällt dir aber auch kein XML-Parser (Am ebsten einer der mit PHP läuft) ein der evtl. nicht die gesamte XML-Datei einliest sondern eine Art Streaming macht? Gibt es so einen überhaupt.
    Ansonsten teste ich mal die XML-Datei split Methode, hauptsache ich erreich das Ziel, egal wie ;)

    Dank dir schonmal d0nUt
    Greetings
    dreimalsieben
  • Eine Datenbank braucht doch nicht mehr Speicher als eine XML Datei. Die XML Datei könntest du direkt nach dem Parsen löschen. Die Datenbank wird sogar auf diese Weise also weniger Speicher verbrauchen, weil die ganzen Tags nicht Teil der Daten sind. Das Argument Speicherplatz entfällt also meiner Meinung nach.

    Aber selbst wenn du ein Gigabyte an Arbeitsspeicher zur Verfügung hättest, solltest du die XML Files auf keinen Fall zur Laufzeit bei jedem Seitenaufruf neu parsen. Das kostet eine Menge Performance.

    Einen Streaming Effekt mit PHP erhältst du, wenn du die Datei mit der oben genannten Methode einliest (oder eben aus der Datenbank), mit echo ausgibst und die Ausgabe dann mit [phpdoc]flush[/phpdoc] sofort sendest. So kannst du quasi beliebig viele Daten verschicken.
  • kurze Rückmeldung

    ... nach langem, langem Suchen, habe ich endlich eine Möglichkeit gefunden die XML-Daten zeilenweise zu parsen ohne Schwierigkeiten mit dem Speicher zu bekommen und ohne die gesamten Daten vorher in die mysql Datenbank abzuspeichern.
    Expat heißt der Zauberparser, ist zwar nicht das neueste unter den Parsern aber sehr effektiv!

    An dieser Stelle nochmal vielen Dank für die Mühe und die Lösungsansätze!
    Bis zu meinem nächsten Problem ;)

    Vielen Dank,
    greetings
    dreimalsieben
  • zu früh gefreut!

    Ach du Sch****,

    da denkt man, man hätte einen riesen Schritt nach vorne getan und dabei war es eher einer zurück....

    Beim parsen der XML-Datei ist mir erst viel später aufgefallen, dass er nur einen Teil ausliest und plötzlich ohne Fehler oder Sonstiges aufhört, d.h. es ist nicht jedesmal die selbe Stelle an die er anhält. Es ist rein zufällig. Also die ersten 1000 Datensätze liest er aus und dann stoppt er. Ich kann es einfach nicht mehr nachvollziehen.

    Hier ein Ausschnitt wie ich es umgesetzt habe:

    Quellcode

    1. // Datei wird zum Öffnen geladen
    2. $fp = fopen("$XML_URL","r")
    3. or die("Error reading RSS data.");
    4. // Mit 4KB Zeilenmweise ausgelesen
    5. while ($data2 = fread($fp, 4096)){
    6. // Jede ausgelesene 4KB werden an Parser übergeben
    7. xml_parse($xml_parser, $data2, FEOF($fp));
    8. // ERROR Verhalten
    9. //or die(sprintf("XML error: %s at line %d",
    10. // xml_error_string(xml_get_error_code($xml_parser)),
    11. // xml_get_current_line_number($xml_parser)));
    12. }
    Alles anzeigen

    Ich kann einfach nicht nachvollziehen warum er einfach an verschiedenen Stellen stoppt... Hat jemand schonmal ein ähnliches Problem gehabt oder hat eine Ahnung woran es liegen kann.

    Vielen Dank,
    Greetings
    dreimalsieben
  • Wie siehts denn mit meiner scanf Lösung von oben aus?
    Ungenügender Speicher kann ja nicht das Problem sein.

    Und wenn dir die regulären Ausdrücke nicht mächtig genug sind, kannst du ja auch für jeden Node den XML Parser anwerfen.

    Quellcode

    1. <?php
    2. $handle = fopen ("bigfile.xml","r");
    3. while ($node = fscanf ($handle, "<node>%s</node>")) {
    4. $xml = new SimpleXMLElement($node);
    5. //Jetzt direkt in der Datenbank speichern
    6. }
    7. fclose($handle);
    8. ?>
  • Sorry, dass ich jetzt dazu komme zu antworten. Viel zu tun :(
    Das was mich an der ganzen Sache so stört ist, dass ich das Parser-Script testweise auf einem eigenen Server eines Bekannten getestet habe, und dort läuft es einfach durch, ohne plötzlichen, leisen Abbruch. Daher scheint im Script selber kein Fehler zu sein.

    Bezogen auf deinen Vorschlag mit fscan die Daten einzulesen, muss ich ehrlich zugeben, dass ich es kurz versucht habe, es aber leider keinen wirklichen Erfolg brachte. Er wollte mir einfach keine Sinnvollen Daten ausgeben.

    Als Beispiel meine XML-Struktur:

    Quellcode

    1. <product id="">
    2. <programm/>
    3. <info>
    4. <name></name>
    5. <ean></ean>
    6. </info>
    7. <links>
    8. <deeplink></deeplink>
    9. </links>
    10. </product>


    Bin anscheinend zu blöd, das fscanf() richtig aufzurufen.

    Vielen Dank schon mal.
    Greets

    dreimalsieben