Eure Meinung ist gefragt



  • Ich hatte das Problem, dass mein Programm "von aussen" mit einer Konfigurationsdatei Einstellungen einlesen können sollte. Da ich leider keine fertige Lösung gefunden habe, habe ich selber eine geschrieben.

    Die Datei sieht dabei so aus:

    Prog.conf:

    Schlüsselwort1 = 1000;
    Schlüsselwort2 = 2000;
    Ende
    

    Anwendung:

    std::string datei("conf.ini"),//Deffinition Dateiname der Konfiguratonsdatei
    port("Port = ");//Schlüsselwörter
    int portr=0//Hier wird der ausgelesene Wert gespeichert
    
    error = lkonf(datei, port, portr);//Funktionsaufruf
    

    Was mich vor allem interessiert ist, wie ihr es gelöst hättet. Welche Fehler im Code sind. Welche Ausnahmeregelungen noch zu treffen sind etc.

    Hier der Code:

    schltxt.h

    //Funktion sucht in einer Datei nach einem String und extrahiert einen Zahlenwert.
    //lkonf steht für "lese Konfiguration"
    int lkonf(std::string, std::string, int&);//Schlüsseltext, Dateiname, gesuchte; Variable gibt Fehlercode zurück
    
    //Funktion schreibt den Schlüsseltext in die Datei. Hängt an das Dateiende den Schlüsseltext an.
    //Vorher prüfen ob der Text nicht vorkommt, da der String einfach ans Ende angehängt wird.
    int skonf(std::string, std::string, int);//Dateiname, Schlüsseltext, Variablenwert; gibt Fehlercode zurück
    
    //Liefert den entsprechnenden Fehlertext
    void errtxt(int, std::string&);//Fehlercode, String in den der Fehlertext geschrieben werden soll
    

    schltxt.cpp

    int lkonf(std::string dname, std::string stxt, int& rueck)
    {
    	int error = 0;//Gibt an ob ein Fehler enstanden ist
    
    	//=== Öffnen der Datei ===
    
    	std::fstream DATEI(dname.c_str(), std::ios::in);//Öffnet die übergebene Datei zum lesen
    
    	if(DATEI.is_open() == true)
    	{
    		error = 0;//Fehlerspeicher 0, Datei vorhanden
    		unsigned int Dateilaenge = 0;//Speichert die Dateilaenge
    
    		DATEI.seekg(0);// An den Dateianfang springen
    		//Auf die Datei kann jetzt mit DATEI zugegriffen werden
    
    		//=== Auslesen des Dateiinhalts ===
    
    		unsigned int  i=0; //Dateipositionszähler
    		char j='\0'; //Zwischenspeicher des Dateiinhaltes
    		std::string such;
    
    		while(!DATEI.eof())//Die Datei bis zum Ende durchgehen
    		{
    			DATEI.get(j);//Das nächste Zeichen einlesen
    			such += j;//und an den Arbeitsstring anhängen
    			i++;//Ein Zeichen weiterschalten
    		}
    		//Der Dateiinhalt steht jetzt in der Variablen "such" zur Verfügung
    
    		Dateilaenge = unsigned int(such.length());
    
    		//=== Sucht im Dateiinhalt nach dem gesuchten Text ===
    
    		std::string::size_type START = 0;//Speichert den Startpunkt des gesuchten Strings
    		std::string::size_type ENDE = 0;//Zeigt auf den nächsten Zeilenumbruch nach dem gesuchten String (Stringende)
    		std::string::size_type ZEILU = 0;//Speichert den Zeilenumbruch nach dem START
    
    		START  = such.find(stxt,0);//Die Funktion sucht in der Datei nach dem gesuchten Schlüsseltext
    
    		if(START >= Dateilaenge)
    		{
    			error = 2;//Der gesuchte String konnte nicht gefunden werden
    		}
    		else
    		{
    			error = 0;
    			size_t laenge = stxt.size();//Die Länge des Schlüsseltextes finden ("Schlüsseltext = ")
    			ZEILU = such.find("\n", START);//Sucht das Zeilenende nach dem Start des Schlüsselwortes
    			ENDE  = such.find(";", START);//Sucht nach dem nächsten Semikolon nach dem Beginn des Schlüsseltextes (Stringende)
    			//Es stehen der Anfang und das Ende des gesuchten Strings nun in START und ENDE zur Verfügung
    
    			if(ZEILU <= ENDE)
    			{
    				error = 3;//Semikolon am Ende der Zeile fehlt -> Komentar
    			}
    			if(START+laenge == ENDE)
    			{
    				error = 4;//Schlüsseltext vorhanden jedoch kein Wert eingetragen
    			}
    			else
    			{
    				error = 0;
    
    				//=== Extrahieren des Schlüsseltextes ===
    
    				std::string ergebnis;//Speicher für den Schlüsseltext mit Wert
    
    				size_t m = START+laenge;//Nach dem Schlüsseltext steht der geuchte Wert. m ist der Begin des Wertes
    				do{
    					ergebnis += such.at(m);//Den gesuchten Wert zeichenweise speichern
    					m++;//Ein Zeichen weiterschalten
    				}while(m <= ENDE);//So lange bis man an das Zeilenende kommt
    
    				// === Rückgabe ===
    				rueck = atoi(ergebnis.c_str()); //Umwandlung des Strings in einen C-String danach in einen Integer und danach Rückgabe an das aufrufende Programm
    			}
    		}
    	}
    	else
    	{
    		error = 1;//Datei konnte nicht gefunden werden
    	}
    
    	DATEI.close();//Datei schliessen
    	return error;
    }
    
    int skonf(std::string dname, std::string stxt, int vari)
    {
    	int error = 0; //Fehlercode 0 = alles OK
    	std::fstream DATEI(dname.c_str(), std::ios::out | std::ios::app);//Öffnet die übergebene Datei zum schreiben oder legt Sie an, wenn sie nicht gefunden werden kann.
    	if(DATEI.is_open() == true)//Prüft ob die Datei vorhanden ist
    	{
    		error = 0;//Alles OK
    		DATEI << "\n" << stxt << vari << ";"; //Schreibt den String und den Wert in die Datei, sodass die Ausleseroutine ihn extrahieren kann
    	}
    	else
    	{
    		error = 1;//Wenn die Datei nicht gefunden werden konnte
    	}
    	return error;
    }
    
    void errtxt(int errstf, std::string& errmldg)
    {
    	//errstf = Error Stufe
    	if(errstf == 0)
    	{
    		errmldg = "Kein Fehler aufgetreten";
    	}
    	else if(errstf == 1)
    	{
    		errmldg = "Datei konnte nicht gefunden werden";
    	}
    	else if(errstf == 2)
    	{
    		errmldg = "Der gesuchte Schl\x81sseltext konnte nicht gefunden werden";
    	}
    	else if(errstf == 3)
    	{
    		errmldg = "Syntax Error, Semikolon am Zeilenende fehlt. Bitte ergänzen";
    	}
    	else if(errstf == 4)
    	{
    		errmldg = "Syntax Error, Bitte tragen Sie einem Wert ein";
    	}
    }
    


  • Ohne mir den Quelltext jetzt genauer anzuschauen, hätte ich die zeile nach einem "=" durchsucht, alles was davor kommt ist dann dein schlüsselwort (am besten noch trimen). Alles was danach ist, kannst du dann mittels stringstream in einen int verwandeln.
    Und das alles dann in eine map<string, int> speichern.
    Wozu brauchst du eigentlich das "Ende" Schlüsselwort?

    P.S.: Hab die ";" übersehen. Also erst nach denen suchen (am besten die ganze datei in einem rutsch lesen, ohne rücksicht auf '\n', dann spliten und dann nach "=" suchen.



  • Unter WinAPI könntest du auch noch GetPrivateProfileInt() und Konsorten verwenden - die verwenden INI-Dateien, die nach einer ähnlichen Struktur aufgebaut sind:

    [Sektion 1]
    Var1=Wert1
    Var2=Wert2
    
    [Sektion 2]
    Var3=Wert3
    Var1=Wert1b
    

    PS: Und ich bin mir ziemlich sicher, deinen Code kann man noch optimieren - das sieht ja (nach erstem Überblick) grauenhaft aus.

    (ich hab' mir jetzt nicht alles angesehen, aber zumindest die errtext()-Funktion kann man eleganter per switch() oder über ein string-Array lösen:

    string errtext(int errstf)
    {
      static const string msgs[]={
        "Kein Fehler aufgetreten",
        "Datei konnte nicht gefunden werden"
        "Der gesuchte Schl\x81sseltext konnte nicht gefunden werden",
        "Syntax Error, Semikolon am Zeilenende fehlt. Bitte ergänzen",
        "Syntax Error, Bitte tragen Sie einem Wert ein",
        "Unbekannter Fehler"
      };
      static const int msgs_ct = sizeof(msgs)/sizeof(msgs[0]);
      if(errstf>=msgs_ct) errstf=msgs_ct-1;
      return msgs[errstf];
    }
    


  • Das Ende Schlüsselwort stammt noch aus einer alten Version, welche das Semikolon nicht kannte und den Zeilensprung als Endkennung hatte. Das Ende bewirkt dann einen Zeilensprung in der letzten Zeile. 🙄

    Ich habe den Tipp mit switch Gleich mal umgesetzt.

    Mit der Zeit vergisst man so manche Dinge. Ausserdem bin ich noch nicht sooo lange dabei.

    Was genau sieht denn so grauenhaft aus? 😞

    Grüße



  • Erstmal brauchst du den Fehlerstatus nicht so ewig mitschleifen - wenn es einen Fehler gab, brichst du per "return xxx;" ab und gut ist. (das erspart auch einige if-else-Kaskaden in deiner Funktion)

    Zweitens kannst du eine Textzeile schneller per getline() einlesen (hat sogar den Vorteil, daß du den Zeilenwechsel als Endemarke verwenden kannst - da brauchst du weder ein ; am Zeilenende noch ein "ENDE" zum Abschluß).

    Und drittens prüft man besser auf datei.fail() als auf datei.eof() - sonst landest du in einer Endlosschleife, wenn beim Einlesen Formatfehler o.ä. aufgetreten sind.


Anmelden zum Antworten