2017-05-03 2 views
1

Ich möchte eine Reihe von Zahlen in einen Vektor von Elementen analysieren. Die Zeichenfolge besteht aus Blöcken mit vier Zahlen, die durch () : / getrennt sind, und jeder Block ist durch eine ; getrennt.Parse Zeichenkette von Zahlen in Vektor von Strukturen

Die Zeichenfolge ist speziell in diesem Format: int(int):float/float;, siehe Codebeispiel unten. Ich denke, ich könnte einen regulären Ausdruck verwenden, aber da die Daten so strukturiert sind, bin ich mir sicher, dass es eine leichter zugängliche und einfachere Möglichkeit geben muss, eine solche Zeichenfolge zu parsen. Ich benutze Istringstream, aber es fühlt sich ein wenig plump an.

std::string line = "0(0):0/0;1(2):0.01/0.02;2(4):0.02/0.04;3(6):0.03/0.06;" 
struct Element { 
    int a; 
    int b; 
    int c; 
    int d; 
}; 

std::vector<Element> = parse(line); 


std::vector<Element> parse(std::string line) 
{ 
    std::vector<Element> elements; 
    std::istringstream iss(line); 
    while(iss) { 
    char dummy; 
    Element element; 

    iss >> element.a; 
    iss.read(&dummy,sizeof(dummy)); // (
    iss >> element.b; 
    iss.read(&dummy,sizeof(dummy)); //) 
    iss.read(&dummy,sizeof(dummy)); // : 
    iss >> element.c; 
    iss.read(&dummy,sizeof(dummy)); ///
    iss >> element.d; 
    iss.read(&dummy,sizeof(dummy)); // ; 

    if (!iss) {break;} 

    elements.push_back(element); 
    } 
    return elements; 
} 

Meine Fragen:

  1. Was wäre ein guter Weg, um zu analysieren? Sollte ich std::stringstream verwenden und Nummer nach Nummer einlesen und die dazwischen liegenden Zeichen "abhacken"? Wie im Codebeispiel gemacht?
  2. Dieser Code hat einen Fehler und versucht, einen zusätzlichen Satz von Werten zu lesen, da while(iss) immer noch wahr ist, nachdem das letzte Zeichen eingelesen wurde. Wie wird diese Schleife ohne Tests nach jeder iss>> beendet? Oder allgemeiner gesagt, wie man Extraktionen von istringstream überbrückt?
+2

'1 * sizeof (char)' garantiert 1. In dem Ausdruck '1 * sizeof (char)' sein, ist der Wert '1' ein Zauber Zahl, die sich auf die Anzahl der Zeichen in "dummy" und "sizeof (char)" zu beziehen scheint, bezieht sich auf den Typ von "dummy". Wenn Sie die Konstante 1 für Ausdruckskraft oder Wartbarkeit nicht liefern, warum nicht einfach 'sizeof (dummy)' verwenden? Die aktuelle Form ist nicht besser als einfach 1 direkt zu liefern. –

+0

Ist es wichtig, das Format des Strings zu validieren, oder ist es bekannt, dass es korrekt ist? Wenn es bekannt ist, dass es gültig ist, können Sie einfach die gesamte Zeichenfolge in einer beliebigen Folge von nicht-numerischen Zeichen in Tokens umwandeln. Es bleibt eine Liste von Zahlen übrig, aus denen man 'Element' leicht konstruieren kann. [Wie mache ich eine Zeichenkette in C++?] (Http://stackoverflow.com/questions/53849/how-do-i-tokenize-a-string-in-c) –

+0

Haben Sie jemals über einen Serializer nachgedacht? Das Lesen von Datenstrukturen ist ein häufiges Problem mit typischerweise einer gemeinsamen Lösung. – Klaus

Antwort

1

Ihre Daten gut strukturiert sind, können Sie überlasten leicht operator>> die Klassenmitglieder aus einem std::ifstream zu extrahieren und dann halten sie von einem istringstream oder einem Datei-Stream zu lesen.

Hier ist eine mögliche Implementierung ist:

#include <iostream> 
#include <vector> 
#include <string> 
#include <sstream> 
#include <fstream> 
#include <iterator> 
#include <stdexcept> 

class Element 
{ 
public: 
    Element() {} 
    Element(int aa, int bb, float cc, float dd) : a{aa}, b{bb}, c{cc}, d{dd} {} 

    friend std::istream &operator>> (std::istream &in, Element &e); 
    friend std::ostream &operator<< (std::ostream &out, Element const &e); 

private: 
    int a; 
    int b; 
    float c; 
    float d; 
}; 

std::istream &operator>> (std::istream &in, Element &e) 
{ 
    char delimiter; 
    if (not (in >> e.a >> delimiter and delimiter == '(' and 
       in >> e.b >> delimiter and delimiter == ')' and 
       in >> delimiter   and delimiter == ':' and 
       in >> e.c >> delimiter and delimiter == '/' and 
       in >> e.d >> delimiter and delimiter == ';') 
     and not in.eof()) 
    { 
     in.setstate(std::ios_base::failbit); 
    } 
    return in; 

} 

std::ostream &operator<< (std::ostream &out, Element const &e) 
{ 
    return out << e.a << '(' << e.b << "):" << e.c << '/' << e.d << ';'; 
} 

std::vector<Element> read_Elements_from(std::istream &in) 
{ 
    std::vector<Element> tmp (
     std::istream_iterator<Element>{in}, 
     std::istream_iterator<Element>{} 
    ); 
    if (not in.eof()) 
     throw std::runtime_error("Wrong format"); 

    return tmp; 
} 

int main() 
{ 
    try 
    { 
    using std::cout; 
    std::istringstream iss { 
     "0(0):0/0;1(2):0.01/0.2;2(4):0.02/0.04;3(6):0.03/0.06;" 
    }; 

    auto els_s = read_Elements_from(iss); 

    cout << "Elements read from the string:\n"; 
    for (auto const &i : els_s) 
    { 
     cout << i << '\n'; 
    } 

    // assuming a file which lines are similar to the string provided 
    std::ifstream input_file {"input_data.txt"}; 
    if (not input_file) 
     throw std::runtime_error("Can't open input file"); 

    auto els_f = read_Elements_from(input_file); 

    cout << "\nElements read from the file:\n"; 
    for (auto const &i : els_f) 
    { 
     cout << i << '\n'; 
    } 
    } 
    catch (std::exception const &e) 
    { 
     std::cerr << "\nAn unexpected problem cause this application to end:\n\n" 
       << e.what() << ".\n\n"; 
     return EXIT_FAILURE; 
    } 
} 
+0

Ziemlich cool, vielen Dank für das Beispiel. Das funktioniert gut und sieht ziemlich komplett aus! –

Verwandte Themen