2010-02-26 6 views
12

I-Daten in folgendem Format haben:Lesen formatierte Daten mit C++ 's Stream Operator >>, wenn Daten hat Räume

 
4:How do you do? 
10:Happy birthday 
1:Purple monkey dishwasher 
200:The Ancestral Territorial Imperatives of the Trumpeter Swan 

Die Nummer kann zwischen 1 und 999 überall sein, und die Zeichenfolge maximal 255 Zeichen lang sein . Ich bin neu in C++ und es scheint, dass einige Quellen empfehlen, formatierte Daten mit dem Operator >> eines Streams zu extrahieren, aber wenn ich einen String extrahieren möchte, stoppt er beim ersten Leerzeichen. Gibt es eine Möglichkeit, einen Stream so zu konfigurieren, dass er die Analyse eines Strings nur an einem Zeilenende oder Dateiende stoppt? Ich habe gesehen, dass es eine getline Methode gibt, um eine ganze Zeile zu extrahieren, aber dann muss ich es noch manuell aufteilen [mit find_first_of], nicht wahr?

Gibt es eine einfache Möglichkeit, Daten in diesem Format nur mit STL zu analysieren?

+2

Streams in C gefunden werden kann ++ ist eines der Dinge, die ich über C++ hassen. – AraK

+0

Da ich neu in C++ bin, hatte ich gehofft, dass Streams eines dieser Dinge sind, die schließlich zu einer Offenbarung von "oooooh das ist schlau" führen, aber nach deinem Kommentar denke ich, dass das niemals passieren wird. :( – dreamlax

Antwort

10

Sie können die Nummer lesen, bevor Sie std::getline verwenden, die aus einem Stream liest und in einem std::string Objekt speichert. So etwas wie das:

+0

Das sieht sauber aus, ich gehe davon aus, dass es sicher ist, 'cin' durch einen' istream' zu ersetzen, den ich bekomme – dreamlax

+0

Wenn Sie von einer Datei lesen, können Sie cin durch ersetzen ein ** gültiges ** ifstream-Objekt – codaddict

+0

Ich habe gerade einen Stream erhalten und ich erwarte, dass mein Code die Daten analysiert, manipuliert und in einen anderen Stream schreibt Ich erstelle keinen der beiden Streams Ich nehme an, mein Filter würde nicht funktionieren Es wird nicht geltend gemacht, wenn der "istream" oder "ostream" ungültig ist, aber gleichzeitig Ich denke nicht, dass es mich betrifft. Müll im Müll raus :). . . oder vielleicht Müll in segfault out. – dreamlax

2
int i; 
char *string = (char*)malloc(256*sizeof(char)); //since max is 255 chars, and +1 for '\0' 
scanf("%d:%[^\n]s",&i, string); //use %255[^\n]s for accepting 255 chars max irrespective of input size 
printf("%s\n", string); 
 

Sein C und wird auch in C++ funktionieren. scanf bietet mehr Kontrolle, aber keine Fehlerverwaltung. Also mit Vorsicht verwenden :).

+0

Es sieht aus wie das 'm' Flag ist nicht standardisiert, so kann ich es nicht verwenden. Aber, wieder, wird dies nicht nur lesen, um die ersten Leerzeichen statt bis zum Ende der Zeile? – dreamlax

+0

Es liest immer noch nur das erste Wort der Zeile, nicht die ganze Zeile, und Sie haben einen Fehler in Ihrem Code: Sie stellen 'i' bereit, aber' scanf' benötigt einen * Zeiger * auf 'i' (' & i'). – dreamlax

+0

@dreamlax danke für das Aufzeigen, korrigiert –

2

Lesen Sie einfach die Daten Zeile für Zeile (ganze Zeile) mit getline und parse es. wenn Sie getline nennen, können Sie auch einen Parameter übergeben sagen es:
Zur Verwendung find_first_of()

+0

könnten Sie bitte einen Beispielcode angeben? – Sergei

