Problem: Werte aus String lesen



  • Hallo,

    ich habe folgende Zeile als String vorliegen:

    string line = A_BC | 1.23 | Irgendein Text
    

    Nun möchte ich sowohl das A_BC als auch das 1.23 als String bzw. Double aus der Zeile auslesen. Dafür habe ich folgendes geschrieben, was soweit auch funktioniert:

    string::size_type found = string::npos;
    found = line.find_first_of('|');
    size_t begin = line.find_first_not_of(' ', found+1);
    size_t end = line.find_first_of('|', found+1);
    size_t length = end - begin;
    string s = line.substr(0, found-1);
    double d;
    sstr << line.substr(begin, length);
    sstr >> d;
    

    Jetzt kann es allerdings vorkommen, dass nach dem A_BC mehrere Leerzeichen stehen und dann funktioniert es nicht mehr. Also hab ich es folgendermaßen umgeschrieben:

    stringstream sstr(line);
    char pipe;
    string s;
    double d;
    sstr >> s >> pipe >> d;
    

    So würde es eigentlich schon funktionieren, allerdings hab ich jetzt gesehen, dass der Double-Wert auch ein String sein kann (und zwar bestehend aus mehreren Worten, durch Leerzeichen getrennt).
    Jetzt wird allerdings immer nur das 1. Wort eingelesen. Ich dachte eigentlich, dass das mit der 1. Variante funktionieren sollte, aber dem ist nicht so.

    Könnt Ihr mir vielleicht helfen, wie ich das mit diesen 2 Sonderfällen in eine Funktion reinbekomme?
    Vielleicht (nein bestimmt) kann man das Ganze insgesamt irgendwie schöner lösen? Bin für jeden Tipp dankbar!



  • Ich rate Dir davon ab, das ganz in genau eine Funktion pressen zu wollen. Du hast hier mehrere Problemstellungen (jede möglich Repräsentation einer Datenzeile entspricht einer weiteren Problemstellung - genauer, deren Interpretierung):

    Die Formalregeln habe ich noch aus Deinen vorherigen Threads im Kopf und fasse diese hier zusammen (korrigiere mich, wenn ich daneben liege).

    Aufbau (Format) eines Datensatzes (Zeile) innerhalb der Textdatei:

    (Schlüssel) "|" (Wert) ["|" (Kommentar)]
    

    A. Wobei gilt:
    1. Dem Pipe-Zeichen ("|") darf eine beliebige Anzahl Leerzeichen vorausgehen oder folgen.
    2. Der Zeile darf eine beliebige Anzahl Leerzeichen vorausgehen oder folgen.
    3. Der Kommentar ist (wie oben dargestellt) nebst vorausgehendem Pipe-Zeichen optional.

    B. Ferner gilt:
    1. 'Wert' kann eine "normale" Zeichenkette sein oder
    2. ein Literal einer Fließkommazahl sein.

    Dein erstes Problem ist nun, aus einer eingelesenen Zeile, die drei Komponenten Schlüssel, Wert und Kommentar herauszusondern. Du benötigst eine Funktion, welche eine Zeile (== Zeichenkette / std::string) annimmt und drei Zeichenketten zurückgibt:

    // Struktur des Datensatzes
    struct Datensatz
    {
        std::string schluessel;
        std::string wert;
        std::string kommentar;
    };
    
    // Funktionsprototyp
    Datensatz zeile_aufteilen(const std::string& zeile);
    

    In der Funktionsdefinition musst Du die Formalregeln für den einzelnen Datensatz und die Zusatzbedingungen (A) berücksichtigen. Welchen Inhalt 'Wert' bis zu diesem Zeitpunkt hat (ob nun "normaler Text", Fließkommazahl oder Käsekuchen) ist unerheblich, da es ein weiteres Problem darstellt.

    Dein zweites Problem ist es, den Wert zu interpretieren. Das spar ich mir im Moment aus. Erstmal sollte das erste Problem angegangen werden.

    Grüße... Heiko



  • LadyM schrieb:

    Könnt Ihr mir vielleicht helfen, wie ich das mit diesen 2 Sonderfällen in eine Funktion reinbekomme?
    Vielleicht (nein bestimmt) kann man das Ganze insgesamt irgendwie schöner lösen? Bin für jeden Tipp dankbar!

    ja - den Tipp hast Du schon bekommen - und zwar am 2.Feb. in diesem Beitrag. Hast Du dazu noch Fragen?

    Gruß
    Werner



  • LadyM schrieb:

    So würde es eigentlich schon funktionieren, allerdings hab ich jetzt gesehen, dass der Double-Wert auch ein String sein kann (und zwar bestehend aus mehreren Worten, durch Leerzeichen getrennt).

    "123 456" sind entweder zwei zahlen, eine fehlerhafte eingabe, oder du löscht die leerzeichen dazwischen und machst daraus "123456"

    "123 456 789" sind entweder drei zahlen, eine fehlerhafte eingabe, oder du löscht die leerzeichen dazwischen und machst daraus "123456789"
    usw.
    🙂



  • Werner Salomon schrieb:

    LadyM schrieb:

    Könnt Ihr mir vielleicht helfen, wie ich das mit diesen 2 Sonderfällen in eine Funktion reinbekomme?
    Vielleicht (nein bestimmt) kann man das Ganze insgesamt irgendwie schöner lösen? Bin für jeden Tipp dankbar!

    ja - den Tipp hast Du schon bekommen - und zwar am 2.Feb. in diesem Beitrag. Hast Du dazu noch Fragen?

    Gruß
    Werner

    Hallo Werner,

    du kannst von dir nicht auf andere schließen. 😉

    Wenn Sie das Ihrem Lehrer oder Professor zeigt, dann wird er mit gezielten Fragen feststellen, dass der Code von dir kommt. 😃

    Ich habe das Programm mal laufen lassen. Ich verstehe zwar, dass es immer zur Abbruchbedingung kommt, aber wie könnte ich das Warning wegbekommen?

    warning C4715: 'read<double>' : not all control paths return a value
    

    Gruß
    Markus



  • Hm, ich habe mich geirrt, die return Anweisung wird überhaupt nicht ausgeführt.

    template< typename T >
    std::istream& read( std::istream& in, std::map< std::string, T >& m )
    {
      using namespace std;
      char pipe;
      for( std::pair< string, T > x; (in >> x.first >> pipe >> x.second).ignore( numeric_limits< streamsize >::max(), '\n' ); )
      {
        if( pipe != '|' )
        {
          in.setstate( ios_base::failbit );
          return in;
        }
        if( !m.insert( x ).second )
          cerr << "Warnung: doppelter Eintrag - " << x.first << endl;
      }
    }
    

    Werner Salomon schrieb:

    Hast Du dazu noch Fragen?

    Auch wenn du mit dem Du nicht mich meinst 😉 stelle ich mal meine Frage.

    Was passiert in dieser Bedingung?

    (in >> x.first >> pipe >> x.second).ignore( numeric_limits< streamsize >::max(), '\n' );
    

    Ich versuche mal soweit es zu erklären wie ich es verstehe. Der Instream schreibt in x.first, pipe und x.second. Der ganze Ausdruck ist geklammert. Aber dann. Auf was wird nun das .ignore ausgeführt? Was erreicht du mit der Parameterliste? Anscheinend ist diese Bedignung falsch, wenn das Testdatei so aussieht:

    ABC | 1.23 | Kommentar1\n
    DEF | 4.56 | Kommentar2\n
    GHI | 7.89 | Kommentar3\n

    Wieso?

    Gruß
    Markus



  • Och das ist doch einfach 😉
    Also bei der read fehlt ein return in; am Ende ...

    Was die Zeile macht? Naja es wird erst die Klammer ausgeführt, d.h. x.first wird ausgelesen, trennzeichen eingelesen und dann x.second ausgelesen. Soweit war es dir ja klar. operator>> eines std::istream gibt eine Referenz auf sich selber zurück (was auch bsw. std::cout << 10 << "AUTO!"; usw. möglich macht!).

    in >> x.first >> pipe >> x.second;
    in.ignore( numeric_limits< streamsize >::max(), '\n' );
    

    wäre also das selbe!

    Nun gibt ignore auch wieder eine Referenz auf in zurück. Diese wird dann per operator void* auf NULL getestet und somit getestet, ob ein Fehler beim einlesen aufgetreten ist 🙂



  • Hi (D)evil,

    (D)Evil schrieb:

    Diese wird dann per operator void* auf NULL getestet

    Wo wird den mit operator void* auf NULL gestestet? Das verstehe ich nicht.

    Gruß
    Markus


Anmelden zum Antworten