Processing von sehr großen Textdateien

perpetuum.mobile

Grand Admiral Special
Mitglied seit
06.02.2003
Beiträge
2.849
Renomée
21
Standort
Hinter dir!
Hallo!
Ich bin gerade mal etwas am Überlegen, welche Architektur ich wählen würde für ein bestimmte Aufgabe:

Einlesen großer Textfiles in verschiedenen Codepages/Linebreaks
  1. Textfiles sehen etwa so aus und stellen ein Dump einer relationalen Datenbank dar :
    Code:
    Spalte1#|#Spalte2#|#Spalte3#|#Spalte4
    123#|#123565654863#|#aÖdf23sdg555#|#äÜö
  2. Ersetzen einzelner Datenfelder wie z.B. 123565654863 durch einen bestimmten Wert (Entweder "*" oder ein Wert aus einer internen Hashtable, die als Configtextfile bereitgestellt wird).
  3. Schreiben des Textfiles im gleichen Format oder Unicode/UTF-8/ASCII ja nachdem was einfacher zu programmieren ist.

Anforderungen sind:
  • möglichst geringer Entwicklungsaufwand (gut ok, kann man schon fast weglassen, es kann ja aber super Bibliotheken in einer Sprache für geben)
  • Laufzeit ist zweitrangig (dann läuft es halt einen Tag, es müssen aber auch Dateien mit bis zu 80 GB bearbeiten werden können
  • Möglichst einfache Portierung auf verschiedene Architekturen und Betriebssysteme. Einsatzgebiete: Solaris, AIX, Linux, Windows Server. Zum Notfall auch nur Windows.

Ich tendiere momentan wohl zu einer Java Entwicklungsumgebung, aus folgenden Gründen:
  • Java ist plattformunabhängig, hat bloß etwas Overhead wegen der Virtual Machine, ist aber auch auf den meisten Servern heutzutage schon als Runtime Environment installiert.
  • Ich kenne Java schon ;)
  • Relativ große Bandbreite an Unterstützung in Richtung Bibliotheken usw., aber da kenne ich mich noch nicht ganz so aus.
Könnt ihr mich vielleicht bei meinem Vorhaben etwas unterstützen und beraten? Fällt euch eine Sprache/Entwicklungsumgebung ein, die die Aufgabe besser bewältigen könnte? Würdet ihr die gleiche Wahl treffen?

Die Wahl der Umgebung ist ja eigentlich recht wichtig, deswegen wollte ich mal nachfragen. Vielleicht kennt ihr auch OpenSource Tools, die sowas schon machen und nur noch modifiziert werden müssen. Ich jedenfalls gehe bald mal etwas auf die Suche nach solchen Programmen, mir geht es momentan nur um die Sprache (Vielleicht ist Java bei solch großen Files nicht performant genug?).

Schöne Woche noch :)
pm
 
Moin,

ich täte mir ein Script aus folgenden Zutaten basteln:
awk o. sed und recode.

Für Windows musst du leider die Binaries mitschlören...-> unixtools/unixutils
Schneller kommst du nicht ans ziel.

Gruß,

Hannnibal
 
Wenn es darum geht, evtl. auch das Encoding zu ändern, ist Java vermutlich geschickter als ein Skript.
Zusammen mit BufferedReader kann man in Sachen Performanz fast nix falsch machen...

Ansonsten würde ich Perl vorschlagen:
+ Dieser "einfache" Einsatzzweck wäre auf Unix und Windows portabel zu machen
+ Perl bringt alles mit, vom zeilenweisen Einlesen einer Datei über regulären Ausdrücken bis zu bequemer Textersetzung, kann man nix falsch machen
+ Perl ist universeller. Etwas Perl zu können ist nie verkehrt (wobei awk natürlich auch ne schöne Sache ist)
- Das Handling von verschiedenen Encodings ist möglich, aber imho nicht schön gelöst (und erst in neueren Versionen verfügbar (5.8.9 iirc), die man speziell bei Solaris oder AIX evtl. nicht voraussetzen kann)


Ich würde jedenfalls nicht groß nach Bibliotheken suchen, das ist an sich schon ne triviale Aufgabenstellung. Alles was es an Convenience-Features braucht hat eine moderne Sprache an Board.
 
