PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++ string-parsen



datalux
22-11-2007, 09:46
Hi,

folgende Situation.

Ich brauche für ein Programm ein config-file.
Dort verwende ich ein Keyword um den danach stehen Teil als Pfadangabe einzulesen (Bsp.: keyword = "/home/user/.conf/needDir/").

Dazu hab ich mal bischen rumgespielt (und das funktioniert auch):



#include <string>
#include <iostream>

using namespace std;


int main()
{
int lastPos; // of find-string

string full = "keyword test!";
string searchFor = "keyword";
string temp = "";

string::size_type loc = full.find( searchFor.c_str(), 0 );

if( loc != string::npos )
{
lastPos = full.find_last_of( searchFor.c_str() );

temp = full.substr( lastPos + 2 );

int newSize = temp.size();

temp.erase( newSize );

cout << temp << "\n";

}
else
{
cout << "\n\tELSE\n";
}

return 0;
}


Ausgabe ist dann wie gewollt "test!" (ohne "").

Jetzt hab ich das ganze in das gewollte Programm übernommen (und hier funktioniert es nicht).



int readConfigFile()
{
int returnValue = -1; // -1 => error!

string configFile = "./obrw.conf";
string configStrings[1024];
ifstream inputStream;
string s;
inputStream.open( configFile.c_str() );

int readedLines = 0; // einlesen der Vorhandenen Zeilen im Configfile

if( !inputStream )
{
cout << "Error opening input stream\n";
exit(1);
}
else
{
while( !inputStream.eof() )
{
getline( inputStream, s );
configStrings[readedLines] = s;
readedLines++;
}
inputStream.close();
}

int lastPos; // lastPos of parseword => 7 wäre hier richtig
string parseWpDir = "wpDir ="; // keyword for wallpaperDir
string tempString = ""; // includes the parsed strings

cout << "lines : " << readedLines << "\n";

for(int i = 0; i < readedLines; i++)
{
// at this position we are searching for parseString (parseWpDir)
string::size_type loc = configStrings[i].find( parseWpDir.c_str(), 0);

// string::npos if parseWpDir doesn't exist
if( loc != string::npos )
{
cout << "parse : " << parseWpDir << "\n";
cout << "configStrings[i] : " << configStrings[i] << "\n";


lastPos = configStrings[i].find_last_of( parseWpDir.c_str() );


cout << "lastPos : " << lastPos << "\n";


tempString = configStrings[i].substr(lastPos +2);


int newSize = tempString.size();
tempString.erase( newSize - 1 ); // holt das hinter " aus dem String


cout << "after erase: " << tempString << "\n";

returnValue = 999;
}
}

return returnValue;
}


Ausgabe:

lines : 3
parse : wpDir =
configStrings[i] : wpDir = "/home/user/.config/wallpapers/"
lastPos : 39
after erase: /

lastPos müsste nun aber eigentlich den Wert 7 haben.
Nun ja, ich versteh es nicht mehr wirklich.
Vielleicht übersehe ich ja auch nur eine Kleinigkeit, oder so.

Wäre schön, wenn ihr euch mal kurz mit dem code befassen könntet und vielleicht sogar den Fehler findet. :)

gruß Datalux

locus vivendi
22-11-2007, 11:14
Du solltest noch einmal nachlesen, was std::string::find_last_of macht. Die Funktion weicht, so wie ich das sehe, in ihrem Verhalten von deiner Erwartung ab.

Noch ein paar Anmerkungen:
1. Du musst nicht so oft string::c_str verwenden. Die meisten String-Funktionen sind doch eh überladen.
2. Es wäre vermutlich von Vorteil, wenn du deine Fehlerbehandlung ausbauen würdest, so das deine Funktion z.B. erkennen kann wenn nach einem "Keyword" in deiner Config-Datei gar kein Datum mehr folgt.
3. Deine Abfrage des Dateiendes ("while( !inputStream.eof() )") ist nicht robust. Du solltest besser prüfen, ob die letzte Eingabeoperation (also getline z.B.) fehlschlug. Wenn ja, dann könnte eof der Grund dafür sein.
4. Du kannst die Lesbarkeit deiner Funktion unter Umständen verbessern, indem du Variablen in den innersten möglichen Scope packst. Speziell meine ich deine "temp" Variablen. Die können in das "if" hinein, weil du sie außerhalb eh nicht brauchst. Aber Urteile selber.
5. Wenn du Puffer fester Größe verwendest ("string configStrings[1024]"), dann solltest du unbeding sicherstellen das du sie niemals außerhalb ihrer Größen indizierst. Also prüfe ob du eventuell mehr als 1024 Zeilen liest.

