La extracción normal de un valor entero tiene éxito si la transmisión puede leer cualquier valor entero. Es decir, si hay al menos un dígito opcionalmente seguido de cualquier cosa, la lectura de un entero tiene éxito. Las operaciones de extracción normal no intentan leer más, en particular, no intentan encontrar el siguiente espacio en blanco.
Por lo que suena, quiere asegurarse de que hay un espacio en blanco después de su número y si no lo hace, puede fallar. Puedo pensar en dos enfoques diferentes para hacer esto:
- Crea un manipulador simple que comprueba si la transmisión está en un carácter de espacio en blanco. Esto, sin embargo, significa que leería sus valores usando algo como
in >> value >> is_space
.
- Cree una faceta
std::num_get<char>
personalizada, instálela en std::locale
y imbue()
en la (s) fuente (es) std::locale
. Es un poco más complicado, pero no requiere ningún cambio en la forma en que se leen los enteros.
Creación de un manipulador de este tipo es bastante trivial:
std::istream& is_space(std::istream& in)
{
if (!std::isspace(in.peek()))
{
in.setstate(std::ios_base::failbit);
}
return in;
}
Ahora, cambiando la forma en que los números se leen es más interesante y sospecho que acababa de llamada de un número de clases de la librería estándar de la mayoría de las personas son bastante inconsciente de. Entonces, escribamos rápidamente un ejemplo para esto también. Cambiaré la faceta std::num_get<char>
solo para tratar con unsigned int
: para otros tipos integrales es necesario anular más funciones.Así pues, aquí es un reemplazo para el std::num_get<char>
faceta:
class num_get:
public std::num_get<char>
{
iter_type do_get(iter_type it, iter_type end,
std::ios_base& ios, std::ios_base::iostate& err,
unsigned int& value) const
{
it = std::num_get<char>::do_get(it, end, ios, err, value);
if (it != end && !isspace(static_cast<unsigned char>(*it)))
{
err |= std::ios_base::failbit;
}
return it;
}
};
Todo esto hace es derivar una clase de std::num_get<char>
y anular una de sus funciones virtuales. La implementación de esta función es bastante directa: empiezo por leer el valor delegando en la clase base (me acabo de dar cuenta de que las funciones virtuales realmente quieren proteger en lugar de ser privadas como lo he hecho en el pasado, pero esta es una discusión completamente diferente) . Independientemente de si esto fue exitoso (si no lo fue, habrá configurado un estado de error en err) la anulación verifica si hay otro carácter disponible y, de ser así, verifica si es un espacio y si no establece un std::ios_base::failbit
en el resultado del error err
.
Lo que queda es la creación de la corriente de utilizar esta faceta particular en un std::locale
y enganche la nueva std::locale
en una corriente:
std::locale loc(std::locale(), new num_get);
in.imbue(loc);
Los std::locale
s y sus facetas están internamente contados referencia, es decir, usted no debe No pierda de vista el puntero a la faceta y no necesita mantener el std::locale
alrededor tampoco. Si parece engorroso para imbue()
el std::locale
creado o si desea utilizar esta lógica modificada en cualquier lugar, puede establecer el std::locale
global que se utiliza para inicializar cualquier secuencia recién creada para usar la faceta personalizada std::num_get<char>
.
Así es como funciona 'operator >>'; extraerá '33' y luego se detendrá, 'abcd' permanecerá en la secuencia para la siguiente llamada a 'operator >>'. En su lugar, podría leer '33abcd4' en una cadena y luego buscar caracteres no numéricos en ella. Además, si tiene un compilador reciente que admita C++ 11, verifique si la biblioteca estándar proporciona 'std :: stoull'. – jrok