@idontknow
Die ETL Idee ist ja eigentlich richtig gedacht. Sowas als Textfiles zu transportieren halte ich auch für ... naja, ich glaube ihr könnt mir folgen. Allerdings muss der Output wieder eine Textdatei sein und dafür extra einen ETL Prozess zu starten, es in eine DB zu laden und dann wieder in eine Textdatei zu schreiben, das ganze mit dem DWH Overhead... ich glaube das ist einfach too much für die Anforderung.

@Hannibal
Wenn ich das richtig verstehe müsste ich aber für jede Plattform einen eigenen Compile anfertigen, oder? Das ist unbrauchbar, da das am Ende wirklich auf den verschiedensten Plattformen laufen soll und man immer die richtigen Binaries mitgeben muss. Also geht es wohl nicht an interpretierten Sprachen vorbei, wenn ich das jetzt nochmal so überdenke.

@ThePsycho
Also das Encoding ändern wäre schon ein praktisches Feature, muss aber nicht sein. Wichtiger ist in dem Fall dann eher, dass keine Laufzeitumgebunden auf dem Server/Rechner installiert werden müssen. Leider kenne ich mich bzgl. Perl und deren Verbreitung nicht ganz so aus, v.a. auf Windows Servern. Weißt du da näheres? Im Vergleich zu Java?
Ansonsten klingt Perl ja jetzt erst einmal nach einer passenden Sprache, zumindest wenn ich mir den Wikipedia-Artikel anschaue.

Ich denke du hast es im letzten Satz schon gesagt, Java oder Perl und einfach loslegen. String-Funktionen sollte man in jeder Sprache finden. Es hätte ja aber sein können, jemand kennt besondere Frameworks, die mir performancetechnisch hätten extrem helfen können.
 
Ich denke Java wäre für den Anwendungszweck das richtige, wegen der Plattformunabhängigkeit. Zum Prozessieren würde ich bei der Datenmenge vermutlich mit mehreren Threads arbeiten. Einer zum Einlesen, mehrere (einer pro CPU?) zum suchen und ersetzen, und einem zum Wegschreiben - ich denke damit könnte man etwas Zeit gewinnen.

Innerhalb einer Tabelle sollte ja die Reihenfolge der Zeilen (bis auf den Header) egal sein. Aber Tabellenübergreifend (außer es ist nur eine Tabelle pro Datei) muss man das natürlich irgendwie ordentlich synchronisieren.

Im Prinzip würde ich mir das bei Java irgendwie so vorstellen:

BufferedReader Thread->LinkedBlockingQueue->Worker Threads->LinkedBlockingQueue->BufferedWriter Thread
 
Stimmt, mit Threads könnte man wohl noch einiges ein Parallelität und somit Performance raus holen.
Ich hab jetzt das Einlesen/Rausschreiben fertig und kann mich jetzt auch an die Transformation machen. Ich mache mal Tests wie schnell das durchläuft. Wenn das ok ist, dann spare ich mir den Aufwand (wie gesagt, Laufzeit ist nicht unbedingt so wichtig, aber Tage sind blöd... ;) )
Ich überlege den Input für welche Felder überschrieben werden sollen, per einer Exceldatei zu machen. Die ist einfach besser zu handeln als ein simpler Textinput.
 
Ich vermute am Ende wird das File-I/O der Bottleneck sein. Das einzige außer Pufferung was man da optimieren kann, wird vermutlich sein mit zwei Laufwerken zu arbeiten.
 
