2016-03-29 15 views
0

Ich schreibe einen Übersetzer aus meiner Programmiersprache, die ein einfaches C++ ist, und ich habe Fragen zum Parsen einer Zeile. In C++ definieren wir einen Gegenstand wie:C++, eine Zeichenkette mit Leerzeichen als eine Einheit analysieren

class Item{ 
    string Item; 
    string Item2; 
}; 

ich das gleiche Objekt in meiner Sprache erstellen möchte das Schlüsselwort add:

add "Item Item", "Item2 Item2"; 

Wie Sie sehen, das string Variable "Item *" may Sei eine Linie mit Leerzeichen.

Aber ich muss es analysieren und einen Befehlsstapel von Arrays machen. In dem Beispiel möchte ich die Zeile analysieren, um ein Array von 3 wie folgt zu erstellen: [add, Item Item, Item2 Item2]. Ich muss also den Abstand zwischen "Item Item" speichern und ihn beim Parsen als eine String-Zeile zählen, aber immer noch Leerzeichen als Trennzeichen zwischen add und dem ersten "Item *" verwenden. Wie mache ich das?

+1

Eröffnen und Schließen von Anführungszeichen zählen. Wenn ein Zitat geöffnet ist, werden Leerzeichen an die letzte Zeichenfolge angehängt. Wenn ein Zitat nicht geöffnet ist, sind Leerzeichen Begrenzer und werden übersprungen. –

+1

