PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C/C++ : Stream "Intelligent Parsen"



DanDanger
25-06-2003, 23:50
Hi,

gibt es eine "Intelligente" Methode, einen String zu "Parsen" ???

z.B.

Ich habe einen String in Folgender Form : <string Wert_1> <int Wert_2> <int Wert_3>#
Die Werte sind mit Leerzeichen getrennt, die "#" Kennzeichnet den Abschluss eines solchen "Übertragungsstrings".

Das ganze könnte z.B. so aussehen : TestName 20 30#

Nun möchte ich diesen String in einer Funktion Parsen, so dass z.B.
string Wert_1Var = TestName
int Wert_2Var = 20
int Wert_3Var = 30
ist.
Die Funktion soll also in die Variabeln Wert_1Var, etc. die entsprechenden Werte aus dem Übergebenen String reinschreiben.


Zur Zeit mache ich das noch in do....while Schleifen, und Lese jedes Zeichen einzeln ein.
Das ist aber soooo Unglaublich langsam, das ne' bessere Lösung her muss:D :D

Meine Frage :
Hat da jemand ne' Idee (gibt's für sowas vielleicht sogar ne' STL-Funktion/Lib) ???

Neugierige Grüsse
DanDanger

DanDanger
26-06-2003, 04:37
Hi,

ich hab mir mal die strtock() Funktion aus der <stdio.h> genauer angesehen :



#include <string>
#include <stdio.h>

using namespace std ;

void Zerlege(char *Text)
{
char *partString ;

partString = strtok(Text, " ") ;
}


Leider kommt es in der strtok-Funktion zu einem Speicherzugriffsfehler :confused:

Allerdings Funktioniert :


#include <string>
#include <stdio.h>

using namespace std ;

void Zerlege()
{
char *partString ;
char Text[] = "TestName 100 200#" ;

partString = strtok(Text, " ") ;
}


ohne Probleme :confused:


Kann mir da jemand helfen ????

Neugierige Grüsse
DanDanger

wraith
26-06-2003, 07:12
Du kannst einfach vom Stream lesen,ob das jetzt ein Filestream ist oder der Inputstream ist egal.
Ein Bsp,mal mit stringstream (alles andere läuft analog)


#include <sstream>
#include <string>
using namespace std;
....
string Text = "Textname 100 200#";
stringstream stream(Text);
string Name;
int zahl1,zahl2;
char c;
stream >> Name >> zahl1 >> zahl2 >> c;

kehj
26-06-2003, 11:33
@wraith:
Ohne dich angreifen zu wollen, aber ich halte den Ansatz für ziemlich schlecht. Was machst du bei einem String der Art "Falsch 10 bla 20#" ?

Ich muß ja gestehen, daß meine C++ Kenntnisse ziemlich geschränkt sind, aber ich würde mal gehaupten, daß da ziemlicher Quatsch rauskommt. (Bei mir für zahl2 134514290)

Ich würde persönlich den String auch erstmal in Token zerlegen und die dann einzelnd verarbeiten. Und wenn die strtok-Funktion nicht funktioniert, sollte es wohl nicht so schwer sein, sich so 'ne Funktion selbst zu schreiben, oder?

Und wenn du es auf die Spitze treiben willst, entwirfst du einen ganz simplen endlichen Automaten, der dir am Ende sagst, ob die Eingabe syntaktisch korrekt war. Ist für das Beispiel zwar übertrieben, wenn du aber Zeilen der Art attribut=<wert> hast und die Dinger in beliebiger Reihenfolge auftreten können, ist er unerläßlich...

Oder sehe ich das falsch?

peschmae
26-06-2003, 11:43
gibt es nicht irgendwo eine Regexp - Lib für C++?

Wär noch angebracht

MfG Peschmä

wraith
26-06-2003, 11:59
Original geschrieben von kehj
[B]@wraith:
Ohne dich angreifen zu wollen,

Nur zu,das scheint gerade Mode zu sein :).



aber ich halte den Ansatz für ziemlich schlecht.

Der Ansatz ist für dieses Problem korrekt und richtig,und die Art und Weise,wie man das in C++ macht.



Was machst du bei einem String der Art "Falsch 10 bla 20#" ?

Das war nicht die Anforderung des OP.Wie der String aufgebaut ist,steht da ganz klar.
Man könnte noch nach jeder Extraktion den Status des Stream testen,mit fail(),aber ich muß hier ja keine Beispiele schreiben,die alle möglichen Fehlersituationen abfangen.
Es ging nur um Prinzip,und das ist korrekt.



