Dateizeiger nach ifstream::putback an falscher Stelle



  • Die continues setze ich lediglich da, wo ein Durchlaufen bis zum Ende sinnlos ist, ist eine gewisse Verarbeitung, meines Erachtens beendet, dann springe ich aus der Schleife raus. Wenn ich an eine Stelle wie PROPERTIES komme und danach gewisse Dinge brauche, dann springe ich an eine Stelle im Code, die mir diesesn Abschnitt anders zerlegt als die anderen und dann springe ich wieder an den Anfang der Schleife. Ich brauche aus den verschiedenen Abschnitten unterschiedliche Dinge und mit dem Programm lief das auch bisher immer sehr gut.

    Was die Mischung zwischen C und C++ angeht: Wie sieht denn dann "reiner" C++-Code aus - eine Klassen- und Template-Benutzung nach der anderen? Vergleiche, Anweisungen, Zuweisungen - das unterscheidet sich doch nicht großartig von C.

    Was ich nicht verstehe ist die Sache mit memset. Ich weiß nicht, was das Problem damit ist. Der Speicherbereich wird doch nur von Beginn der Variablen bis zu ihrem Ende mit Nullen überschrieben. Kann ich auf andere Weise noch 0.000000000000001 Sekunde rausholen oder quält diese Anweisung die CPU?

    Fakt ist, dass eine solche Differenzdatei (durchschnittlich 220 kB groß) in einer gefühlten halben Sekunde durchlaufen wird, und da ist dann auch noch das Füllen einer visuellen List mit dabei - wie gesagt, der Code ist auf das wesentliche reduziert. Und dadurch, dass ich mit bitweisen Anweisungen arbeite und nicht mit Schlüsselworten wie AND oder OR oder XOR oder erst zuweise und vergleiche, ergibt im übersetzten Programm bestimmt keinen Nachteil.



  • Hier der Inhalt der Datei die Ärger macht: http://codepad.org/sy9VExXs

    Edit durch Arcoth: Code ausgelagert.



  • Und hier mal der Inhalt einer Datei bei der es funktioniert: http://codepad.org/cVRwe9BS

    Edit durch Arcoth: Code ausgelagert.



  • Wegen memset lies dir mal memset() or value initialization to zero out a struct? durch (ich hoffe, du weißt, was POD bedeutet?).

    Und hast du mal den Ratschlag getestet?

    Schlangenmensch schrieb:

    Hast du dir die Werte von dateiposV und dateipos mal einzeln angeschaut?

    Und packe deinen letzten beiden Beiträge in korrekte Code-Tags.


  • Mod

    Code solcher Ausmaße bitte extern hochladen und verlinken. Sonst wird die Navigation in einem solchen Thread erheblich erschwert.



  • Arcoth schrieb:

    Code solcher Ausmaße bitte extern hochladen und verlinken. Sonst wird die Navigation in einem solchen Thread erheblich erschwert.

    Sorry, wußte ich nicht besser. Werde ich in Zukunft so machen.

    Th69 schrieb:

    Wegen memset lies dir mal memset() or value initialization to zero out a struct? durch (ich hoffe, du weißt, was POD bedeutet?).

    Habe ich mir jetzt mal angesehen. Vielen Dank für den Link.

    Th69 schrieb:

    Und hast du mal den Ratschlag getestet?

    Schlangenmensch schrieb:

    Hast du dir die Werte von dateiposV und dateipos mal einzeln angeschaut?

    Das ganze sieht so aus:

    streampos dateiposV = is.tellg();	// DEBUG
    is.putback(strTmpBuffer[0]);
    dateipos = is.tellg();			// DEBUG
    

    Bei einem Objekt, dass noch richtig eingelesen wird, hat dateiposV bspw. den Wert 120371 und dateipos nach dem putback 120370. An der Stelle, an der es zu diesem mir nicht verständlichen Verhalten kommt, hat dateiposV den Wert 126975 und nach dem putback enthält dateipos nicht, 126974, sondern 130558.



  • _Bongo schrieb:

    Tut mir ja Leid, aber der Code funktioniert so wie der da steht.

    Falsch ist er trotzdem.

    _Bongo schrieb:

    Ich bezweifele auch, dass das irgendwas mit der fehlerhaften Funktion zu tun hat.

    Kannst du bezweifeln. Muss aber nicht stimmen. Als erstes sollte man mMn. mal grobes UB wie z.B. das memset auf std::string fixen - also bevor man anfängt gross Fehler zu suchen. Für so "daran kann es nicht liegen" Einschätzungen braucht es speziell bei C++ einiges an Erfahrung - und selbst die alten Hasen vertun sich da manchmal.

    _Bongo schrieb:

    Wenn der Zeiger auf Byte x steht, dann sollte er nach dem putback auf x-1 stehen. Der Rest meines Codes hat sicher nix damit zu tun ...

    Dann sollte es für dich ja möglich sein ein kleines Beispiel mit 10-20 Zeilen zu basteln welches das selbe Verhalten zeigt.

    _Bongo schrieb:

    ...und wenn ja, dann sage mir mal wo.

    Wenn ich das wüsste würde ich es dir vermutlich sogar sagen. Nur suchen werde ich nicht. Das ist deine Aufgabe. Und da die Ursachen für solche Fehler oft nicht auf den 1. Blick zu erkennen sind...



  • _Bongo schrieb:

    An der Stelle, an der es zu diesem mir nicht verständlichen Verhalten kommt, hat dateiposV den Wert 126975 und nach dem putback enthält dateipos nicht, 126974, sondern 130558.

    130558 - 126974 = 3584 = 7 * 512
    7 * 512 ist schon sehr verdächtig. Könnte also wirklich ein Bug in der iostream Implementierung sein. Welchen Compiler verwendest du (genaue Version)?

    _Bongo schrieb:

    Was ich nicht verstehe ist die Sache mit memset. Ich weiß nicht, was das Problem damit ist. Der Speicherbereich wird doch nur von Beginn der Variablen bis zu ihrem Ende mit Nullen überschrieben.

    Und genau das darfst du nicht machen. Du darfst nicht einfach so Objekte wie std::string mit Nullen überschreiben. Wie kommst du auf die Idee dass das erlaubt wäre?
    ->

    strctDeltaBuffer.strChangeTyp.clear();
        strctDeltaBuffer.strZiel.clear();
        // oder
        strctDeltaBuffer = strctDelta();
    


  • hustbaer schrieb:

    Und genau das darfst du nicht machen. Du darfst nicht einfach so Objekte wie std::string mit Nullen überschreiben. Wie kommst du auf die Idee dass das erlaubt wäre?

    Nachdem ich mir das mit memset mal durchgelesen habe, schien mir das auch sehr verständlich, dass das keine so gute Idee ist alles zu überschreiben. Ich habe das bereits gestern geändert.

    Der Compiler ist Microsoft Visual Studio Professional 2012 und die Version ist 11.0.61219.00 Update 5



  • Also das ganze scheint zu passieren wenn man genau an einer 4K grenze putback() versucht. Sieht mir nach einem Bug in der MSVC Implementierung aus.*
    putback ist zwar nicht garantiert, aber laut Doku sollte badbit bzw. failbit gesetzt werden. Und das passiert nicht. tellg steht dann auch irgendwo, und als nächstes zeichen kommt nicht das "zurückgestellte". Mit MSVC 2017 verhält es sich etwas anders aber auch nicht korrekt.

    Pragmatische Lösung: vermeide putback einfach grundsätzlich. Du kannst ja vorher mit peek() gucken was kommt, dann hast du im Falle des Falles nix zurückzustellen.

    Programm zum nachstellen:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <cctype>
    
    int main(int argc, char* argv[])
    {
    	int found = 0;
    
    	for (int i = 0xFF0; i < 0x1010; i++) {
    
    		{
    			std::ofstream f("_test.txt", std::ios::binary);
    			for (int j = 0; j < i; j++)
    				f.write(" ", 1);
    			f << "  ; } x---------------------------------------------------------------------------------------------";
    			f.flush();
    			f.close();
    		}
    
    		std::ifstream f("_test.txt");
    
    		std::string str;
    		getline(f, str, ';');
    //		f >> str;
    		str.clear();
    		char c;
    		while (std::isspace(f.peek()))
    			f.read(&c, 1);
    		while (!std::isspace(f.peek())) {
    			f.read(&c, 1);
    			str.push_back(c);
    		}
    
    		auto p1 = f.tellg();
    		f.putback(str[0]);
    		auto s1 = f.rdstate();
    		auto p2 = f.tellg();
    		auto s2 = f.rdstate();
    
    		auto diff = static_cast<long long>(p1 - p2);
    
    		if (diff > 2 || diff < -2 || found) {
    			std::cout << "@" << p1 << ":\n";
    
    			f.read(&c, 1);
    			auto p3 = f.tellg();
    			std::cout << "next char: '" << c << "'\n";
    			f.read(&c, 1);
    			auto p4 = f.tellg();
    			std::cout << "next char: '" << c << "'\n";
    			f.read(&c, 1);
    			auto p5 = f.tellg();
    			std::cout << "next char: '" << c << "'\n";
    			f.read(&c, 1);
    			auto p6 = f.tellg();
    			std::cout << "next char: '" << c << "'\n";
    			found++;
    			if (found > 1)
    				break;
    		}
    	}
    
    	return 0;
    }
    

    Output mit MSVC 2017:

    @4096:
    next char: ' '
    next char: 'x'
    next char: '-'
    next char: '-'
    @4097:
    next char: '}'
    next char: ' '
    next char: 'x'
    next char: '-'
    

    BTW: Kann mir jemand erklären wofür putback eigentlich gut ist? Frag ich mich schon lange. "Könnte sein dass geht, könnte sein dass nicht" ist ja meistens nicht so wirklich hilfreich...
    Oder gibt es für bestimmte konkrete Stream-Typen (z.B. ifstream) stärkere Garantien?

    *: Nah, bin mir nicht mehr so sicher. Nämlich wie die Sache mit dem "last character read" zu interpretieren ist. Die Iostreams müssen nämlich "peeken", und "peeken" liest ja einen Character - auch wenn er dann nicht "konsumiert" wird. Mit "stream.get() + stream.unget()" lässt sich das ganze nämlich nicht reproduzieren, da funktioniert alles. Mit "stream.get() + stream.peek() + stream.unget()" geht es aber wieder nicht. Und da der operator >> mit (istream&, string&) halt peeken muss... wurde ein character "gepeekt" wenn du versuchst "putback" zu machen. D.h. es müssten zwei chars zurückgestellt werden, supported wird aber nur einer. Doof. Keine Ahnung was der Standard dazu sagt. Ich find's komisch (unpraktisch). Aber ich find' sowieso die ganzen iostreams unpraktisch.

    TL;DR: Don't use putback or unget unless you like eating canned worms.





  • Hallo miteinander.

    An dieser Stelle schon mal vielen, lieben Dank für eure Hilfe. Ich werde mir das mal genau anschauen und versuchen zu verstehen. Auf so etwas wäre ich in 100 Jahren nicht gekommen. Man lernt nicht aus.



  • Update: Der Bug soll laut MS mit Visual Studio 2017 15.6 behoben sein.
    Hab mir aber noch nicht die Arbeit gemacht es zu checken.


Anmelden zum Antworten