Ich glaube das Tool wird später u.a. im SAN eingesetzt... also I/O ist (hoffentlich) vorhanden. Momentan hab ich noch meine Probleme den Output richtig hinzubekommen, habe aber schon einen Lösungsansatz. Bei Java wirklich direkt einen String in einem bestimmten Format zu schreiben ist echt der Krampf. Man muss den String natürlich erst in das Zielformat konvertieren, Java arbeitet intern mit UTF-16 (LE oder BE weiß ich garnet, glaube ich je nach System.)
Jedenfalls wenn man den String als normal UTF-16 Output kodieren will, muss man das vorher in eine Bytefolge konvertieren. Ich habe es pertu nicht hinbekommen, den String als etwas anderes als UTF-8 zu schreiben, bzw. haben die Java-Funktionen immer geschludert. Ich wandle das jetzt in eine Bytefolge um und mit einem CharsetEncoder wird das Ganze dann in das Zielformat gequetscht. Blöd nur, wenn er dann bei jeder Zeile immer einen BOM davor setzt. Also muss ich den dann unterbinden (UTF-16LE oder UTF-16BE direkt angeben, dann lässt er das weg). Natürlich muss man den dann aber manuell noch reinschreiben... aber das ist so ein häßlicher Hack...
Hat jemand einen Plan wie man mit der Klasse CharsetEncoder in Java in eine Bytefolge umwandelt, die keinen BOM enthält, aber ohne UTF-16LE oder UTF-16BE anzugeben? Naja, jedenfalls komme ich ganz gut vorwärts. Bei Bedarf kann ich das Tool dann auch online stellen. Nur wird mein wirrer Bastel/Codingstil nicht jedem gefallen ;)
 
Achtung, siehe Anmerkung weiter unten!
Code:
[LEFT]try {
Charset charset = Charset.forName(outputCodepage); //outputCodepage to added = UTF-16BE
CharsetEncoder encoder = charset.newEncoder();
ByteBuffer bb = encoder.encode(CharBuffer.wrap(line)); //Encode in desired Output Charset[/LEFT]
 
[LEFT]try {
//TODO: add BOM (UTF-16LE UTF-16BE work without BOM)[/LEFT]
// Write the ByteBuffer contents
[LEFT]wChannel.write(bb);
} catch (IOException e) {
e.printStackTrace();
}
//dos.write(line.getBytes(outputCodepage)); // outputCodepage: Unicode (UTF-16), does not work somehow [/LEFT]
} 
[LEFT]catch (IOException e) { [/LEFT]
 
[LEFT]e.printStackTrace();[/LEFT]
 
[LEFT]}[/LEFT]

Irgendwie bekomme ich die Formatierung hier nicht hin. Bei write() und getBytes(outputCodepage) hat er das bei mir immer in UTF-8 geschrieben... k.A. warum. Ich mache das deswegen jetzt mit dem CharsetEncoder und das klappt. Jetzt muss ich nur noch den BOM einfügen und gut ist.

Mein Problem ist vor allem, dass ich das zeilenweise bzw. immer Stückchen für Stückchen schreiben muss. Und jedes mal, wenn man neu encodiert, setzt er einen BOM an den Anfang. Und unterdrücken kann man das nur mit UTF-16BE oder UTF-16LE, da lässt er das sein. Schon blöd irgendwie.

---

Die Apache Tools schauen praktisch aus. Naja, ich habe jetzt meinen Weg gefunden. Außerdem glaube ich, dass die auch den BOM schreiben würden.
 
Zuletzt bearbeitet:
Mit UTF-16 hab ich noch nicht gearbeitet, aber geht die Lösung JKuehl nicht? Denn so hab ich mir das auch vorgestellt.
Die Konvertierung sollte Java dann innerhalb des write() automatisch machen.
 
Dachte ich auch erst, aber ich hatte ständig Probleme. Deswegen der Umweg über den CharsetEncoder mit einem ByteBuffer. Im Endeffekt wird write (was in dem Fall eines Strings ja wohl einfach nur überladen ist) auch nichts anderes machen als intern zu dekodieren und den ByteBuffer/bytes[]-array einfach zu schreiben.

Ich vermute das hat was mit dem Standard der Codepage im System zu tun, was denn geschrieben wird und das es da irgendwie durcheinander kommt. Bei mir muss das ja aber unbedingt unabhängig davon sein.
Unabhängig davon würde das Problem mit dem BOM am Anfang auch da auftreten.

UPDATE:
Das was ich oben geschrieben habe ist großer Quark... zumindest wenn man den ByteBuffer nicht löscht, denn der fasst standardmäßig auch nur eine bestimmte Größe. (2mb?) Da muss ich nochmal Hand anlegen...

UPDATE2:
Ich hab auch mein Problem mit dem Charset gelöst. Ich bin fälschlicherweise von Originaldaten aus einem System ausgegangen, dass es US-ASCII war. Leider war es dann doch ANSI-CP1252 (Windows Standard). Deswegen hat es mir manche Zeichen immer verhauen. Naja, jetzt geht. Das nächste mal erstelle ich mir die Testdaten selbst ;)

