große Strings stückweise bearbeiten?



  • Hallo,

    ich habe ein kleines Problem. Ich arbeite gerade an einem kleinen persönlichem Projekt und möchte dafür eine txt Datei für weitere Bearbeitung aufbereiten, sprich Formatierungen durch einen normalen Zeilenumbruch ersetzen. Das klappt ja auch alles schön und gut, nur leider können diese (ursprünglichen) txt Dateien recht groß sein, so mehr als 5 MB. Mit meinem Code bekomme ich bis ca. einem halben MB eine annehmbare Laufzeit hin, aber alles darüber hinaus dauert halt viel zu lange.

    Ich denke mal, dass es an der find Funktion liegt, die bei großen Strings einfach zu lange braucht (insbesondere, wenn man die Mehrfachsuche in dem String auch mehrfach braucht).

    Jetzt hatte ich die Idee, dass es dann doch einfach die Laufzeit extrem reduzieren würde, wenn ich den String nur stückweise bearbeite. Nur wie mache ich das? Ich bin relativ neu mit C++ und weiß gerade nicht so recht weiter.

    Ich gebe jetzt einfach mal den Beispielcode an, den ich bis jetzt schon selbst entwickelt habe. Um den Code erstmal einfach zu halten, habe ich bis jetzt noch keine eigenen Funktionen verwendet, obwohl es sich hier wahrscheinlich anbieten würde.

    Ich wäre über jede Hilfe dankbar. Achja, ich möchte nicht unbedingt den kompletten Code von euch wissen, aber ein kleiner Denkanstoß würde schon sehr weiterhelfen. Danke 🙂

    #include <fstream>
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    int main()
    {
    	int i,j,k;
    	std::string finden_umbruch,finden_tabulator,finden_doppelt_umbruch,finden_return;
    	ifstream in_file("D:/Test.txt", ios::binary);
    
    	if (!in_file.is_open()) return -1;
    
    	in_file.seekg (0, ios::end);
    	int length = in_file.tellg();
    	in_file.seekg (0, ios::beg);
    
    	std::string test;
    	test.resize(length+1, '\0');
    
    	in_file.read (&(test[0]), length);
    
    	finden_umbruch="\n";
    	finden_tabulator="\t";
    	finden_doppelt_umbruch="\n\n";
    	finden_return="\r";
    
    	string::size_type position = 0;
    
    	while (position = test.find(finden_tabulator) != string::npos){
    		i= test.find(finden_tabulator);
    		if (i != -1)
    			test.replace(i,1,finden_umbruch);
    	}
    
    	while (position = test.find(finden_return) != string::npos){
    		j = test.find(finden_return);
    		if (j != -1)
    			test.replace(j,1,finden_umbruch);
    	}
    
    	while (position = test.find(finden_doppelt_umbruch) != string::npos){
    		k = test.find(finden_doppelt_umbruch);
    		if (k != -1)
    			test.replace(k,2,finden_umbruch);	
    	}
    
    	ofstream testfile("D:/Testfile.txt", ios::out);
    	testfile << test;
    
    	return 0;
    }
    

    Edit: Tippfehler raus 🙂



  • Kleiner Tip von mir.
    Lies die Datei komplett ein, am besten in einen std::vectorstd::string oder in eine std::queuestd::string.
    und auf dieser basis bearbeitest du dann deine strings innerhalb des programms und nicht innerhalb der datei.



  • @Firefighter
    Er liest doch die Datei komplett in den Speicher ein.
    @Takhlahr
    Ich sehe da zwei Möglichkeiten für Geschwindigkeitsgewinn:
    1)
    Die Suche nach den zu ersetzenden Zeichen beginnt immer wieder von vor, das kostet Rechenzeit. Besser ist es, den String nur einmal zu durchlaufen.
    2)
    Einen weiteren Speedgewinn bekommst du, wenn du einen zweiten Stringpuffer spendierst; denn die Zeichenersetzung im selben String kostet ebenfalls viel Rechenzeit, weil ständig neu umkopiert werden muss.

    Hier ein highspeed format0r, der Dateiinhalt ist bereits in file_content eingelesen, guckst du hier:

    #include <iostream> 
    #include <string> 
    using namespace std; 
    
    char* chars_to_replace = "\t\r\n";
    char replac0r = '\n';
    
    bool is_char_to_be_replaced ( char c )
    {
    	char* a = chars_to_replace;
    	while(*a)
    		if ( *a++ == c )
    			return true;
    	return false;
    }
    
    int main() 
    { 
    	string file_content = "abc\tdef\r\nghi\n\njkl";
    	string formated_file_content;
    	const string::size_type len = file_content.size();
    	string::size_type i = 0;
    
    	while ( i < len )
    	{
    		if ( is_char_to_be_replaced (file_content.at(i)))
    		{
    			i++;
    			formated_file_content += replac0r;
    			while (is_char_to_be_replaced (file_content.at(i)))
    				{i++;}
    		}
    		else
    		{
    			formated_file_content += file_content.at(i);
    			i++;
    		}
    	}
    
    	cout << "original: " << endl;
    	cout << file_content << endl;
    	cout << "------" << endl;
    	cout << "formated: " << endl;
    	cout << formated_file_content << endl;
    

    Gruß,
    B.B.



  • Kompilier mal im release mode mit optimierungen.



  • ...und ersetz at durch operator[]

    bb



  • unskilled schrieb:

    ...und ersetz at durch operator[]

    bb

    Ja, das wird wahrscheinlich der Jahrhundert Performance Boost...



  • theta schrieb:

    unskilled schrieb:

    ...und ersetz at durch operator[]

    bb

    Ja, das wird wahrscheinlich der Jahrhundert Performance Boost...

    Es ist einfach mal unsinnig, in diesem Kontext at zu verwenden... Und da schon so was offensichtliches eigenartiges daran ist, hab ich mir die Zeit, alles durchzulesen, direkt gespart...
    beim überfliegen hab ich jedoch noch viele eigenarten gesehen...

    bb



  • @Big Brother

    Vielen Dank für deine doch recht ausführliche Antwort!
    Ich habe mir den Code mal genauer angeschaut und er macht tatsächlich das, was ich mir zuerst dachte, aber mangels ausreichender Kenntnisse nicht umsetzen konnte und mit meinen schwachen String-Operationen ersetzen musste.

    Deine Lösung ist in der Tat um vieles eleganter und auch "performanter" als die meine. Ich denke, ich werde diese Lösung in ähnlicher Form durchaus verwenden können. Vielen Dank nochmal (auch an alle anderen)!


Anmelden zum Antworten