2017-08-01 5 views
4

Ich habe ein Problem gefunden, um msg aus einer Datei mit C++ zu lesen. In der Regel erstellen die Benutzer einen Dateistream und verwenden dann die Funktion getline(), um die Nachricht abzurufen. getline() Funktion kann einen zusätzlichen Parameter als Trennzeichen akzeptieren, so dass es jede "Zeile" getrennt durch das neue Trennzeichen, aber nicht standardmäßig '\ n' zurückgibt. Dieses Trennzeichen muss jedoch ein Zeichen sein. In meinem Anwendungsfall ist es möglich, dass das Trennzeichen in der msg etwas anderes ist als "| - |", also versuche ich eine Lösung zu finden, die eine Zeichenkette als Trennzeichen anstelle eines Zeichens akzeptiert.Lesen von FileStream mit beliebigem Trennzeichen

Ich habe StackOverFlow ein wenig gesucht und einige interessante Beiträge gefunden. Parse (split) a string in C++ using string delimiter (standard C++) Dieser gibt eine Lösung, string::find() und string::substr() zu verwenden, um mit beliebigem Trennzeichen zu analysieren. Alle Lösungen, die dort angenommen werden, sind jedoch eine Zeichenfolge anstelle eines Streams. In meinem Fall sind die Daten des Datenstroms zu groß, um in den Speicher zu passen, so dass sie in msg nach msg (oder einem Großteil von msg at Einmal).

Eigentlich lesen Sie durch die GCC-Implementierung von std::getline() Funktion, es scheint, es ist viel einfacher zu handhaben, die Fallbegrenzer ist eine einzige char. Da Sie jedes Mal, wenn Sie einen Teil der Zeichen laden, immer das Trennzeichen suchen und trennen können. Während es anders ist, wenn das Trennzeichen mehr als ein Zeichen ist, kann das Trennzeichen selbst zwischen zwei verschiedenen Abschnitten liegen und viele andere Fälle verursachen.

Nicht sicher, ob jemand andere diese Art von Anforderung vor hat und wie Sie es elegant behandelt. Es scheint, es wäre schön, eine Standardfunktion wie istream& getNext (istream&& is, string& str, string delim) zu haben? Dies scheint mir ein allgemeiner Anwendungsfall zu sein. Warum nicht diese in Standard lib, so dass die Leute nicht mehr ihre eigene Version separat implementieren?

Vielen Dank

+0

getline mit einer Zeichenfolge würde Lookahead erfordern, so dass es im Allgemeinen langsamer sein könnte. Nur Spekulation. Wir müssen unsere eigene benutzerdefinierte getline implementieren. – AndyG

+0

Gibt es eine elegante Implementierung? Wie Sie bereits erwähnt haben, macht Lookahead den Code kompliziert.vielleicht eine FSM eine elegante Lösung? –

+0

Der Lookahead wäre eine einfache FSM haha, einfach nicht so kompliziert wie ein regulärer Ausdruck. Der Kern des Programms besteht darin, Zeichen einzulesen, bis Sie den "delimiter" -Zustand erreichen, und diese Zeichen dann zu einer Zeichenkette zu analysieren. Wenn Sie nur an einer Lösung interessiert sind, die "funktioniert", verwenden Sie einen "std :: vector" und spielen Sie herum. Eine "optimale" Lösung wäre ein wenig schwieriger. Wenn niemand etwas beantwortet, schreibe ich etwas auf. – AndyG

Antwort

0

, wenn Sie mit dem Lesen Byte für Byte ok sind, könnten Sie eine Zustandsübergangstabelle Implementierung eines endlichen Automaten bauen Sie Ihre Stop-Bedingung

std::string delimeter="someString"; 
//initialize table with a row per target string character, a column per possible char and all zeros 
std::vector<vector<int> > table(delimeter.size(),std::vector<int>(256,0)); 
int endState=delimeter.size(); 
//set the entry for the state looking for the next letter and finding that character to the next state 
for(unsigned int i=0;i<delimeter.size();i++){ 
    table[i][(int)delimeter[i]]=i+1; 
} 

jetzt in Ihnen zu erkennen, kann es wie diese verwenden

int currentState=0; 
int read=0; 
bool done=false; 
while(!done&&(read=<istream>.read())>=0){ 
    if(read>=256){ 
     currentState=0; 
    }else{ 
     currentState=table[currentState][read]; 
    } 
    if(currentState==endState){ 
     done=true; 
    } 
    //do your streamy stuff 
} 

gewährt das funktioniert nur, wenn der Begrenzer in erweiterten ASCII ist, aber es wird für einige etwas wie Ihr Beispiel gut funktionieren.

0

Die STL unterstützt einfach nicht nativ, wonach Sie fragen. Sie müssen Ihre eigene Funktion schreiben (oder eine Drittanbieterfunktion finden), die das tut, was Sie brauchen.

Zum Beispiel können Sie std::getline() verwenden, um bis zum ersten Zeichen Ihres Trennzeichens zu lesen, und dann std::istream::get() verwenden, um nachfolgende Zeichen zu lesen und sie mit dem Rest Ihres Trennzeichens zu vergleichen. Zum Beispiel:

std::istream& my_getline(std::istream &input, std::string &str, const std::string &delim) 
{ 
    if (delim.empty()) 
     throw std::invalid_argument("delim cannot be empty!"); 

    if (delim.size() == 1) 
     return std::getline(input, str, delim[0]); 

    str.clear(); 

    std::string temp; 
    char ch; 
    bool found = false; 

    do 
    { 
     if (!std::getline(input, temp, delim[0])) 
      break; 

     str += temp; 

     found = true; 

     for (int i = 1; i < delim.size(); ++i) 
     { 
      if (!input.get(ch)) 
      { 
       if (input.eof()) 
        input.clear(std::ios_base::eofbit); 

       str.append(delim.c_str(), i); 
       return input; 
      } 

      if (delim[i] != ch) 
      { 
       str.append(delim.c_str(), i); 
       str += ch; 
       found = false; 
       break; 
      } 
     } 
    } 
    while (!found); 

    return input; 
} 
0

Es scheint, ist es am einfachsten, so etwas wie getline() zu erstellen: Lesen zum letzten Charakter des Separators. Überprüfen Sie dann, ob die Zeichenfolge für das Trennzeichen lang genug ist, und falls ja, ob sie mit dem Trennzeichen endet. Wenn nicht, lesen Sie weiter:

std::string getline(std::istream& in, std::string& value, std::string const& separator) { 
    std::istreambuf_iterator<char> it(in), end; 
    if (separator.empty()) { // empty separator -> return the entire stream 
     return std::string(it, end); 
    } 
    std::string rc; 
    char  last(separator.back()); 
    for (; it != end; ++it) { 
     rc.push_back(*it); 
     if (rc.back() == last 
      && separator.size() <= rc.size() 
      && rc.substr(rc.size() - separator.size()) == separator) { 
      return rc.resize(rc.size() - separator.size()); 
     } 
    } 
    return rc; // no separator was found 
} 
Verwandte Themen