Wer Interesse am Quellcode hat, kann mir gerne schreiben.
 
Zuletzt bearbeitet:
Jo,
das erste was ich machen würde ist dem Admin n Vogel zeigen!
Warum solltest du son Dreck parsen, Relationale Datenbanken haben die Möglichkeit das in verschiedene Ausgabeformate zu exportieren (csv, e.t.c) wodurch das Parsen zum Kinderspiel wird.

2. Java kannste knicken für die Aufgabe und bei sonstigen Skriptspachen keine Chance!. Ich hab mal ne App gehabt die Textdateien erzeugt, die Gigabyteweise groß waren(java). Allein die Erzeugung hat ewig lange gedauert. Das parsen war in akzeptabler Zeit nicht möglich gewesen, musste deswegen zu C++ wechseln. Aber 80Gb-Textfiles sind auch mit C++ n Krampf, das schon mal vorab.

Aufgrund dessen gibt´s Datenbanken!
 
Ja klar, aber es gibt auch noch andere Restriktionen und auch Tabellen, die wesentlich mehr Daten beinhalten, als die man braucht. Gut man könnte auch einfach mit SQL Statement begrenzen... aber glaube mir, ich habe schon von einer Oracle DB ein "snapshot too old" bekommen, weil die SELECT-Transaktion auf eine (!) Tabelle zu lange lief. Auf DB Ebene ist außerdem zuviel Spielraum, welche DB eingesetzt wird und dann gibt es hier da wieder Restriktionen der DB selbst... kurzum: Es wird einfach auf Anwendungsebene exportiert und dort halt als diese Textdatei. Ob der Delimiter jetzt ";" ist oder "#|#", ist doch egal.
Genauso wird eine IT-Abteilung in größeren Unternehmen nicht einfach mal irgendwelchen Murks auf der Datenbankebene ausführen, das muss meistens laut Anforderungen auf Anwendungsebene erledigt werden. Glaub mir, das hat schon so seinen Grund.

BTW hab ich das Tool jetzt eigentlich fertig. Leider bin ich schon auf einen Stand von Funktionen, die ich nicht öffentlich machen will. Daher kann ich das jetzt nicht mal schnell zeigen. Nachfragen beantworte ich aber gerne, was ich so genommen hab (FileChannel mit Buffer usw.)

Die Performance ist zweitrangig, aber nicht irrelevant. IMHO arbeitet das Tool recht fix und ich sehe da (noch) kein Problem, Java genommen zu haben. Warum ist Java dafür eigentlich nicht geeignet? Der große Vorteil ist doch, dass ich das Tool nicht für verschiedene Zielarchitekturen neu kompilieren/anpassen muss. Da muss die andere (Skript) Sprache schon einige Vorteile bieten.

Einzig Tests mit großen Datenvolumen mit bis zu 500GB oder auch 1TB muss ich noch machen. Ich habe das Gefühl dass dann irgendwann die Grenzen vom Speicher der VM greifen und ich die Hashtabelle quasi in eine Temporäre Datei auslagern muss. (nicht alle Datenfelder werden so "pseudonymisiert" sondern nur einige Felder) Oder kann ich die VM, wenn genügend Speicher da ist, "unendlich" groß machen? Bis jetzt geht bei mir immer nur max. 1500 MB
 
Zuletzt bearbeitet:
@Avatar
Würde mich auch interessieren, wo du da Vorteile bei C++ siehst. Ich hoffe du beschränkst dich nicht auf das Märchen "Java ist langsam"!?

@perpetuum.mobile
Unter einem 64-Bit-System kann man eine 64-Bit VM "unendlich" groß machen, unter 32-Bit ist leider bei ca 1,5 GB Ende (bzw. manchmal auch weniger, je nach Zustand des Systems).
Grund ist, dass Java wegen seiner eigenen Speicherverwaltung einen zusammenhängenden Adressraum benötigt, unter 64-Bit ist der Raum aber größer als durch handelsübliche Hardware erreichbar.

