Bekomme NSXMLParserPrematureDocumentEndError ohne Grund

  • Bekomme NSXMLParserPrematureDocumentEndError ohne Grund

    Hallo,

    ich möchte ein sehr großes XML-Dokument (> 5 GB) mit dem NSXMLParser durchparsen. Sollte ja grundsätzlich kein Problem sein, oder?

    Seltsamerweise fängt er aber gar nicht erst damit an. Er meldet in Zeile 1, Spalte 1, einen Error 5, also NSXMLParserPrematureDocumentEndError.

    Das gleiche ist mir schonmal passiert, als der Pfad zur Datei falsch war, aber den habe ich mittlerweile zigfach überprüft, auch per Copy&Paste eingefügt.

    Aufgerufen wird der Parser wie folgt:

    Quellcode

    1. map = [[zRoute alloc] initWithXMLFile:[NSURL fileURLWithPath:@"/Users/zottel/source/osm/germany.osm"]];

    und in der Klasse zRoute:

    Quellcode

    1. - (id)initWithXMLFile:(NSURL *)xmlURL {
    2. id initReturn = [super init];
    3. […]
    4. NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithContentsOfURL:xmlURL];
    5. […]
    6. }


    Sobald ich eine andere, kleinere Datei (20 MB) im gleichen Ordner wähle (und auch nur den Dateinamen im Pfad austausche), funktioniert alles, wie es soll.

    Wie gesagt, der Fehler tritt beim allerersten Zeichen des Files auf.

    Kann sich jemand darauf einen Reim machen?

    EDIT: Ach ja, und ich kann germany.osm problemlos mit vi öffnen, auch mit perl kann ich darauf zugreifen. Das erste Zeichen ist ein <, wie es sein soll, und die erste Zeile ist identisch zur ersten Zeile einer Datei, die funktioniert. Ich habe sicherhitshalber sogar einmal eine Kopie der Datei angelegt, aber auch mit der Kopie das gleiche Verhalten. Dann hatte ich noch vermutet, dass vllt. ein Unicode-BOM am Anfang der Datei ist, aber auch das ist nicht der Fall.

    Danke,

    Christian
  • Erstens glaube ich auch, dass Deine App auf jeden Fall 64 Bit kompiliert sein sollte, denn ich habe die VERMUTUNG, das NSXMLParser kein Streaming parser ist.

    Leute die mehr Zeit haben können ja mal ausprobieren, wie der ein 5 GB File behandelt.

    ICH würde Dir hier empfehlen, direkt libxml2 zu verwenden, die kannst Du mit Chucks füttern (also: 100 MB lesen, 100 MB parsen, 100 MB lesen, 100 MB parsen etc

    Alex
    The only thing that really worried me was the ether.
  • Original von little_pixel
    wenn Du Deinen Verdacht bewiesen haben möchtest, dann halbiere Doch einfach mal die Kopie.
    Lösche die Hälfte der Daten einfach raus.
    Wenn es dann immer noch nicht geht, dann nochmals die Hälfte. usw.

    Den Vorschlage finde ich sehr gut. Schau Dir dabei den Speicherverbrauch Deiner App an. Probier das auch mal mit einem Parser, der nichts macht - also insbesondere keinen zusätzlichen Speicher während des Parsens anfordert. Wenn dann der Speicherverbrauch mit der Größe der XML-Datei wächst, liegt die Vermutung sehr nahe, dass der NSXMLParser die komplette Datei in den Speicher lädt.

    Falls das der Fall ist, brauchst Du einen SAX-Parser, der die Daten über einen Stream verarbeiten kann.
    „Meine Komplikation hatte eine Komplikation.“
  • Oh, ich war eigentlich davon ausgegangen, dass ein event driven parser nicht versucht, alles auf einmal einzulesen. :-/

    In NSData habe ich jetzt aber eine Lösung gefunden.

    Erstmal nochwas, was nicht funktioniert: Wenn ich selber mit [NSData dataWithContentsOfFile:…] ein NSData-Objekt erstelle, bekomme ich nicht nil zurück, aber das Endergebnis ist genau das selbe wie vorher. (Läuft übrigens im 64bit-Modus – jedenfalls vermute ich das, wenn mein Target x86_64 ist. Bin da noch eher neu in der Materie, korrigiert mich, wenn ich falsch liege.)

    Es funktioniert, wenn ich

    Quellcode

    1. NSData *xmlData = [NSData dataWithContentsOfFile:@"/Users/zottel/source/osm/germany.osm" options:NSDataReadingMapped error:&error];

    benutze.

    Dann wird, wenn ich das richtig verstehe, die Datei teils direkt als virtueller Speicher benutzt. Wenn das gut programmiert ist, könnte das ziemlich effizient sein.

    Ergebnis ist jedenfalls, dass die Applikation zwar sehr viel Speicher zieht, aber nicht so viel, dass das große Geswappe losgeht.

    Ich denke das ist als Lösung erstmal ok. Danke für alle Vorschläge!

    Christian
  • Wundert mich auch... ich hätte gedacht, dass unter x86_64 beides funktioniert (unabhängig davon, was mehr swapped).

    Aber unter 32 Bit wirst Du auch mit dieser Methode nicht weiterkommen (falls Deine App auch unter 32 Bit laufen soll)

    Alex
    The only thing that really worried me was the ether.
  • Hm, ist leider doch nicht so ideal. Auf meinem 4GB-System zieht diese Version erstmal spontan > 2GB. Dann sind zwar noch ein paar 100MB frei, aber natürlich lese ich auch Daten aus der Datei aus, und irgendwann ist das so viel, dass nur noch herumgeswappt wird.

    Ich habs testweise trotzdem mal weiterlaufen lassen, aber leider ist es irgendwann (in Zeile 12.618.410 X-) ) mit Error 40 ausgestiegen, also NSXMLParserAttributeNotFinishedError, was in dieser Zeile aber definitiv nicht zutrifft.

    So oder so, ich denke, ich muss das irgendwie speichereffzienter hinkriegen. Hat jemand noch eine Idee, wie man das ohne Core Foundation hinkriegen könnte? Ich kann normales C nicht wirklich (lesen ja, aber hab nie auch nur eine Zeile damit programmiert) und wäre froh, wenn ich da drumrumkommen würde.

    Wie mancher vielleicht schon vermutet hat, das sind Kartendaten von openstreetmap.org. Ich lese das ganze in ein Core Data Managed Object Model mit SQLite-Store ein, damit habe ich dann wenig Speicherprobleme (auch wenn auch das bei so viel Zeug dann trotzdem ganz schön groß wird).

    Problem: Am Anfang kommen alle Nodes (mit IDs), dann die Ways, also die Straßen, die jeweils eine Liste von von Node-IDs haben, die dazugehören. Die kriegen dann jeweils Relationships, so sollte man dann ja schnell drauf zugreifen können. Um eine Node mit einem Weg verbinden zu können, muss ich sie dann also anhand ihrer ID suchen. Wenn ich dafür ein NSMUtableDictionary benutze, ist das natürlich hübsch flott, dürfte aber bei 5GB Daten zuviel Speicher verbrauchen. Jedesmal einen Fetch Request auszuführen, ist aber unendlich langsam, schon bei 20MB XML brauche ich damit ein paar Minuten, bis das XML komplett eingelesen ist.

    Gibt es sowas wie nen Hash auf der Platte, den ich für do eine Aufgabe benutzen könnte? Oder sonstige Ideen?

    Danke,

    Christian