Und wenn die strtok-Funktion nicht funktioniert, sollte es wohl nicht so schwer sein, sich so 'ne Funktion selbst zu schreiben, oder?

Ja,und so eine Funktion wird intern mit stringstreams arbeiten :),womit wir wieder beim Anfang wären.



und die Dinger in beliebiger Reihenfolge auftreten können, ist er unerläßlich...

Siehe oben,die Aufgabe war klar formuliert.

So long :)

DanDanger
26-06-2003, 13:06
Hi,

wraith's Lösung ist Wirklich Goldrichtig :D
Da ich ja die Reihenfolge kenne, in der die Variablentypen auftauchen, (int, string,....) funzt seine Lösung wirklich Super:D

DANKE :-)

anda_skoa
26-06-2003, 16:02
Original geschrieben von DanDanger
Hi,

wraith's Lösung ist Wirklich Goldrichtig :D


Definitiv!

Ich hätte auch ein stringstream Beispiel geschrieben, wenn nicht schon eins da gewesen wäre.

Ciao,
_

DanDanger
27-06-2003, 15:20
Hallo,

ne' kleine Frage zu den String-Streams hab' ich noch (aus den Manpages werd' ich nicht wirklich schlau...) :

Die String-Streams scheinen ja Anhand der Leerzeichen den String "Automagisch" in seine Tokens zu teilen.

Gibt es eine Möglichkeit, dieses "Teilerzeichen" zu Ändern.

z.B. Anstatt : string Text = "Test1 34 45#" ; // String wird duch Lerrzeichen ' ' seperiert
Nun : string Text = "Test1*34*45#" ; // String wird nun durch '*' seperiert


Neugierige Grüsse
DanDanger


PS: Gibt es "gutes" Buch über die STL , das Ihr mir empfehlen könntet ???

anda_skoa
27-06-2003, 15:31
das ist auch kein Problem, du musst nur ein Einzelzeichen lesen, so wie vorher das # Zeichen.

also in etwa


stream >>zahl1 >> c >> zahl2 >> c;


Ciao,
_

wraith
27-06-2003, 15:42
Etwas tricky ist das '*' zwischen dem String und der Zahl,das würde er sonst in einem Rutsch als ein String lesen.


string Text = "Textname*100*200#";
stringstream stream(Text);
string Name;
int zahl1,zahl2;
char c;
getline(stream,Name,'*'); //Name gesondert einlesen.
stream >> zahl1 >> c >> zahl2 >> c;

DanDanger
27-06-2003, 15:56
@wraith : Genau da liegt das Problem :

Bei : Text = "Textname*100*200#";
wird alles als ein einziger String eingelesen.........

Na, so wieh's aussieht komme ich um ein Manuelles zerlegen des Strings nicht herum
:mad:

Mann, ich HASSE es, Text zu parsen (wie machen das bloss die Compilerbauer.....?:p )

wraith
27-06-2003, 16:01
Original geschrieben von DanDanger
@wraith : Genau da liegt das Problem :

Bei : Text = "Textname*100*200#";
wird alles als ein einziger String eingelesen.........

Ja,darum habe ich ja eine leicht veränderte Version geschrieben :).
Nicht bemerkt,oder nicht das was du wolltest?

DanDanger
27-06-2003, 18:02
@wraith : Hehe, gar nicht bemerkt (Ich brauch' Urlaub :D )

Nun funzt alles, wie es soll :-)

Nochmals: DANKE :D :D

anda_skoa
27-06-2003, 18:53
Original geschrieben von DanDanger
Mann, ich HASSE es, Text zu parsen (wie machen das bloss die Compilerbauer.....?:p )

Die benutzen generierte Lexer und Parser.

Ciao,
_

wraith
27-06-2003, 18:59
Original geschrieben von anda_skoa
Die benutzen generierte Lexer und Parser.

Oder schreiben einen kompletten Parser per Hand from Scratch :)
Siehe http://gcc.gnu.org/gcc-3.4/changes.html.


A hand-written recursive-descent C++ parser has replaced the YACC-derived C++ parser from previous GCC releases. The new parser contains much improved infrastructure needed for better parsing of C++ source codes, handling of extensions, and clean separation (where possible) between proper semantics analysis and parsing. The new parser fixes many bugs that were found in the old parser.

