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; EndeAnwendung:
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);//FunktionsaufrufWas 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 sollschltxt.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=Wert1bPS: 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.