"Preben Holm" <64bitNOSPAMNO@mailme.dk> wrote in message
news:4332f801$0$49013$14726298@news.sunsite.dk...
> Hej gruppe,
>
> jeg har et meget mystisk problem.
>
> Det er meningen at jeg skal parse en fil som indeholder properties og
> attributes - som sådan virker min løsning ganske udmærket, men med visse
> uheldige sammensætninger, som bevirker, at det ikke altid virker som det
> skal.
Parsning af tekst er svært - hvis det skal virke robust.
F.eks. får du læst devices selvom der ikke er nogen } i teksten
Som Michael Rasmussen skriver kan det være en ide at lave en
tilstandsmaskine for parseren.
Personligt skal jeg ikke parse ret meget tekst, før jeg benytter
Boost::Spirit (
http://www.boost.org/libs/spirit/index.html).
Det virker måske lidt mere kompliceret til at starte med, men det er langt
simplere at lave rigtigt og vedligeholde. Man bruger en formel syntaks
specifikation, der ligger tæt på EBNF.
>
> Et eksempel er følgende:
> --------------------------------------------
> { "Link0"
> Position (0, 0, 0)
> RPY (0, 0, 0)
> ReferenceFrame ""
> Attributes
> Link
> }
>
> { "Link1"
> Position (0, 0, 0.0)
> RPY (0, 0, 0)
> ReferenceFrame "Link0"
> Attributes
> Link
> JointName "axis0"
> ActiveJoint
> Revolute
> JointPosLimit -185 185
> JointVelLimit 156
> JointAccLimit 312
> }
> --------------------------------------------
>
> For hver "{" til "}" er der tale om et nyt "objekt".
> Jeg læser en linie ad gangen, og får udtrykt en property (f.eks. Position)
> ved navn på property og så også parameteren.
>
> Dette gemmes i klasser af typen Property. Dernæst er der tale om
> attributter.
>
> Når jeg når attributterne, så læses f.eks. "Link" korrekt, men JointName
> forkert, fordi der tilføjes et mellemrum. Om det er fordi der tidligere er
> læst en attribut uden værdi og der så kommer en værdi, eller det er fordi,
> det er en attribut har jeg meget svært ved at finde ud af. Men Det er her
> problemet er.
Koden kan simplificeres en del, og så ser det ud til at virke.
Se nedenfor.
[8<8<8<]
> Property::Property(const std::string& parseString) {
> _name = "";
> _value = "";
Hvis _name og _value er string objekter er den tildeling overflødig.
Foretræk iøvrigt initialisering frem for tildeling
Property::Property(const std::string& parseString) :
_name(""),
_value("")
{
selvom det også er overflødigt.
> std::string s = parseString;
Den kopi er overflødig.
>
> int i = 0, val = 0;
Typen som string::find returnerer er string::size_type - ikke int.
Derfor
string::size_type i = 0, val = 0;
så slipper du for alle de cast
> while ((int) s.find("\t", i) == i || (int) s.find(" ", i) == i) {i++;}
Du kan måske bruge funktionen isspace
> val = s.find(" ", i);
>
> if (val >= 0)
> _name = s.substr(i, val-1) + ".";
Det skal vist være s.substr(i, val-i)
> else
> _name = s.substr(i, (int) s.size() - i - 1) + ".";
Ved at droppe anden parameter får du resten af teksten: s.subst(i);
>
> int vallen = (int) s.size() - val - 2;
> if (val != -1)
> _value = s.substr(val+1, vallen); // The "\n" omitted
> else
> _value = "";
Den else er overfløde.
Ved at undlade den syntes jeg at koden tydeligere viser at value er
optionel.
[8<8<8<]
> void DeviceParser::loadObjects(std::ifstream& ifs) {
Det er egentligt tilstrækkeligt at bruge en istream& i stedet for en
ifstream&.
[8<8<8<]
> Resten af koden ses på
www.interrupt.dk/deviceparser.tar.gz
Den kan jeg ikke umiddelbart få downloaded.
Prøv (jeg syntes det er mere overskueligt og det ser ud til at virke inden
for de begrænsninger din kode har):
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
const char* FILE_CONTENT =
"{ \"Link0\"\n"
" Position (0, 0, 0)\n"
" RPY (0, 0, 0)\n"
" ReferenceFrame \"\"\n"
" Attributes\n"
" Link\n"
"}\n"
"\n"
"{ \"Link1\"\n"
" Position (0, 0, 0.0)\n"
" RPY (0, 0, 0)\n"
" ReferenceFrame \"Link0\"\n"
" Attributes\n"
" Link\n"
" JointName \"axis0\"\n"
" ActiveJoint\n"
" Revolute\n"
" JointPosLimit -185 185\n"
" JointVelLimit 156\n"
" JointAccLimit 312\n"
"}\n";
void parse_property(const std::string& s)
{
string _name = "";
string _value = "";
string::size_type i = 0;
while (s.find('\t', i) == i || s.find(' ', i) == i) {i++;}
string::size_type val = s.find(' ', i);
if (val != string::npos) {
_name = s.substr(i, val-i);
_value = s.substr(val+1); // The rest of the line
}
else {
_name = s.substr(i); // The rest of the line
}
std::cout << "Name: ." << _name << "." << std::endl;
std::cout << "Value: ." << _value << "." << std::endl << std::endl;
}
void parse_device(std::istream& ifs)
{
string line;
while(getline(ifs, line)) {
if (line.find("{") != string::npos) {
// Begin read object
bool attrib = false;
while(getline(ifs, line)) {
if (line.find("}") != string::npos)
break;
if (line.find("Attributes") != string::npos) {
attrib = true;
continue;
}
if (attrib != true) {
// Property found
cout << "Property: ";
parse_property(line);
}
else {
// Attribute found
cout << "Attribute: ";
parse_property(line);
}
}
}
}
}
int main()
{
stringstream is(FILE_CONTENT);
parse_device(is);
}
Venlig hilsen
Mogens Hansen