9

Du hast bereits gesagt worden, über std::getline, aber sie nicht erwähnt, ein Detail, dass Sie werden wahrscheinlich feststellen, nützlich zu analysieren welcher Charakter als Ende der Eingabe zu behandeln ist. Um Ihre Nummer zu lesen, können Sie verwenden:

std::string number; 
std::string name; 

std::getline(infile, number, ':'); 
std::getline(infile, name); 

Dadurch werden die Daten stellen bis zu den ‚:‘ in number, verwerfen die ‚:‘, und der Rest der Zeile in name lesen.

Wenn Sie >> verwenden, um die Daten zu lesen, können Sie das auch tun, aber es ist ein bisschen schwieriger und taucht in einen Bereich der Standardbibliothek ein, den die meisten Leute nie berühren. Ein Stream hat eine zugeordnete locale, die für Dinge wie die Formatierung von Zahlen verwendet wird und (wichtig ist) zu bestimmen, was "White Space" ausmacht. Sie können Ihr eigenes Gebietsschema definieren, um ":" als Leerraum und den Leerraum ("") als nicht Leerraum zu definieren. Weisen Sie den Stream an, dieses Gebietsschema zu verwenden, damit Sie Ihre Daten direkt lesen können.

#include <locale> 
#include <vector> 

struct colonsep: std::ctype<char> { 
    colonsep(): std::ctype<char>(get_table()) {} 

    static std::ctype_base::mask const* get_table() { 
     static std::vector<std::ctype_base::mask> 
      rc(std::ctype<char>::table_size,std::ctype_base::mask()); 

     rc[':'] = std::ctype_base::space; 
     rc['\n'] = std::ctype_base::space; 
     return &rc[0]; 
    } 
}; 

Nun, es zu benutzen, wir den Strom mit einem locale „durchtränken“:

#include <fstream> 
#include <iterator> 
#include <algorithm> 
#include <iostream> 

typedef std::pair<int, std::string> data; 

namespace std { 
    std::istream &operator>>(std::istream &is, data &d) { 
     return is >> d.first >> d.second; 
    } 
    std::ostream &operator<<(std::ostream &os, data const &d) { 
     return os << d.first << ":" << d.second; 
    } 
} 

int main() { 
    std::ifstream infile("testfile.txt"); 
    infile.imbue(std::locale(std::locale(), new colonsep)); 

    std::vector<data> d; 

    std::copy(std::istream_iterator<data>(infile), 
       std::istream_iterator<data>(), 
       std::back_inserter(d)); 

    // just for fun, sort the data to show we can manipulate it: 
    std::sort(d.begin(), d.end()); 

    std::copy(d.begin(), d.end(), std::ostream_iterator<data>(std::cout, "\n")); 
    return 0; 
} 

Jetzt wissen Sie, warum , die Teil der Bibliothek wird so vernachlässigt. Theoretisch ist es großartig, die Standard-Bibliothek zu bekommen, um Ihre Arbeit für Sie zu erledigen - aber in der Tat ist es meistens einfacher, diese Art von Arbeit selbst zu erledigen.

+0

Lebe zu lernen! Danke, Jerry! –

13

Die C++ String Toolkit Library (StrTk) hat die folgende Lösung für Ihr Problem:

#include <string> 
#include <deque> 
#include "strtk.hpp" 

int main() 
{ 
    struct line_type 
    { 
     unsigned int id; 
     std::string str; 
    }; 

    std::deque<line_type> line_list; 

    const std::string file_name = "data.txt"; 

    strtk::for_each_line(file_name, 
         [&line_list](const std::string& line) 
         { 
          line_type temp_line; 
          const bool result = strtk::parse(line, 
                  ":", 
                  temp_line.id, 
                  temp_line.str); 
          if (!result) return; 
          line_list.push_back(temp_line); 
         }); 

    return 0; 
} 

Weitere Beispiele Here

Verwandte Themen