Würds wahrscheinlich in Excel-VBA machen. Sollte es genug XML-Parser-Bibliotheken geben
Wenn ich ihn richtig verstanden habe will er aber gar kein XML parsen, sondern bloß ganz bestimmte Zeichenketten rausfiltern.
Und dafür C++ zu nehmen, ist wie mit Atomraketen auf Ameisen zu schiessen.
Es muss nicht C++ sein, aber mit Python habe ich noch nichts zu tun gehabt. Kann ich mein Ziel mit Sed und Awk erreichen, das kenne ich leider auch nicht ?
Ja, aber was machste halt wenn in deinem Arsenal bloß Atomraketen sind? Wenn ich irgendwo Windows neu aufsetze (was ich zugegebenermaßen sehr ungern mache; ich schlitze ja auch Autos nicht die Reifen auf) sind Python und Git die ersten Dinge, die ich gleich mal nachinstalliere. Wenn man sich überlegt wie viel "Bordsoftware" im Vanilla-Windows sowieso mit Python läuft, finde ich es völlig unverständlich es nicht gleich von Hause aus mitzuliefern. Stattdessen liegen halt irgendwo im system32 5 bis 8 veraltete Python-Versionen rum, und ich muss mir die neueste nachinstallieren. Ist halt so wenn man keinen Paketmanager hat.
Aber zurück zum Thema: C++ it is.
Um auf den Punkt zu kommen, so ungefähr kann man das machen:
C++:
#include <iostream> // std::cout
#include <fstream> // std::ifstream, std::ofstream
#include <string> // std::string
#include <sstream> // std::ostringstream
#include <regex> // std::regex
void extract_from(const char* filename, std::ofstream& target);
int main(int argc, char* argv[])
{
std::ofstream output_fstream(argv[argc-1], std::ofstream::out);
for (int i = 1; i < (argc-1); i++) {
extract_from(argv[i], output_fstream);
}
output_fstream.close();
}
void extract_from(const char* filename, std::ofstream& target)
{
std::ifstream input_fstream(filename, std::ifstream::in);
std::string content;
if (input_fstream) {
std::ostringstream oss;
oss << input_fstream.rdbuf();
content = oss.str();
}
input_fstream.close();
// Verschiedene Varianten zum Durchtesten:
std::regex words_regex(R"([Tt][Xx][t_]?\w*\W)");
//std::regex words_regex(R"((Txt|txt|TX_|Tx_|Tx)\w*\W)");
//std::regex words_regex(R"(([Tt]xt|T(X|x_?))[_[:alnum:]]+[^_[:alnum:]])");
auto words_begin = std::sregex_iterator(content.begin(), content.end(), words_regex);
auto words_end = std::sregex_iterator();
std::cout << "Found "
<< std::distance(words_begin, words_end)
<< " words in "
<< filename << std::endl;
for (auto i = words_begin; i != words_end; i++) {
std::smatch match = *i;
std::string match_str = match.str();
target << match_str << std::endl;
}
}
(Der Code könnte schöner sein; ich hab's halt in 5 min schnell zusammengebastelt und versucht es möglichst anfängerfreundlich zu halten)
Im Prinzip ist die Sprache beinahe egal. Wichtig ist: Reguläre Ausdrücke! Dein Anwendungsfall schreit beinahe danach. Und Regular Expressions sind in praktisch jeder Programmiersprache vorhanden.
Wir könnten hier jetzt eine lange Diskussion über REs und ihre Performance anfangen. Aber seien wir mal ehrlich: Das Programm läuft so wie es ist auf jedem Mittelklasselaptop mit deutlich über 100 MB/s. Da brauchste nix optimieren. Außer du hast halt wirklich gigabyteweise Daten...
Kurz zur Benutzung, falls du noch Neuling bist:
- Du compilierst dir das Programm. Gerne auch mit -O2 oder -O3.
- Die compilierte Programmdatei kommt dorthin, wo sie für dich praktisch liegt. Z.B. direkt in dem Ordner mit den XML-Dateien.
- Shift+Rechtsklick in den Ordner und auf "Command hier öffnen" oder "Powershell hier öffnen" klicken.
- Der Befehl lautet dann
mein_programm erste_xml.xml zweite_xml.xml dritte_xml.xml ausgabedatei.txt
- Das Programm filtert aus den XML-Dateien alle Treffer heraus und schreibt sie in die Ausgabedatei.
So wie ich den Code geschrieben habe, wird jeweils eine ganze XML-Datei in den Hauptspeicher gelesen. Bei typischerweise mehreren freien GB RAM heutzutage und Dateigrößen von wahrscheinlich deutlich unter einem Gigabyte sollte das aber kein Problem sein.
Falls du Fragen zum Code hast natürlich immer raus damit.
Kurze Erläuterung zu Regulären Ausdrücken
Ich habe dir einfach mal 3 mögliche Patterns in das Programm (als Raw-String) gepackt. Die Syntax für C++ REs kann man
hier nachlesen. Eine gute Einführung in Regular Expressions gibt es
hier.
Variante 1 - "[Tt][Xx][t_]?\w*\W": Erwarte ein t oder T. Danach ein X oder x. Optional (?) folgt hiernach ein t oder ein _ (Unterstrich). Wiederhole mind. 0 mal (*) ein alphanumerisches Zeichen (\w). Am Ende steht ein nicht-alphanumerisches Zeichen (\W). Vorteil: Sehr kurz. Nachteil: Matcht auch "falsche" Zeichenketten, z.B. "tX_".
Variante 2 - "((Txt|txt|TX_|Tx_|Tx)\w*\W)": Erwarte am Anfang "Txt" oder "txt" oder "TX_" oder "Tx_" oder "Tx". Hier habe ich also wörtlich die Suche aus deinem ersten Beitrag übernommen. Danach kommen wieder 0 oder mehr alphanumerische Zeichen und am Schluss ein nicht-alphanumerisches Zeichen. Vorteil: Gut lesbar/nachvollziehbar, da nur 5 Varianten. Nachteil: Sehr unoptimiert (in jeglicher Hinsicht). Wenn es mehr als 5 mögliche Varianten gibt wird es außerdem sehr unübersichtlich.
Variante 3 - "([Tt]xt|T(X|x_?))[_[:alnum:]]+[^_[:alnum:]])": Der erste Teil des Patterns kann auf zwei Weisen gebildet werden. "[Tt]xt": Entweder beginnt das Wort mit T oder mit t, danach folgt "xt". "T(X|x_?)": Oder das Wort beginnt mit einem großen T, gefolgt von einem großen X (dann aber kein Unterstrich), oder von einem kleinen x, wobei hier dann noch ein Unterstrich folgen kann. Danach kommt mindestens ein (+) alphanumerisches Zeichen, und das Pattern stoppt beim ersten nicht-alphanumerischen Zeichen. Die Gruppe "[:alphanum:]" habe ich hier extra mal verwendet, damit du siehst wo das "\w" und "\W" oben herkommt. Vorteil: Elegant und matcht alles richtig. Außerdem wahrscheinlich ganz performant. Nachteil: Braucht mehr Hirnschmalz.
Achtung: Wie du an den Regulären Ausdrücken siehst, hängt am Schluss immer noch das "Stoppzeichen", also ein nicht-alphanumerisches Zeichen dran. Falls du das nicht mit übertragen willst (bei Newlines nicht so schlimm, bei ">" aber vielleicht schon), kannst du es über
match_str.pop_back();
leicht entfernen.
Ich hoffe ich habe dich jetzt nicht mit allem totgeschlagen. Bei Unklarheiten frag einfach nach.
Gruß Technipion