2010-02-26 10 views
12

mam dane w następującym formacie:Reading sformatowane dane z C++ 's operator strumień >> gdy dane zawiera spacje

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

liczba może wynosić od 1 do 999, a łańcuch o długości co najwyżej 255 znaków . Jestem nowicjuszem w C++ i wydaje mi się, że kilka źródeł zaleca wyodrębnianie sformatowanych danych za pomocą operatora strumienia o numerze >>, ale gdy chcę wyodrębnić ciąg znaków, zatrzymuje się on przy pierwszym znaku odstępu. Czy istnieje sposób skonfigurowania strumienia tak, aby przestał analizować ciąg tylko na linii nowej lub na końcu pliku? Widziałem, że istnieje metoda getline, aby wyodrębnić całą linię, ale nadal muszę ją podzielić ręcznie [z find_first_of], prawda?

Czy istnieje łatwy sposób analizowania danych w tym formacie przy użyciu tylko STL?

+2

Strumienie w C++ są jedną z rzeczy, których nienawidzę o C++. – AraK

+0

Ponieważ jestem nowy w C++, miałem nadzieję, że strumienie były jedną z tych rzeczy, które ostatecznie doprowadziły do ​​epifanii "oooooh that's clever", ale po twoim komentarzu zaczynam myśleć, że to się nigdy nie wydarzy. :( – dreamlax

Odpowiedz

10

Możesz przeczytać numer przed użyciem std::getline, który czyta ze strumienia i zapisuje do obiektu std::string. Coś w tym stylu:

int num; 
string str; 

while(cin>>num){ 
    getline(cin,str); 

} 
+0

To wygląda na czyste, zakładam, że będzie bezpiecznie zastąpić 'cin' z' istream', które dostaję? – dreamlax

+0

Jeśli czytasz z pliku, możesz zastąpić cin z ** ** ** obiekt typu ifstream – codaddict

+0

Po prostu dostaję strumień, a mój kod ma przetwarzać dane, manipulować nimi i zapisywać je w innym strumieniu. Nie tworzę żadnego strumienia. Nie można go wywołać, jeśli 'istream' lub' ostream' jest nieprawidłowy, ale w tym samym Czas nie wydaje mi się, żeby to była moja troska. Syf na wejściu, syf na wyjściu :) . . . lub może śmieci w segfault na zewnątrz. – 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); 
 

Jego C i będzie działać również w C++. scanf zapewnia większą kontrolę, ale bez zarządzania błędami. Więc używaj z rozwagą :).

+0

Wygląda na to, że flaga 'm' nie jest wystandaryzowana, więc nie mogę jej użyć, ale czy nie będzie to nadal czytać tylko do pierwszej spacji zamiast do końca linii? – dreamlax

+0

wciąż czyta tylko pierwsze słowo linii, a nie całą linię, i masz błąd w kodzie: podajesz 'i', ale' scanf' potrzebuje * wskaźnika * do 'i' (' i i'). – dreamlax

+0

@dreamlax dzięki za wycelowanie Poprawione –

2

Wystarczy odczytać wiersz danych po linii (całej linii) za pomocą getline i przeanalizować go.
do analizowania użycia find_first_of()

+0

czy mógłbyś podać przykładowy kod? – Sergei

9

Masz już powiedziano o std::getline, ale nie wspomina jeden szczegół, który prawdopodobnie okaże się przydatne: podczas rozmowy getline można również przekazać parametr informujący go jaką postać traktować jako koniec wejścia. Aby odczytać numer, można użyć:

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

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

To będzie umieścić dane aż do „:” w number, wyrzucić „:” i przeczytać resztę linii do name.

Jeśli chcesz odczytać dane z >>, możesz to zrobić, ale jest to trochę trudniejsze i zagłębia się w obszar standardowej biblioteki, której większość ludzi nigdy nie dotyka. Strumień jest powiązany z locale używanym do formatowania liczb i (co ważniejsze) określania, co stanowi "białą przestrzeń". Możesz zdefiniować własne ustawienia narodowe, aby zdefiniować ":" jako białą przestrzeń i spację ("") jako białą przestrzeń. Poinformuj strumień, aby używał tego ustawienia narodowego, i pozwoli Ci bezpośrednio odczytać dane.

#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]; 
    } 
}; 

Teraz go używać, my „nasycić” strumień z lokalizacji:

#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; 
} 

Teraz wiesz dlaczego że część biblioteki jest tak zaniedbane. Teoretycznie, zdobycie standardowej biblioteki, która wykona twoją pracę dla ciebie, jest wspaniałe - ale w rzeczywistości, w większości przypadków łatwiej jest wykonać taką pracę na własną rękę.

+0

Żyj, aby się uczyć! Dzięki, Jerry! –

13

C++ String Toolkit Library (StrTk) ma następujące rozwiązanie problemu:

#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; 
} 

Więcej przykładów można znaleźć Here

Powiązane problemy