Una alternativa de C ++ más segura pero fácil de usar y flexible a sscanf ()

Cuando necesito escanear valores de un montón de cadenas, a menudo me encuentro sscanf() C estrictamente debido a su simplicidad y facilidad de uso. Por ejemplo, puedo extraer muy sucintamente un par de valores dobles de una cadena con:

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

Esto obviamente no es muy C ++. No necesariamente lo considero una abominación, pero siempre estoy buscando una mejor manera de hacer una tarea común. Entiendo que la “forma en C ++” de leer cadenas es istringstream , pero la escritura adicional requerida para manejar el paréntesis y la coma en la cadena de formato anterior solo hace que sea demasiado incómodo para que quiera usarla.

¿Hay una buena manera de doblar las instalaciones incorporadas a mi voluntad de una manera similar a la anterior, o hay una buena biblioteca de C ++ que hace lo anterior de una manera más segura? Parece que Boost.Format realmente resolvió el problema de salida de una buena manera, pero no he encontrado nada igualmente conciso para la entrada.

Escribí un poco de código que se puede leer en literales de cadenas y caracteres. Al igual que las lecturas de secuencias normales, si obtiene datos no válidos, establece el bit de la secuencia. Esto debería funcionar para todos los tipos de flujos, incluidos los flujos amplios. Pegue este bit en un nuevo encabezado:

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

Y hará que los caracteres de entrada sean muy fáciles:

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

PRUEBA DE CONCEPTO . Ahora puede cin literales de cadena y caracteres, y si la entrada no es una coincidencia exacta, actúa como cualquier otro tipo que no ingresó correctamente. Tenga en cuenta que esto solo coincide con los espacios en blanco en los literales de cadena que no son el primer carácter. Son solo cuatro funciones, todas ellas con muerte cerebral simple.

EDITAR

Analizar con streams es una mala idea. Utilice un regex.

Lo mejor que he usado para analizar cadenas es boost.spirit. Es rápido, seguro y muy flexible. La gran ventaja es que puede escribir reglas de análisis en forma cercana a la gramática 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 

Creo que con regex se podría hacer fácil. Así que boost :: regex o std :: regex en un nuevo estándar. Después de eso, simplemente convierte tus tokens a flotar usando lexical_cast o transmite directamente.