2009-05-06 8 views
1

Ich versuche, einen Konstruktor für eine Grafikklasse zu erstellen, die eine Zeichenfolge als -Parameter akzeptiert und verwendet, um das Diagramm zu erstellen.Eine Zeichenfolge in C++ analysieren

Die Zeichenfolge ist wie folgt formatiert: |vertex list|Edges list| z. |1,2,3,4,15|(1->2),(3->2),(4->15)|

Die Idee ist, dass der Konstruktor die Werte aus der Zeichenfolge nehmen und dann wissen die folgenden Aktionen (Einfügen der Eckpunkte in der Eckenliste und dann Einfügen der Kanten in die Kanten Liste) auszuführen:

addVertex(1) 
addVertex(2) 
addVertex(3) 
addVertex(4) 
addVertex(15) 
addEdge(1,2) 
addEdge(3,2) 
addEdge(4,15) 

Ich hätte nur ein paar "for" -Schleifen gemacht, um die Zeichenkette zu scannen, aber ich weiß nicht , was man über doppelte (oder mehr) Ziffern machen soll. Ich fange an, alle Arten von ernsthaft kompliziert für Schleifen vorstellen und ich frage mich, ob jemand hier mit mir mehr intelligente Möglichkeiten teilen könnte, diese Daten zu extrahieren und zu verwenden.

+0

Ist das Eingabeformat von Ihnen erfunden? Wenn ja, ändern Sie es in etwas einfacheres. –

+0

Ich muss zustimmen. Diese Syntax ist unnötig komplex. Ein einfacheres Format wäre etwa (1 2 3 4 15) ([1 2] [3 2] [4 15]) – rlbond

+0

Dieses Format ist in der Hausaufgabe angegeben. Ich wünschte, ich könnte es ändern ... – Meir

Antwort

2

Sie können eine stringstream verwenden und den Stream-Extraktionsoperator verwenden, um Ihre Ganzzahlen zu erhalten.

string s("12 34"); 
istringstream ss(s); 
int x, y; 
ss >> x >> y; 

Da es sich um Hausaufgaben, ich fordere Sie auf, die Möglichkeiten zu erforschen und die komplette Code für sich selbst herausfinden.

+0

stringstream wird die Dinge hier nicht viel einfacher machen, da Sie keine begrenzenden Whitespaces garantieren können. niemand sagte etwas über Integer; Sie waren nur ein Beispiel. – wilhelmtell

+0

Stringstream scheint eine gute Lösung zu sein, aber wie gehe ich mit Kommas um? Meine Liste ist nicht durch Leerzeichen getrennt, sondern durch Kommas. z. B. (12,34) – Meir

+0

Sie würden 'ss >> x >> komma >> y;' wo 'komma' ist ein char. – dirkgently

2

Ich habe es noch nie zuvor benutzt, aber es gibt eine Boost tokenizer Klasse. Du könntest das Ding leicht in Komponenten für dich zerlegen, ohne das ganze Looping.

+0

Boost Tokenizer ist eine gute Option für diesen Fall. +1 –

+0

Boost Tokenizer ist wunderbar zum Aufbrechen von Strings. Selbst wenn Sie es nicht in dieser Situation verwenden, ist es gut, für die Zukunft zu wissen. –

0

Verwenden Sie eine stringstream. Beachten Sie das Beispiel auf dieser Seite zum Einlesen von Zahlen mit istringstream.

6

Sie scheinen überwältigt zu sein, die ganze Sache zu betrachten. Brich es in Stücke ... Aufgaben. Was Sie versuchen zu tun, scheint hier separate Funktionen zu sein.

  1. Tokenizing
  2. Parsing Vertices
  3. Parsing Kanten
  4. Ausführung auf Vertices
  5. Ausführung auf Kanten

Das 5-Funktionen mehr oder weniger ist.

Sie möchten auf der Grundlage der Pipe (|) Tokenize, also nehmen Sie eine Teilzeichenfolge basierend auf der Pipe und übergeben Sie jede Seite an den entsprechenden Parser, analysieren Sie die Kommas und so weiter.

Ich werde es nicht für Sie tun, aber hoffentlich kann ich Sie in die richtige Richtung denken lassen. Beim Programmieren geht es weniger um eine bestimmte Sprache als vielmehr darum, die Denkweise zu ändern.

+3

+1 für die Hausarbeit-freundliche Beratung. –

1

Ohne Ihre Hausaufgaben für Sie zu machen, wird dies Ihnen einen guten Vorsprung geben. Ich habe Ihnen den grundlegenden Arbeitsablauf zum Parsen der Eckenliste gegeben. Sie sollten in der Lage sein, die Kantenliste selbst zu erstellen. Ich überlasse Ihnen auch die Fehlerüberprüfung, zum Beispiel in parseVertex() möchten Sie möglicherweise einen Fehler geben, wenn Sie auf ungültige Zeichen stoßen.

