seekg springt weiter als es sollte



  • Hier ein Ausschnitt einer Funktion, die eine Bildschirmauflösung aus einer Textdatei ausliest:

    for (int zahlpos = 0; zahlpos < 20; zahlpos++)
    	{
    		if (Aufloesung.get() != 'x')
    		{
    			AktuellesTextstueck[zahlpos] = Aufloesung.get();
    			printf(&AktuellesTextstueck[zahlpos]);
                            printf("\n");
    		} else {
    			AktuellesTextstueck[zahlpos] = '\0';
    			zahlpos = 20;
    		}
    		Aufloesung.clear();
    		Aufloesung.seekg(1, ios::cur);
    		nZahl = Aufloesung.tellg();
    		printf("Position: ");
    		printf(itoa(nZahl,Zahl,10));printf("\n");
    	}
    

    Die Auflösung steht folgendermaßen in der Textdatei:

    screen resolution: 124x78

    Vorher wird der Lesezeiger schon so gesetzt, dass er auf der "1" der 124 steht. (Ich weiß, die Auflösung ist quatsch, ist auch nur zum Testen da. 😉 )

    Die erste Ausgabe von printf ist also 1. Nun soll in dieser for-Schleife der Lesezeiger mit seekg immer ein Zeichen weiter springen. Tut er aber nicht. Im Internet habe ich gelesen, dass ich einen cléar-Befehl davorsetzen soll, wie er auch dort steht, aber das ändert rein gar nichts. Hier die Ausgabe:

    1
    Position: 147
    n
    Position: 156
    e
    Position: 165
    v
    Position: 173
    g
    Position: 180
    e
    Position: 187
    p
    Position: 193

    etc.

    Warum erzeugt:

    Aufloesung.seekg(1, ios::cur);
    

    Keinen Sprung um ein Zeichen nach vorne?


  • Administrator

    1. Wieso verwendest du printf statt std::cout . printf ist C und sollte in C++ mit grosser Vorsicht verwendet werden.
    2. Wieso verwendest du itoa ? Das ist kein Standard C++ und nicht einmal Standard C. Zudem ist die Verwendung hier völlig überflüssig. Wenn du schon printf benutzt, dann benutze es richtig:
    printf("Position: %d\n", nZahl);
    Oder gleich in C++:
    std::cout << "Position: " << nZahl << std::endl;
    3. Ich habe gar nicht erst getestet, was bei dir falsch sein könnte. Ich habe auch gar nicht genügend Informationen. Ich müsste auch zuerst ein eigenes kleines Progrämmchen schreiben, welches dies umsetzt. Allerdings habe ich auch dafür eigentlich zu wenig Informationen. Die meisten Typangaben und Vorgehensweisen fehlen.
    4. Mit C++ könntest du dies allerdings wesentlich einfacher lösen. Hier mal ein simples Progrämmchen, welches die Eingabe einer Auflösung erwartet, also zum Beispiel "124x78":

    #include <iostream>
    
    int main()
    {
    	int width;
    	int heigth;
    
    	if(!(std::cin >> width))
    	{
    		std::cout << "couldn't read width" << std::endl;
    		return 0;
    	}
    
    	if(std::cin.get() != 'x')
    	{
    		std::cout << "Didn't find x character" << std::endl;
    		return 0;
    	}
    
    	if(!(std::cin >> heigth))
    	{
    		std::cout << "couldn't read height" << std::endl;
    		return 0;
    	}
    
    	std::cout << "success" << std::endl;
    	return 0;
    }
    

    Nicht vergessen, std::cin ist ein std::istream Objekt.
    Beim Einlesen einer Zahl über ein std::istream Objekt, wird so lange gelesen, bis die Zahl nicht mehr in den Speicher passt, dann wird der Stream ungültig, oder es wird ein Zeichen gefunden, welches keine Ziffer ist. Beim ersten Einlesevorgang wird dies ausgenützt, damit es beim 'x' abbricht.
    Jeder Lesevorgang gibt den Stream selber zurück und dieser kann auf Gültigkeit geprüft werden. Somit kann jeweils immer gleich geprüft werden, ob der Lesevorgang gültig war.

    Grüssli



  • Ich benutzte kein cout, weil ich SDL in dem Projekt eingebunden habe und iostream und SDL sich irgendwie nicht vertragen. Wie printf genau funktioniert weiß ich nicht, da muss ich zugeben, hab ich solange rumprobiert, bis es funktioniert hat.

    Cin und <iostream> bringt mir leider überhaupt nichts, da ja eine Textdatei ausgelesen werden soll bzw. muss.

    Die verwendeten Variablen sind die folgenden:

    char AktuellesTextstueck[21];
    char nZahl[4]; //Ist nur für meine printf-Bastelei da, sonst sinnlos
    

    Ich hab mal die printf verschönert und ein wenig mehr Code:

    //Der Anfang der Funktion
    #ifndef _FSTREAM_
    #define _FSTREAM_
    #include <fstream>
    #endif
    using namespace std;
    
    bool AufloesungLaden(int* Position, char AktuellesTextstueck[21], int* ScreenX, int* ScreenY)
    {
    ...Auswertungen, die die richtige Zeile suchen und den Lesezeiger auf die 1 der 124 setzen...
    //X-Zahl auslesen
    	for (int zahlpos = 0; zahlpos < 20; zahlpos++)
    	{
    		if (Aufloesung.get() != 'x')
    		{
    			AktuellesTextstueck[zahlpos] = Aufloesung.get();
    			printf(&AktuellesTextstueck[zahlpos]);
    			printf("\n");
    		} else {
    			AktuellesTextstueck[zahlpos] = '\0';
    			zahlpos = 20;
    		}
    		Aufloesung.clear();
    		Aufloesung.seekg(1, ios::cur);
    		nZahl = Aufloesung.tellg();
    		printf("Position: %d\n", nZahl);
    	}
    

    Später benutze ich atoi, um die ausgelesenen Character in Zahlen umzuwandeln. Was soll ich denn sonst verwenden, wenn das kein Standard ist? Ich hab mal eine Umwandlung für vierstellige Arrays in Zahlen geschrieben und das waren aber 70 Zeilen. Warum sollte ich es mir umständlicher als nötig machen? Oder gibt es dafür noch eine andere Funktion?



  • Little Programmer schrieb:

    Später benutze ich atoi, um die ausgelesenen Character in Zahlen umzuwandeln. Was soll ich denn sonst verwenden, wenn das kein Standard ist? Ich hab mal eine Umwandlung für vierstellige Arrays in Zahlen geschrieben und das waren aber 70 Zeilen. Warum sollte ich es mir umständlicher als nötig machen? Oder gibt es dafür noch eine andere Funktion?

    Zum Beispiel std::stringstream , schau mal bei www.cplusplus.com.



  • Danke, muss ich mich mal drin einarbeiten. Aber das war ja nicht mein eigentliches Problem, das ist nach wie vor unverändert. Hier mal die komplette Funktion, falls das endlich den Fehler offenbart. Die atois sind jetzt noch drin. Bitte einfach ignorieren im Moment. Erstmal zur Erklärung. Eine übergeordnete Funktion läd von Beginn der Datei an die ersten 20 Zeichen in AktuellesTextstueck[21] und gibt dies dann an die folgende Problemfunktion weiter. Anschließend werden wieder 20 Zeichen ausgelesen, diesmal aber vom zweiten Zeichen an. dies geht dann bis zum Ende der Datei. Die letzten 20 Zeichen sind Beschreibungstext für den Benutzer, da gibt es nichts mehr auszulesen. Das heißt jede dieser Funktionen wie die hier genannte wird einmal aufgerufen, während AktuellesTextstueck[21] gerade mit ihrem Suchtext beginnt. Ich weiß, dass ist eine miese Auslesefunktion, aber Hauptsache es funktioniert erstmal.

    #ifndef _FSTREAM_
    #define _FSTREAM_
    #include <fstream>
    #endif
    using namespace std;
    
    bool AufloesungLaden(int* Position, char AktuellesTextstueck[21], int* ScreenX, int* ScreenY)
    {
    int Uebereinstimmung = 0;
    bool Funktioniert = false;
    char aufloesung[18] = {'s','c','r','e','e','n',' ','r','e','s','o','l','u','t','i','o','n',':'};
    //Bildschirmauflösung auslesen
    for (int x = 0; x < 18; x++)
    {
    	if (AktuellesTextstueck[x] == aufloesung[x])
    	{
    		Uebereinstimmung++;
    	}
    }
    if (Uebereinstimmung == 18)
    {
        //Variable leeren
        for (int x = 0; x < 20; x++)
        {
            AktuellesTextstueck[x] = '\0';
        }
    
    	//Bildschirmauflösung auslesen, dazu erst zurück, ein Feld hinter das 18te
    	ifstream Aufloesung("config.txt", ios::in);
    	Aufloesung.seekg((*Position + 17), ios::beg);
    char Zahl[4];
    int nZahl;
    	//Leerzeichen überspringen
    	if (Aufloesung.get() == ' ')
    	{
            while (Aufloesung.get() == ' ')
            {
                Aufloesung.seekg(1, ios::cur);
            }
    	}
    
    	//X-Zahl auslesen
    	for (int zahlpos = 0; zahlpos < 20; zahlpos++)
    	{
    		if (Aufloesung.get() != 'x')
    		{
    			AktuellesTextstueck[zahlpos] = Aufloesung.get();
    			printf(&AktuellesTextstueck[zahlpos]);
    			printf("\n");
    		} else {
    			AktuellesTextstueck[zahlpos] = '\0';
    			zahlpos = 20;
    		}
    		Aufloesung.clear();
    		Aufloesung.seekg(1, ios::cur);
    		nZahl = Aufloesung.tellg();
    		printf("Position: %d\n", nZahl);
    	}
    
    	//Text in Zahl umwandeln
    	*ScreenX = atoi (AktuellesTextstueck);
        printf("ScreenX: %d\n", ScreenX);
    	//Y-Zahl auslesen
    	for (int zahlpos = 0; zahlpos < 20; zahlpos++)
    	{
    		if (Aufloesung.get() != 'x')
    		{
    			AktuellesTextstueck[zahlpos] = Aufloesung.get();
    		} else {
    			AktuellesTextstueck[zahlpos] = '\0';
    			zahlpos = 20;
    		}
    		Aufloesung.seekg(1, ios::cur);
    	}
    
    	//Schließen
    	Aufloesung.close();
    
    	//Text in Zahl umwandeln
    	*ScreenY = atoi (AktuellesTextstueck);
        printf("ScreenY: %d\n", ScreenY);
    
    	//Konfiguration erfolgreich geladen
    	Funktioniert = true;
    	printf("Aufloesung erfolgreich geladen.\n");
    }
    return Funktioniert;
    }
    

    Nochmal zum Problem, das muss in Zeile 55 liegen. Die Funktion befindet sich in dieser for-Schleife auf der 1 folgenden Textes:

    screen resolution: 124x78

    Aber die Ausgabe der printfs gibt folgendes aus:

    1
    Position: 147
    n
    Position: 156
    e
    Position: 165
    v
    Position: 173
    g
    Position: 180
    e
    Position: 187
    p
    Position: 193

    Aus irgendeinem Grund geht der Lesezeiger nicht ein Zeichen weiter, sondern mehrere, das wiederspricht jeglicher Logik. Der Fehler kann eigentlich nur in Zeile 55 liegen, aber die ist ja ziemlich übersichtlich:

    Aufloesung.seekg(1, ios::cur);
    

    Wie kann so etwas auftreten? Ich bin echt verwirrt. 😕

    Bitte, weiß denn niemand, wie so ein merkwürdiges Verhalten auftreten kann? 😞



  • Wahrscheinlich benutzt du get falsch. Ich kann deinen Algorithmus nicht wirklich entwirren, aber mir scheint, du bist dir nicht bewusst, dass zweimal get() hintereinander nicht zweimal dasselbe Zeichen liefert, sondern dass jedesmal der Dateizeiger eine Position weiterrückt.
    Das erklärt nicht, warum der Dateizeiger gleich 9 Zeichen hüpft, aber mehr als 1 Zeichen sollte es auf jeden Fall sein.


  • Administrator

    Little Programmer schrieb:

    Ich benutzte kein cout, weil ich SDL in dem Projekt eingebunden habe und iostream und SDL sich irgendwie nicht vertragen.

    Ehm, wie bitte? Könntest du dieses Problem etwas näher spezifizieren? Ein std::cout ist nicht viel anderes als ein std::ostream und ein std::cin nicht viel anderes als ein std::istream . Ein std::ifstream ist auch ein std::istream . Und den std::ifstream verwendest du ja.

    Little Programmer schrieb:

    Cin und <iostream> bringt mir leider überhaupt nichts, da ja eine Textdatei ausgelesen werden soll bzw. muss.

    std::cin ist ein std::istream Objekt. std::ifstream erbt von std::istream . Wenn dir da kein Licht aufgeht, dann lern zuerst C++.

    Dann nochmals zu deinem Code. Ganz ehrlich, der ist so hässlich, dass ich mir verdammt schwer tue, den durchzuarbeiten. Uneinheitliche Einrückungen, uneinheitliche Variablenbenennung, teilweise echt seltsame Vorgehensweise, unübersichtlich ... eben grauenhaft ...
    Auch hast du überhaupt nicht die Möglichkeiten von C++ genutzt, nicht einmal die Möglichkeiten von C. Es ist mir ein Rätsel, wie du auf die Idee kommst, in dem Stadium von deinem Wissen über C++ mit der SDL anzufangen. Das kann ja nur katastrophal enden.

    Nur kurz zu deiner Schleife, wieso dort mehr als 1 Zeichen vorgerückt wird:

    if(Aufloesung.get() != 'x') // get() -> Ein Zeichen vorwärts.
    {
        AktuellesTextstueck[zahlpos] = Aufloesung.get(); // get() -> Ein Zeichen vorwärts.
        // printf(&AktuellesTextstueck[zahlpos]);
        // Kann gefährlich sein. Zum Glück füllst du das Array zuerst mit Nullen.
        // Könnte man aber auch einfach anders angehen. Gäbe sowieso im ganzen Code
        // bessere Möglichkeiten. Erst recht mit C++
        printf("%c\n", AktuellesTextstueck[zahlpos]);
        // printf("\n"); // unnötig geworden.
    } else {
        AktuellesTextstueck[zahlpos] = '\0';
        zahlpos = 20;
    }
    // Aufloesung.clear(); // unnötig
    Aufloesung.seekg(1, ios::cur); // seekg -> 1 Zeichen vorwärts.
    nZahl = Aufloesung.tellg();
    printf("Position: %d\n", nZahl);
    

    Also ganz ehrlich. Es löst zwar vielleicht dein aktuelles Problem nicht, aber lern erst vernünftig C++, bevor du dich an sowas wie die SDL ranwagst. Mir scheint, du probierst ohne Wissen, irgendetwas hinzubiegen. So sieht jedenfalls dein Code aus 😉
    C++ ist eine sehr komplexe Sprache. Nimm dir Zeit, übe zuerst fleissig auf der Konsole. Der Einstieg ist hart und erfordert viel Geduld. Probier nicht an fortgeschrittene Themen zu gehen, wenn du einfach noch nicht bereit dafür bist. Es hat keinen Sinn, das endet nur in einem GAU. Arbeite zum Beispiel mal ein C++ Grundlagen Buch durch. Empfohlen werden oft:
    - C++ Primer
    - Thinking in C++ 1&2 (Auch gratis als PDF verfügbar, nur Englisch)

    Auch noch einen Tipp: Probier von deutschen Variablennamen wegzukommen. Das sieht einfach nur hässlich aus in Kombination mit den restlichen englischen Namen aus den Bibliotheken. Die Sprache in der Informatik ist hauptsächlich Englisch, daran sollte man sich gewöhnen.

    Grüssli



  • Danke für die ganzen Tipps. Bin zur Zeit dabei, "C++ für Dummies" durchzuarbeiten. Aber Bashar hat schonmal eine wichtige Sache genannt. Ich wusste nicht, das get() gleichzeitig ein Zeichen ausgibt und ein Zeichen weiter springt. Also eigentlich hätte es mir aus den ganzen while-Schleifen Beispielen aus dem Internet klar sein müssen, aber soweit hab ich nicht gedacht. Jetzt springt es immer noch zwei Zeichen weiter und liest ganz merkwürdige Zahlenwerte in die Integervariablen ein. Ich kenne fstream jetzt eigentlich nur aus dem Internet, aber es kommt noch einmal in meinem Buch vor. Ich lass das jetzt erstmal ruhen, bis ichs nochmal im Buch durchgelesen habe.


Anmelden zum Antworten