Estrai più parole in una variabile stringa

std::stringstream convertor("Tom Scott 25"); std::string name; int age; convertor >> name >> age; if(convertor.fail()) { // it fails of course } 

Mi piacerebbe estrarre due o più parole in una variabile stringa. Finora ho letto, sembra che non sia ansible. Se sì, in quale altro modo farlo? Vorrei il name per ottenere tutti i personaggi prima del numero (l’età).

Mi sentirei a mio agio con sscanf, ma ovviamente non posso.

Quello di cui ho bisogno è la capacità di estrarre tutte le parole prima age ad esempio.

La maggior parte delle soluzioni pubblicate finora non soddisfa le specifiche: tutti i dati fino all’età vengono considerati come il nome. Ad esempio, fallirebbero con un nome come “Richard Van De Rothstyne”.

Come notato dall’OP, con scanf puoi fare qualcosa di simile: scanf("%[^0-9] %d", name, &age); e lo leggerebbe bene. Supponendo che questo sia un input orientato alla linea, tenderei comunque a farlo:

 std::string temp; std::getline(infile, temp); // technically "[^0-9]" isn't required to work right... sscanf(temp.c_str(), "%[^0123456789] %d", name, &age); 

Sfortunatamente, gli iostream non forniscono un analogo diretto a una conversione di tipo scanset come quella – getline può leggere fino a un delimitatore, ma è ansible specificare solo un carattere come delimitatore. Se davvero non si può usare scanf e company, la prossima fermata sarebbe il codice a mano (l’inizio dell’età sarebbe temp.find_first_of("0123456789"); ) o usare un pacchetto RE (TR1 se il compilatore fornisce esso, altrimenti probabilmente Boost ).

Cosa c’è di sbagliato in questo?

 std::stringstream convertor("Tom Scott 25"); std::string firstname; std::string surname; int age; convertor >> firstname >> surname >> age; std::string name = firstname + " " + surname; 

Cosa c’è di sbagliato in questo?

 std::stringstream convertor("Tom Scott 25"); std::string first, last; int age; convertor >> first >> last >> age 

Se vuoi veramente leggere prima e durare in una volta, qualcosa del genere funzionerà

 class Name { std::string first, last; public: std::istream& read(std::istream& in) { return in >> first >> last; } operator std::string() const { return first + " " + last; } }; std::istream& operator>>(std::istream& in, Name& name) { return name.read(in); } /* ... */ Name name; int age; converter >> name >> age; std::cout << (std::string)name; 

Un esempio più generico in cui si desidera leggere N parole potrebbe funzionare in questo modo:

 class Reader { int numWords; std::vector words; // ... std::istream& read(std::istream& in) { std::vector tmp; std::string word; for (int i = 0; i < numWords; ++i) { if (!in >> word) return in; tmp.push_back(word); } // don't overwrite current words until success words = tmp; return in; } 

Algoritmo generale che è ansible implementare:

 read word into name loop try reading integer if success then break loop else clear error flag read word and attach to name 

Un approccio sarebbe quello di creare una nuova class con un operatore sovraccarico >>

 class TwoWordString { public: std::string str; }; istream& operator>>(istream& os; TwoWordString& tws) { std::string s1, s2; os >> s1; os >> s2; tws.str = s1 + s2; return os; } 

Ecco il modo eccessivo (usando Boost.Spirit )>: D

 #include  #include  #include  #include  #include  #include  #include  int main() { namespace qi = boost::spirit::qi; namespace phoenix = boost::phoenix; namespace ascii = boost::spirit::ascii; using ascii::char_; using ascii::digit; using ascii::blank; using qi::_1; using qi::int_; using phoenix::ref; using phoenix::at_c; std::string input("Sir Buzz Killington, esq. 25"); std::string name; int age = 0; qi::rule nameRule; nameRule %= (+(char_ - digit - blank)); std::string::const_iterator begin = input.begin(); std::string::const_iterator end = input.end(); qi::parse(begin, end, ( nameRule[ref(name) += _1] >> *( ((+blank) >> nameRule)[ref(name) += ' '] [ref(name) += at_c<1>(_1)] ) >> *blank >> int_[ref(age) = _1] ) ); std::cout << boost::format("Name: %1%\nAge: %2%\n") % name % age; return 0; } 

Produzione:

Nome: Sir Buzz Killington, esq.

Età: 25 anni

Seriamente, se si esegue spesso l'analisi di input non banale nel programma, è consigliabile utilizzare una libreria di espressioni o di espressioni regolari .

Questo è un compito che ho appena fatto. Ma i tipi int o double devono essere posizionati di fronte alla stringa. quindi puoi leggere più parole di dimensioni diverse. Spero che questo ti possa aiutare un po ‘.

 string words; sin>>day>>month>>year; sin>>words; watch = words; while(sin>>words) { watch += " "+words; } 

Ecco una soluzione con std::regex (qualsiasi numero di nomi):

 auto extractNameAndAge(std::string const &s) -> std::tuple { using namespace std::string_literals; static auto const r = std::regex{"(.*)\\s+(\\d+)\\s*$"s}; auto match = std::smatch{}; auto const matched = std::regex_search(s, match, r); if (!matched) throw std::invalid_argument{"Invalid input string \""s + s + "\" in extractNameAndAge"s}; return std::make_tuple(match[1], std::stoi(match[2])); } 

Test:

 auto main() -> int { using namespace std::string_literals; auto lines = std::vector{"Jonathan Vincent Voight 76"s, "Donald McNichol Sutherland 79"s, "Scarlett Johansson 30"s}; auto name = ""s; auto age = 0; for (auto cosnt &line : lines) { std::tie(name, age) = extractNameAndAge(line); cout << name << " - " << age << endl; } } 

Produzione:

 Jonathan Vincent Voight - 76 Donald McNichol Sutherland - 79 Scarlett Johansson - 30