Alternativa C ++ più sicura ma facile da usare e flessibile per sscanf ()

Quando ho bisogno di scansionare i valori da un gruppo di stringhe, spesso mi ritrovo a tornare a C sscanf() causa della sua semplicità e facilità d’uso. Ad esempio, posso estrarre in modo molto sintetico un paio di valori double di una stringa con:

 string str; double val1, val2; if (sscanf(str.c_str(), "(%lf,%lf)", &val1, &val2) == 2) { // got them! } 

Questo ovviamente non è molto C ++. Non lo considero necessariamente un abominio, ma sono sempre alla ricerca di un modo migliore per svolgere un compito comune. Capisco che il “modo C ++” per leggere le stringhe è istringstream , ma la tipizzazione aggiuntiva richiesta per gestire la parentesi e la virgola nella stringa di formato sopra rende tutto troppo complicato per farmi desiderare di usarlo.

C’è un buon modo per piegare le strutture integrate alla mia volontà in un modo simile a quanto sopra, o c’è una buona libreria C ++ che fa quanto sopra in un modo più sicuro dal punto di vista dei caratteri? Sembra che Boost.Format abbia davvero risolto il problema in uscita in un buon modo, ma non ho trovato nulla di altrettanto succinto per l’input.

Ho scritto un po ‘di codice che può essere letto in stringhe e caratteri letterali. Come le normali letture di stream, se ottiene dati non validi imposta il badbit del stream. Questo dovrebbe funzionare per tutti i tipi di flussi, inclusi i flussi ampi. Attacca questo bit in una nuova intestazione:

 #include  #include  #include  #include  template std::basic_istream& operator>>(std::basic_istream& in, const e(&sliteral)[N]) { std::array buffer; //get buffer in >> buffer[0]; //skips whitespace if (N>2) in.read(&buffer[1], N-2); //read the rest if (strncmp(&buffer[0], sliteral, N-1)) //if it failed in.setstate(in.rdstate() | std::ios::failbit); //set the state return in; } template std::basic_istream& operator>>(std::basic_istream& in, const e& cliteral) { e buffer; //get buffer in >> buffer; //read data if (buffer != cliteral) //if it failed in.setstate(in.rdstate() | std::ios::failbit); //set the state return in; } //redirect mutable char arrays to their normal function template std::basic_istream& operator>>(std::basic_istream& in, e(&carray)[N]) { return std::operator>>(in, carray); } 

E renderà i caratteri di input molto semplici:

 std::istringstream input; double val1, val2; if (input >>'('>>val1>>','>>val2>>')') //less chars than scanf I think { // got them! } 

PROVA DEL CONCETTO . Ora puoi inserire string letterali di caratteri e caratteri, e se l’input non è una corrispondenza esatta, si comporta come qualsiasi altro tipo che non è riuscito a inserire correttamente. Si noti che questo corrisponde solo agli spazi bianchi in stringhe letterali che non sono il primo carattere. Sono solo quattro funzioni, tutte semplici come il cervello.

MODIFICARE

L’analisi con i flussi è una ctriggers idea. Usa un’espressione regolare.

La cosa migliore che abbia mai usato per l’analisi delle stringhe è boost.spirit. È veloce, sicuro e molto flessibile. Il grande vantaggio è che puoi scrivere regole di parsing in forma vicino alla grammatica EBNF

 using namespace boost::spirit; boost::fusion::vector < double, double > value_; std::string string_ = "10.5,10.6 "; bool result_ = qi::parse( string_.begin(), string_.end(), qi::double_ >> ',' >> qi::double_, // Parsing rule value_); // value 

Penso che con regex potrebbe essere fatto facile. Quindi boost :: regex o std :: regex in un nuovo standard. Successivamente, converti i token in float usando direttamente lexical_cast o gli stream.