c++ - ejemplos - Ejemplo de por qué stream:: good es incorrecto
bitset c++ example (5)
Di una respuesta que quería verificar la validez del flujo cada vez a través de un bucle aquí .
Mi código original se usa good
y se parece a esto:
ifstream foo("foo.txt");
while (foo.good()){
string bar;
getline(foo, bar);
cout << bar << endl;
}
Me señalaron de inmediato y me dijeron que nunca hiciera una good
prueba. Claramente, esto es algo que no he entendido, pero quiero hacer correctamente mi E / S de archivos.
Probé mi código con varios ejemplos y no pude hacer que fallara el good
código de prueba.
Primero (esto se imprime correctamente, terminando con una nueva línea):
Bleck 1
Blee 1 2
paja
termina en una nueva línea
Segundo (este impreso correctamente, terminando con la última línea):
Bleck 1
Blee 1 2
paja
esto no termina en una nueva línea
El tercero era un archivo vacío (esto se imprimió correctamente, una sola línea nueva).
El cuarto era un archivo faltante (esto no imprimió correctamente nada).
¿Alguien puede ayudarme con un ejemplo que demuestre por qué no se debe hacer una good
prueba?
El uso de foo.good()
solo te dice que la operación de lectura anterior funcionó bien y que la siguiente también podría funcionar. .good()
comprueba el estado de la transmisión en un punto determinado. No verifica si se llega al final del archivo. Digamos que sucedió algo mientras se leía el archivo (error de red, error de os, ...) bueno, fallará. Eso no significa que se llegó al final del archivo. Sin embargo, .good () falla cuando se llega al final del archivo porque la transmisión ya no puede leer.
Por otro lado, .eof()
verifica si realmente se llegó al final del archivo.
Entonces, .good()
podría fallar mientras no se alcanzaba el final del archivo.
Espero que esto te ayude a entender por qué usar .good()
para verificar el final del archivo es un mal hábito.
Esto ya está cubierto en otras respuestas, pero lo revisaré brevemente para completarlo. La única diferencia funcional con
while(foo.good()) { // effectively same as while(foo) {
getline(foo, bar);
consume(bar); // consume() represents any operation that uses bar
}
Y
while(getline(foo, bar)){
consume(bar);
}
Es que el primero hará un bucle adicional cuando no haya líneas en el archivo, haciendo que ese caso no se distinga del caso de una línea vacía. Yo diría que esto no es típicamente el comportamiento deseado. Pero supongo que es cuestión de opinión.
Como dice Sehe, el mantra está por la borda . Es una simplificación. De lo que realmente se trata es de que no debe consume()
el resultado de leer la secuencia antes de probar la falla o al menos EOF (y cualquier prueba antes de que la lectura sea irrelevante). Que es lo que las personas hacen fácilmente cuando prueban good()
en la condición de bucle.
Sin embargo, lo que pasa con getline()
es que prueba EOF internamente, para usted y devuelve una cadena vacía incluso si solo se lee EOF. Por lo tanto, la versión anterior podría ser más o menos similar a la siguiente pseudoc ++:
while(foo.good()) {
// inside getline
bar = ""; // Reset bar to empty
string sentry;
if(read_until_newline(foo, sentry)) {
// The streams state is tested implicitly inside getline
// after the value is read. Good
bar = sentry // The read value is used only if it''s valid.
// ... // Otherwise, bar is empty.
consume(bar);
}
Espero que eso ilustre lo que trato de decir. Se podría decir que hay una versión "correcta" del ciclo de lectura dentro de getline()
. Esta es la razón por la cual la regla se satisface al menos parcialmente con el uso de readline incluso si el bucle externo no se ajusta.
Pero, para otros métodos de lectura, romper la regla duele más. Considerar:
while(foo.good()) {
int bar;
foo >> bar;
consume(bar);
}
¡No solo siempre obtienes la iteración adicional, la bar
en esa iteración no está inicializada!
Así que, en resumen, while(foo.good())
está bien en su caso, porque getline()
diferencia de otras funciones de lectura, deja el resultado en un estado válido después de leer el bit EOF. y porque no le importa o incluso espera la iteración adicional cuando el archivo está vacío.
tanto good()
como eof()
le darán una línea adicional en su código. Si tienes un archivo en blanco y ejecuta esto:
std::ifstream foo1("foo1.txt");
std::string line;
int lineNum = 1;
std::cout << "foo1.txt Controlled With good():/n";
while (foo1.good())
{
std::getline(foo1, line);
std::cout << lineNum++ << line << std::endl;
}
foo1.close();
foo1.open("foo1.txt");
lineNum = 1;
std::cout << "/n/nfoo1.txt Controlled With getline():/n";
while (std::getline(foo1, line))
{
std::cout << line << std::endl;
}
El resultado que obtendrá es
foo1.txt Controlled With good():
1
foo1.txt Controlled With getline():
Esto demuestra que no está funcionando correctamente, ya que un archivo en blanco nunca debe leerse. La única manera de saberlo es usar una condición de lectura ya que la transmisión siempre será buena la primera vez que se lee.
Ellos estaban equivocados. El mantra es ''nunca prueba .eof()
''.
Incluso ese mantra está por la borda, porque ambos son útiles para diagnosticar el estado de la secuencia después de que falló una extracción.
Entonces el mantra debería ser más como
No use
good()
oeof()
para detectar eof antes de intentar leer más
Lo mismo para fail()
y bad()
Por supuesto, stream.good
puede utilizar de manera útil antes de usar una transmisión (por ejemplo, en el caso de que la transmisión sea una ruta de archivos que no se haya abierto correctamente)
Sin embargo, ambos son muy muy a menudo maltratados para detectar el final de la entrada, y no es así como funciona.
Un ejemplo canónico de por qué no deberías usar este método:
std::istringstream stream("a");
char ch;
if (stream >> ch) {
std::cout << "At eof? " << std::boolalpha << stream.eof() << "/n";
std::cout << "good? " << std::boolalpha << stream.good() << "/n";
}
Huellas dactilares
false
true
Permítanme decir claramente que la respuesta de Sehe es la correcta.
Pero la opción propuesta por Nathan Oliver , Neil Kirk y user2079303 es usar readline
como la condición de bucle en lugar de good
. Debe ser abordado por el bien de la posteridad.
Compararemos el ciclo en la pregunta con el siguiente ciclo:
string bar;
while (getline(foo, bar)){
cout << bar << endl;
}
Porque getline
devuelve el istream
pasado como el primer argumento, y porque cuando un istream
se istream
en bool
, !(fail() || bad())
vuelve !(fail() || bad())
, y desde que lee el carácter EOF establecerá tanto el failbit
como el eofbit
esto hace getline
una condición de bucle válida.
Sin embargo, el comportamiento cambia cuando se usa getline
como condición, ya que si se lee una línea que contiene solo un carácter EOF, el ciclo saldrá para evitar que se genere esa línea. Esto no ocurre en los ejemplos 2 y 4. Pero el ejemplo 1:
Bleck 1
Blee 1 2
paja
termina en una nueva línea
Imprime esto con la good
condición de bucle:
Bleck 1
Blee 1 2
paja
termina en una nueva línea
Pero corta la última línea con la condición getline
loop:
Bleck 1
Blee 1 2
paja
termina en una nueva línea
El ejemplo 3 es un archivo vacío:
Imprime esto con las good
condiciones:
No imprime nada con la condición getline
.
Ninguno de estos comportamientos es incorrecto. Pero esa última línea puede hacer una diferencia en el código. Esperemos que esta respuesta sea útil para usted al decidir entre los dos para fines de codificación.