DanDanger
29-06-2003, 23:26
Hallo,

Mit Eurer Hilfe habe ich's geschaft, meine Text-Parsing Funktion zu schreiben :-)
Nun, strings der Form "Text1 23 45#" kann ich nun Parsen.

Leider Bekommt meine Text-Parsen Funktion Ihre string-Texte nun übers Netzwerk (BSD-Socket's), und da kommen dann solche Sachen an:

"Text1 23 45#Text2 19 23#"

Damit meine Text-Parsing Funktion des vorherigen Posts greift, müsste ich also den String in seine beiden "Teilstrings" (durch '#' getrennt) zerlegen,
also aus : "Text1 23 45#Text2 19 23#"
-> "Text1 23 45#"
-> "Text2 19 23#"
machen.

Meine Frage : Wie geht das ??
Ich habe schon so ziemlich alle Varianten von scanf(), cin.ignore(), etc. Ausprobiert,
aber ohne Erfolg.

PS : Ich habe ausser dem String auch noch die Anzahl der enthaltenen Zeichen (von den Socket's).

PPS: Kann mir jemand ein gutes Buch zu der STL (besonders den String-Manipulations-Routienen empfehlen) ??

Neugierige Grüsse
DanDanger

wraith
30-06-2003, 09:20
Original geschrieben von DanDanger
...und da kommen dann solche Sachen an:

"Text1 23 45#Text2 19 23#"

Damit meine Text-Parsing Funktion des vorherigen Posts greift, müsste ich also den String in seine beiden "Teilstrings" (durch '#' getrennt) zerlegen,

Das mußt du nicht,da die Daten in einer geordneten Reihenfolge kommen.
Für Daten der Form n-mal "Text 23 213#",kannst du einfach so parsen


#include <vector>
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

struct Data
{
string text;
int zahl1,zahl2;
};

int main()
{
string Text = "Text1 23 45#Text2 19 23#Text3 23 34#";

stringstream stream(Text);

char c;
typedef vector<Data> DVEC;
typedef DVEC::const_iterator DVECITER;
DVEC vec;

while(!stream.fail())
{
Data temp;
stream >> temp.text >> temp.zahl1 >> temp.zahl2 >> c;
if(!stream.fail())
vec.push_back(temp);
}

for(DVECITER i = vec.begin();i != vec.end();++i)
cout << i->text << " " << i->zahl1 << " " << i->zahl2 << endl;
}


Aber du kannst den String auch zerlegen


#include <string>
#include <iostream>

using namespace std;

int main()
{
string Text = "Text1 23 45#Text2 19 23#";

string::size_type pos = Text.find_first_of('#');
string text1 = Text.substr(0,pos + 1);
string text2 = Text.substr(pos + 1,string::npos);

cout << text1 << endl << text2 << endl;
}

SourceKoT
21-07-2003, 21:38
Die ganze Typumwandlung in gcc 2.96 funktioniert nicht mit stringstreams!!!
Voll der scheiß

wraith
22-07-2003, 07:19
Original geschrieben von SourceKoT
Die ganze Typumwandlung in gcc 2.96 funktioniert nicht mit stringstreams!!!
Voll der scheiß
Ja,da war doch was :).
Da mußt du mit strstream arbeiten (im Header strstream.h),ist etwas unbequemer,aber besser als die C Funktionen.

anda_skoa
22-07-2003, 14:57
Original geschrieben von SourceKoT
Die ganze Typumwandlung in gcc 2.96 funktioniert nicht mit stringstreams!!!
Voll der scheiß

Kann ich nicht bestätigen.

Hab das hier mit einem g++2.96 auf RH7.3 ausprobiert


#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main()
{
int x,y,z;

stringstream istr("34 88 -144");

istr >> x >> y >> z;
cout << x << " " << y << " " << z << endl;

return 0;
}


Funktioniert einwandfrei.

Ciao,
_

Badsteve
22-07-2003, 16:13
RedHat benutzt auch einen gepachten gcc.


Steve

anda_skoa
22-07-2003, 16:42
Original geschrieben von Badsteve
RedHat benutzt auch einen gepachten gcc.


ja, darum heißt er auch 2.96

Ciao,
_

Badsteve
22-07-2003, 17:07
Original geschrieben von anda_skoa
ja, darum heißt er auch 2.96

Ciao,
_

stimmt ja.
sch****

Steve