datalux
22-11-2007, 11:57
hi,

ich beziehe mich einfach schonmal zu deinem post (schafft Klarheit)

zu 1: hatte oft mal nen Fehler, wenn ich einen String direkt übergeben habe und so hab ich es mir angewöhnt.

zu 3: mhhh, ok (obwohl ich meine, dass auch ein leeres file eingelesen wird (leerer String), aber trotzdem müsste es ja noch funktionieren, da der pfad String an position i = 1 steht + es auch funktioniert, wenn ich lastPos manuel auf 7 setze.
Werde es aber trotzdem in die fix-liste zur Überprüfung nehmen.

zu 4: stimmt eigentlich :)

zu 2 und 5: Programm arbeitet mom nur für mich und ich stecke mom halt bei der ConfReadFunktion fest. Das Array ist z.B. momentan noch feste größe, da ich noch nicht soviel in C++ programmiert habe und schauen möchte, dass das Einlesen ordnungsgemäß funktioniert (aber steht halt schon in der fix-list, da das ja auch ein mieser Fehler wäre, der nicht auf jedem System zu finden ist ;) ).

anda_skoa
22-11-2007, 14:52
Wenn deine Datei im Muster


key = value

aufgebaut ist, wäre es vermutlich einfacher, nur nach dem "=" zu suchen, also



string::size_type pos = line.find('=');
if (pos != string::npos)
{
string key = line.substring(0, pos);
string value = line.substring(pos + 1);

// dann eventuell noch Leerzeichen entfernen
}
else
{
// ungültige Zeile
}


Wenn jeder Key eindeutig ist, kann man die Werte gleich direkt in einer Map ablegen



map<string, string> config;

config.insert(make_pair(key, value));


Ciao,
_

datalux
22-11-2007, 16:03
ok und dankeschön :)

Hab den Code jetzt mal funktionsfähig abgewandelt (ohne map-funktion, aber muss ich mir wohl mal anschauen :)).

axo, Config-Muster: wpDir = "/pfad/zum/verzeichnis/"
(erklärt den value.erase()-Aufruf in :167 und :168)




int readConfigFile()
{
int returnValue = -1; // -1 => method-error!

string configFile = "./obrw.conf";
string configStrings[1024];
ifstream inputStream;
string s;
inputStream.open( configFile.c_str() );

int readedLines = 0;

if( !inputStream )
{
cout << "Error opening input stream\n";
exit(1);
}
else
{
while( !inputStream.eof() )
{
getline( inputStream, s );
configStrings[readedLines] = s;
readedLines++;
}
inputStream.close();
}

// keywords to parse
string parseWpDir = "wpDir "; // keyword for wallpaperDir

for(int i = 0; i < readedLines; i++)
{
// at this position we are searching for possible stringpart ( '=' )
string::size_type loc = configStrings[i].find( '=' );

// string::npos if '=' doesn't exist in configStrings[i] (!!! if != x))
if( loc != string::npos )
{
string key = configStrings[i].substr( 0, loc );
string value = configStrings[i].substr( loc + 1 );

// is wpDir-pathVar?
if(key == parseWpDir)
{
//here we could start a value-check (regular-expression => injections possible?)

value.erase( 1, 1 ); // first "
value.erase( value.size() - 1 ); // last "
dirName = value; // set globalVar dirName

returnValue = 999; // returnValue for correct run
}
else
{
// error-msg if keyword doesn't match
cout << "Error in config (wpDir isn't ok)\n";
}
}//if ( loc != string::npos )
}//for

return returnValue;
}

datalux
22-11-2007, 16:16
Du solltest noch einmal nachlesen, was std::string::find_last_of macht. Die Funktion weicht, so wie ich das sehe, in ihrem Verhalten von deiner Erwartung ab.


Argh ...

Hab mir die Referenz gerade nochmal angeschaut und das Brett mal abgeschaft ...

Klar, den letzten, übereinstimmenden char ... :o

und mit wpDir findet er ihn bei mir in wallpaper vorher in Position 39.

omg, der Wald und die Bäume ...

Dankeschön nochmal :)