Lesen Sie es als eine einzige Zeile (mit z. B. ['std :: getline]] (http://en.cppreference.com/w/cpp/string/basic_string/getline)) dann müssen Sie die Zeichenfolge in einigen anderen analysieren Weg. Das Entfernen des ersten durch Leerzeichen begrenzten Wortes und des abschließenden Semikolons sollte einfach sein. Dann spalte einfach den Rest auf das Komma und entferne alle Zeichenketten in Anführungszeichen. –

+1

Versuchen Boost.Spirit. Es hilft dir sicher. –

Antwort

0

Was Sie brauchen, ist ein Tokenizer. Sobald Sie die Eingabe in Token brechen können, können Sie die Reihenfolge (Syntax) analysieren. Hier ist ein Ausgangspunkt (demo):

#include <iostream> 
#include <map> 

using namespace std; 

enum token 
{ 
    end, 
    add, 
    str, 
    comma, 
    semicolon 
}; 

token t; 
string s; 


token gettoken(istream& is) 
{ 
    typedef map< string, token > kwmap_t; 

    static kwmap_t kw{ { "add", token::add } /*more keywords here*/ }; 

    ws(is); 

    char c; 

    if (! is.get(c)) 
    return token::end; 

    switch (c) 
    { 
    case ',': 
    return t = token::comma; 
    case ';': 
    return t = token::semicolon; 
    case '"': 
    s.clear(); 
    while (is.get(c) && c != '\n' && c != '"') 
     s += c; 
    if (c != '"') 
     throw "missing ending quote"; 
    return t = token::str; 
    default: 
    s.clear(); 
    s = c; 
    while (is.get(c) && isalpha(c)) 
     s += c; 
    is.unget(); 
    kwmap_t::const_iterator i = kw.find(s.c_str()); 
    if (i != kw.end()) 
     return t = i->second; 
    else 
     throw "unexpected"; 
    } 
} 

int main() 
{ 
    try 
    { 
    while (true) 
    { 
     switch (gettoken(cin)) 
     { 
     case token::end: return 0; 
     case token::add: cout << "add" << endl; break; 
     case token::str: cout << "str: " << s << endl; break; 
     case token::comma: cout << "comma" << endl; break; 
     case token::semicolon: cout << "semicolon" << endl; break; 
     } 
    } 
    } 
    catch (const char* x) 
    { 
    std::cout << x; 
    return -1; 
    } 
} 
1

Hier finden Sie die Linien in Befehle brechen wollen, könnte dies mit getline erreicht werden. Dann wirst du den Befehl mit quoted auseinander brechen wollen. (Beachten Sie, dass quoted 14 C++ nur. Also, wenn Sie nicht, dass diese Lösung nicht funktionieren.)

Ein Problem bei diesem Ansatz ist, dass Sie Leerzeichen verwenden und Kommas als Trennzeichen in Ihrer Sprache . Sie müssten also das Komma extrahieren. Doch das könnte sich mit einem einfachen erreicht werden if Sie verlassen mit so etwas wie:

vector<vector<string>> result; 
string command; 

while(getline(input, command, ';')) { 
    istringstream i(command); 
    string element; 
    result.resize(result.size() + 1); 

    while(i >> quoted(element)){ 
     if(element != ",") result.back().push_back(element); 
    } 
} 

Wo input ist ein istringstream mit den Befehlen.

Live Example

+0

Was passiert, wenn ein Element in Anführungszeichen ein Komma oder ein Semikolon enthält? – ZDF

+0

@ ZDF In C++ wären die Regeln Teil des zitierten Elements. Ich gehe davon aus, dass das auch hier gilt. –

+0

Ich bemerkte nicht "zitiert". Ist nicht ein Teil von C++ 14 "zitiert"? – ZDF

0

Wenn Sie von Boost-machen können Sie die split-Funktion verwenden. Ich habe nicht alle möglichen Fehler bemerkt, aber es gibt die richtige Antwort in einem ziemlich verständlichen Code. Was ich getan habe, ist den Code auf den Zitaten zu teilen. Bevor die Anführungszeichen gibt es die Aktion, zwischen dem ersten quotes ist das erste Element, dann kommt das Komma und zwischen dem zweiten Satz von Anführungszeichen kommt der zweite Punkt:

#include <iostream> 
#include <vector> 
#include <boost/algorithm/string/split.hpp> 
#include <boost/algorithm/string/classification.hpp> 

class Item_container 
{ 
    public: 
     Item_container(const std::string& s) 
     { 
      std::cout << "Input string: " << s << std::endl; 

      std::vector<std::string> string_vector; 
      boost::split(string_vector, s, boost::is_any_of("\"")); 

      // Below, error checking should be implemented 
      stack.push_back(string_vector[0]); 
      stack.push_back(string_vector[1]); 
      stack.push_back(string_vector[3]); 
     } 

     std::vector<std::string> get_stack() const { return stack; } 

    private: 
     std::vector<std::string> stack; 
}; 

int main() 
{ 
    Item_container item_container("add \"Item Item\", \"Item2 Item2\";"); 

    for (auto &s : item_container.get_stack()) 
     std::cout << s << std::endl; 

    return 0; 
} 
-1

Hier ist ein kleiner tokenizer. Dies ist nur ein Beispiel; Es hat keine Fehlerprüfung, daher ist es wahrscheinlich, dass es bei unerwarteten Eingaben abstürzt. Include-Dateien sind iostream, string, vector und ctype.h.

enum st 
{ 
    inSpace, 
    inToken, 
    inString 
}; 

static st newstate(const char* p) 
{ 
    if (isalpha(*p)) 
    { 
     return inToken; 
    } 
    if ('"' == *p) 
    { 
     return inString; 
    } 
    return inSpace; 
} 

int main(int argc, const char * argv[]) { 
    // insert code here... 
    std::cout << "Hello, World!\n"; 
    char line[128]; 
    std::cin.getline(line, sizeof(line)); 
    st state = inSpace; 
    char* p = line; 
    char* ptok = nullptr; // Will point to the beginning of a token 
    std::vector<std::string*> sym; 

    while(*p) 
    { 
     switch(state) 
     { 
      case inSpace: 
       while(isspace(*p) || (',' == *p)) 
       { 
        ++p; 
       } 
       state = newstate(p); 
       break; 

      case inString: 
       ptok = p; // Token includes opening quote 
       while('"' != *++p); 
       sym.push_back(new std::string(ptok, p + 1)); 
       state = newstate(++p); 
       break; 

      case inToken: 
       ptok = p; 
       while(isalpha(*++p)); 
       sym.push_back(new std::string(ptok, p)); 
       state = newstate(p); 
       break; 

      default: 
       std:: cout << "Error\n"; 
     } 
    } 

    for(int i = 0; sym.size() > i; ++i) 
    { 
     std::cout << "Symbol #" << i + 1 << " = " << *(sym[i]) << std::endl; 
    } 
    return 0; 
} 
+0

Ich bin neugierig, warum meine Antwort abgelehnt wurde. Ich habe es getestet; es läuft korrekt. Wie ist es eine gute Antwort? – Logicrat

Verwandte Themen