Thread 80 Mio Zeilen einlesen (24 answers)
Opened by FanClub at 2013-07-16 10:17

clms
 2013-07-16 11:48
#168894 #168894
User since
2010-08-29
373 articles
BenutzerIn
[default_avatar]
2013-07-16T08:41:12 FanClub
Ich habe eine XML Datei. So aufgebaut (grob)

Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<Produkt>
<Produkt_Nummer>5</Produkt_Nummer>
<details>
<details2>..</2details>
</details>
</Produkt>
<Produkt>
<Produkt_Nummer>5</Produkt_Nummer>
<details>
<details2>..</2details>
</details>
</Produkt>
<Produkt>
<Produkt_Nummer>5</Produkt_Nummer>
<details>
<details2>..</2details>
</details>
</Produkt>
<Produkt>
<Produkt_Nummer>5</Produkt_Nummer>
<details>
<details2>..</2details>
</details>
</Produkt>
<Produkt>
<Produkt_Nummer>9</Produkt_Nummer>
<details>
<details2>..</2details>
</details>
</Produkt>


Ich will nun das XML File durchlaufen und den gesamten Block, welcher doppelt vorkommt, löschen. Leider läuft dein mechanismus auch auf einen Out of Memory.

Wenn ich es richtig verstanden habe, willst Du aus dem XML diejenigen <Produkt>...</Produkt>-Blöcke rausfiltern, deren Produkt_Nummer vorher schon einmal vorgekommen ist. Im obigen Beispiel würden also nur der erste und der letzte Produkt-Block wieder ausgegeben.

Hier ein erster Ansatz mit Pseudo-Code
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
open(my $fh_in, '<', "input.xml") or die "... : $!";
open(my $fh_out, '>', "output.xml") or die "... : $!";

# Produkte, die schon ausgegeben wurden
my %Produkt_nummer_gesehen;

# lese immer einen kompletten Produktdatensatz ein
local $/ = "</Produkt>";

while (my $prod = <$fh_in>) {
  my $pn = extract_produkt_nummer($prod);
  next if $Produkt_nummer_gesehen{$pn}++;
  print $fh_out, $prod;
}

Bei diesem Ansatz musst Du immer nur den Datensatz zu einem Produkt im Speicher halten plus einen Hash mit allen schon gelesenen Produktnummern.
Bekommst du damit immer noch ein "Out of Memory"?

Wenn der Datensatz für ein einzelnes Produkt so groß werden kann, dass er Deinen Speicher sprengt, die Produkt-Nummer aber immer relativ weit am Anfang des Prodiukt-Blocks steht, kann man noch etwas drehen, indem man den einzelen Produkt-Datensatz nicht auf einen Schlag einliest sondern nur bis man zur Produkt-Nummer kommt und entscheiden kann, ob man den Datensatz verwerfen oder ausgeben muss (und dann ggf. den Rest des Blocks zeilenweise lesen und gleich wieder ausgeben.) Allerdings wird dann die Programmstruktur komplexer.

Wenn Du allerdings so viele und lange Produktnummern hast, dass der Hash zu groß wird, weiss ich auch nicht mehr weiter. Außer vielleicht noch eine Idee: Wenn die Produktnummern - wie in Deinem Beispiel - immer reine Zahlen sind, kann man vermutlich noch Speicherplatz sparen, in dem man int(extract_produkt_nummer($prod)) schreibt.

View full thread 80 Mio Zeilen einlesen