void skipWhiteSpace(const char*& first , const char* last) { 
    // do whatever need to be done to skip white space 
} 

// parse integer only, no error checking is performed 
bool parseVertex(const char*& first , const char* last) { 
    skipWhiteSpace(first, last); 
    const char* numBegin = first; 
    for (; first != last && ::isdigit(static_cast<unsigned char>(*first)); 
     ++first) {} 
    if (numBegin != first) { 
     std::cout << "addVertex(" << std::string(numBegin, first) << ")" << std::endl; 
     return true; 
    } 

    return false; 
} 

bool parseComma(const char*& first , const char* last) { 
    skipWhiteSpace(first, last); 
    if (first != last && ',' == *first) { 
     ++first; 
     return true; 
    } 

    return false; 
} 

// VL := V (, VL) 
// a vertex list (VL) is a vertex (V) followed by a comma than another vertex list 
bool parseVertexList(const char*& first, const char* last) { 
    if (parseVertex(first, last)) { 
     parseComma(first, last) && parseVertexList(first, last); 
     return true; 
    } 

    return false; 
} 
} 

void test() { 
    const char* str = "1,2,3,4,15"; 
    parseVertexList(str, str + sizeof("1,2,3,4,15")); 
} 
+0

Wirklich zu schätzen den Code. Der Strom scheint etwas eleganter, aber deine Antwort ist großartig. – Meir

0

Ich würde sicherlich dieses Problem als Vorwand verwenden, um mit boost spirit zu spielen! Ein wenig Grammatik für diese kleine Sprache zu schreiben sollte eine Menge Spaß machen.

1

Parsing dieser Art von Sache ist ziemlich einfach (obwohl mühsam) mit rekursiven Abstiegstechniken. Die Idee besteht darin, die zu analysierende Sprache in logische Einheiten zu trennen und dann eine Funktion zu schreiben, um jede dieser Einheiten zu analysieren.

Wenn wir im Beispiel "| 1,2,3,4,15 | (1-> 2), (3-> 2), (4-> 15) | |" dass die gesamte Zeichenfolge ein „Polygon“ ist, würden wir parsePolygon() schreiben, die wie folgt aussehen würde:

void parsePolygon (Buffer& b) 
{ 
    parseVertices (b); 
    parseEdges (b); 
} 

Nehmen wir an, Buffer eine Klasse, die durch die Zeichenfolge ausgeführt wird. Sie benötigen zwei grundlegende Operationen: Suchen Sie das nächste Zeichen, ohne es zu verbrauchen, und konsumieren Sie das nächste Zeichen.

parseVertices könnte wie folgt aussehen:

void parseVertices (Buffer& b) 
{ 
    if (b.peek() != '|') { /* error */ } 
    b.consume(); // burn the '|' 
    parseVertexList (b); 
    if (b.peek() != '|') { /* error */ } 
    b.consume(); // burn the '|' 
} 

Sie wollen würde Fehler viel besser, offensichtlich zu handhaben. Wenn der Stream einen Fehler feststellt, muss der Fehlercode den Callstack übergeben oder eine Ausnahme auslösen.

Zwei weitere Beispiele ... parseVertexList und parseNumber könnte wie folgt aussehen:

void parseVertexList (Buffer& b) 
{ 
    addVertex (parseNumber (b)); 
    while (b.peek() == ',') 
    { 
    b.consume(); // eat the comma 
    addVertex (parseNumber (b)); 
    } 
} 

int parseNumber (Buffer& b) 
{ 
    char accum[80] = { '0' }; // sensible default in case of failure 
    int accumPos = 0; 
    while (isDigit (b.peek()) 
    { 
    accum[accumPos++] = b.consume(); 
    } 
    return atoi(accum); 
} 

Das alles sehr schnell und schmutzig ist, aber hoffentlich es gibt Ihnen eine Vorstellung davon, wie die Technik funktioniert. Sie können Ihre Handhabung mit Ihrem Parsing mischen, wie oben gezeigt, wo die Funktion parseVertexList tatsächlich den Aufruf von addVertex für Sie aufruft.

Ich denke, das ist wirklich eine der einfachsten Methoden der manuellen Analyse. Im Idealfall könnten wir generierte Parser wie boost spirit oder pyparsing oder lex/yacc verwenden, aber das Leben ist nicht immer so gut, besonders für Hausaufgaben.

Auch ich denke, es ist erwähnenswert, dass die obige Technik eine Menge Overkill für einige Parsing-Situationen sein kann.

+0

Danke für Ihre Hilfe. Ich kann sehen, wie deine Lösung großartig wäre, wenn ich etwas komplizierter analysiere. Ich wünsche ihnen einen wunderbaren Tag! – Meir

+0

Ich habe das nur benutzt, um bei einem anderen Projekt zu helfen. Vielen Dank! – Meir