scanf en un objeto istream

NOTA: He visto la publicación ¿Cuál es el cin analougus de la entrada formateada Scanf? antes de hacer la pregunta y la publicación no resuelve mi problema aquí. La publicación busca C ++, la forma de hacerlo, pero como ya mencioné, es inconveniente usar C ++, es una manera de hacerlo a veces y tengo ejemplos claros para eso.

Estoy tratando de leer datos de un objeto istream, y algunas veces es inconveniente usar solo las formas de C ++, como el operador >>, por ejemplo, los datos están en forma especial 123: 456, por lo que debes imbuir para hacer ‘:’ como espacio (que es muy hacky, en comparación con% d:% d en scanf), o 00123 donde desea leer como cadena y convertir decimal en lugar de octal (en oposición a% d en scanf), y posiblemente en muchos otros casos.

La razón por la que elegí istream como interfaz es porque se puede derivar y, por lo tanto, es más flexible. Por ejemplo, podemos crear secuencias en la memoria, o algunas secuencias personalizadas que se generaron sobre la marcha, etc. C-style FILE *, por otro lado, es muy limitado, al menos de una manera estándar, en la creación personalizada streams

Entonces mi pregunta es, ¿hay alguna manera de hacer una extracción de datos similar a Scanf en el objeto istream? Creo que fscanf internamente lee carácter por carácter de FILE * usando fgetc, mientras que istream también proporciona dicha interfaz. Entonces es posible simplemente copiando y pegando el código de fscanf y reemplazando el FILE * con el objeto istream, pero eso es muy hacky. ¿Hay una manera más inteligente y más limpia, o hay algún trabajo existente sobre esto?

Gracias.

Nunca debe, bajo ninguna circunstancia, utilizar scanf o sus familiares para nada , por tres razones:

  1. Muchas cadenas de formato, que incluyen, por ejemplo, todos los usos simples de %s , son tan peligrosas como gets .
  2. Es casi imposible recuperarse de una entrada mal formada, porque el scanf no le dice qué tan lejos están los caracteres de la entrada que recibió cuando golpeó algo inesperado.
  3. El desbordamiento numérico desencadena un comportamiento indefinido: sí, eso significa que scanf puede bloquear todo el progtwig si un campo numérico en la entrada tiene demasiados dígitos.

Antes de C ++ 11, la especificación de C ++ definía la entrada formateada istream de números en términos de scanf , lo que significa que la última objeción es muy probable que se aplique a ellos también. (En C ++ 11, la especificación se cambia para usar strto* lugar y para hacer algo predecible si eso detecta desbordamiento).

Lo que debe hacer en su lugar es: leer líneas enteras de entrada en objetos std::string con getline , lógica de código de mano para dividirlos en campos (no recuerdo de ningún modo lo que el C ++ – string equivalente de strsep es, pero estoy seguro de que existe) y luego convierte cadenas numéricas en números de máquina con la familia de funciones strtol / strtod .

No puedo enfatizar esto lo suficiente: LA ÚNICA FORMA 100% FIABLE PARA CONVERTIR CADENAS A NÚMEROS EN C O C ++, a menos que tenga la suerte de tener un tiempo de ejecución C ++ que ya strto* C ++ 11 en este sentido, ES CON EL strto* FUNCIONES, y debe usarlas correctamente:

 errno = 0; result = strtoX(s, &ends, 10); // omit 10 for floats if (s == ends || *ends || errno) parse_error(); 

(Las páginas de manual de OpenBSD, vinculadas anteriormente, explican por qué tiene que hacer esta cosa bastante enrevesada).

(Si es inteligente, puede usar ends y alguna lógica manual para omitir ese punto, en lugar de strsep ).

No recomiendo mezclar salida de entrada C ++ y salida de entrada C. No, que son realmente incompatibles, pero que simplemente podrían interoperar mal.

Por ejemplo, Oracle Doc recomienda no mezclarlo http://www.oracle.com/technetwork/articles/servers-storage-dev/mixingcandcpluspluscode-305840.html

Pero nadie te impide leer datos en el búfer y analizarlos con funciones c estándar como sscanf.

 ... string curString; int a, b; ... std::getline(inputStream, curString); int sscanfResult == sscanf(curString.cstr(), "%d:%d", &a, &b); if (2 != sscanfResult) throw "error"; ... 

Pero no ayudará en algunas situaciones cuando la transmisión es solo una larga secuencia contigua de símbolos (como una cadena convertida en secuencia de memoria).

Hacer su propio fscanf desde cero o portar (?) La función CRT original en realidad no es la peor idea posible. Solo asegúrate de haberlo probado a fondo (la manipulación personalizada de bajo nivel siempre fue una fuente de dolor en C).

Realmente nunca he probado el espíritu de impulso y tal infraestructura de análisis podría ser una exageración para su proyecto. Pero las bibliotecas de impulso generalmente se prueban y diseñan bien. Al menos podrías intentar usarlo.

Basado en el comentario de @tmyklebu, implementé StreamScanf que envuelve istream como FILE * a través de fopencookie: https://github.com/likan999/codejam/blob/master/Common/StreamScanf.cpp