Neugierig bin ich aber schon, weswegen du auf FileChannel gewechselt bist.
- Wegen Performance und einstellbarem Puffer?
- Oder weil die Encoding-Probleme sich so leichter lösen ließen?
Ich hab mit dem java.nio-Paket bisher eher negative Erfahrungen gemacht, hab bisher aber nur mit einem ByteBuffer gearbeitet (die nio-Version war 10-15% langsamer, ka warum).
 
@ThePsycho:
Es ist kein Märchen, es ist die Wahrheit.

@perpetum.mobile:
Genau den Teil den du noch net getestet hast, ist ja das gerade das spannende an der Applikation. Genau daran wird es nämlich scheitern ^^. Lass uns später weiterreden, wenn du solche Test mal gefahren hast..
 
Also die alte Aussage dass Java generell langsam ist, halte ich schon für etwas polemisch und pauschalisiert.
Wenn es um Algorithmen geht ist die Implementierung sowieso meist das unkritischste was die Performance betriffft. Hotspot optimiert auch schon ganz saftig.
Java ist in verdammt vielen großen kommerziellen Projekten im Einsatz, und die wären schön blöd es zu verwenden wenn es so schnarchlahm wäre...
Dass der Overhead natürlich bei riesigen Datenmengen problematisch sein kann ist unbestreitbar.
Dann müsste man aber eigentlich in letzter Konsequenz auf den ganzen Objektkram verzichten und C statt C++ nehmen. fopen() und mit einer Schleife durchgehen...
Oder, man folgt den aktuellen Trends, schreibt einen Webservice in C# oder Java, der eine Zeile in die DB einfügt, und ein clientprogramm in C/C++ welches das File parst und für jede Zeile den Webservice aufruft *hrhr*
Cloud-Computing und SOA lassen grüßen.
 
Ich bin selbst Entwickler, oft Java( im Studium 85-90%), aber ich hatte ebenso C++ Projekte. Sowohl beruflich, als auch privat.
Niemand behauptet, Java sei langsam, ganz im Gegenteil, aber C/C++ ist ne native Programmiersprache, in dessen Compiler bisher mitunter die Optimierung reingeflossen ist. Der Unterschied ist sogar so hoch, dass man das einfach selbst mal ausprobieren muss, um es zu glauben.
 
Moin,

Viele Leute halten Java für langsamer weil ein Programmstart länger dauert als bei nativen Sprachen und Java GUIS in der Regel etwas träger laufen native (vor allem auf Linux).
Dass es auch anders geht, sieht man z.B. bei Android.
Vieles eine frage der Implementierung (sowohl bei der VM als auch beim Programm).

Gruß,

Hannnibal
 
Zuletzt bearbeitet:
Wos ? Bei Android wird jede Menge JNI benutzt, das heißt es werden C++ Libs aufgerufen, wo es nur geht. Mit dem Thema hab ich mich auch schon auseinandersetzen müssen ;-)
 
Es stellt sich doch die Frage des Einsatzzwecks.
Hier in diesem Beispiel geht es um das Einlesen einer Datei, Ändern der gepufferten Daten und Rausschreiben der Daten.

Da kann es kaum Overhead geben, der eine Java-Implementierung langsamer macht als eine in C++.
In diesem Falle wäre auch eine (erste) C-Implementierung imho langsamer. Nicht weil C langsam ist *g*, sondern weil Dinge wie Pufferung beim Einlesen und Arbeiten auf einer geeigneten Größe von Daten erstmal von Hand implementiert werden müsste (was genau deshalb zunächst nicht geschieht).
Von C++ kenne ich die verfügbaren Mechanismen nicht, aber praktischer als in Java kann es eigentlich kaum sein. Wogegen der zusätzliche Layer durch die VM wohl kaum ins Gewicht fällt (bzw. die vom OS losgelöste Speicherverwaltung sogar schneller sein _kann_).

Daher würde es mich - auf diese Aufgabenstellung beschränkt - doch sehr wundern, wenn sich mit einer nativen Sprache ein Performance-Vorteil erreichen ließe.
 
Moin,

dann machen wir doch ne challenge draus :).

perpetuum.mobile müsste die Aufgabe verallgemeinern (einlesen, encoding, parsen, raus schreiben) und seine bereinigte Java Implementation Posten - dann könnte ja die c fraktion mal zeigen wie schnell sie sind.

Gruß,

Hannnibal
 
Zurück
Oben Unten