Cómo leer hasta EOF de cin en C ++

80

Estoy codificando un programa que lee datos directamente de la entrada del usuario y me preguntaba cómo podría (sin bucles) leer todos los datos hasta EOF de la entrada estándar. Estaba considerando usar, cin.get( input, '\0' )pero '\0'no es realmente el carácter EOF, que solo se lee hasta EOF o '\0', lo que ocurra primero.

¿O es el uso de bucles la única forma de hacerlo? Si es así, ¿cuál es la mejor manera?

Guille
fuente

Respuestas:

78

La única forma de leer una cantidad variable de datos stdines mediante bucles. Siempre he encontrado que la std::getline()función funciona muy bien:

std::string line;
while (std::getline(std::cin, line))
{
    std::cout << line << std::endl;
}

Por defecto getline()lee hasta una nueva línea. Puede especificar un carácter de terminación alternativo, pero EOF no es en sí mismo un carácter, por lo que no puede simplemente realizar una llamada a getline().

trotterdylan
fuente
8
Algo de lo que debe tener cuidado aquí es cuando lee archivos que se han canalizado a través de stdin, es que esto y la mayoría de las respuestas dadas agregarán un avance de línea adicional al final de su entrada. No todos los archivos terminan con un salto de línea, sin embargo, cada iteración del bucle agrega "std :: endl" al flujo. Es posible que esto no sea un problema en la mayoría de los casos, pero si la integridad del archivo es un problema, esto puede volver a molestarlo.
Zoccadoum
1
Esta no debería ser la respuesta más votada. Vea la respuesta de la wiki de la comunidad a continuación. Además, tal vez vea esta pregunta: stackoverflow.com/questions/3203452/…
loxaxs
52

Usando bucles:

#include <iostream>
using namespace std;
...
// numbers
int n;
while (cin >> n)
{
   ...
}
// lines
string line;
while (getline(cin, line))
{
   ...
}
// characters
char c;
while (cin.get(c))
{
   ...
}

recurso

Degvik
fuente
50

Puede hacerlo sin bucles explícitos utilizando iteradores de flujo. Estoy seguro de que usa algún tipo de bucle internamente.

#include <string>
#include <iostream>
#include <istream>
#include <ostream>
#include <iterator>

int main()
{
// don't skip the whitespace while reading
  std::cin >> std::noskipws;

// use stream iterators to copy the stream to a string
  std::istream_iterator<char> it(std::cin);
  std::istream_iterator<char> end;
  std::string results(it, end);

  std::cout << results;
}
KeithB
fuente
3
Agradable. En cuanto a "Estoy seguro de que utiliza algún tipo de bucle internamente", cualquier solución lo haría.
Michael Burr
Esto parece eliminar los caracteres de nueva línea de la entrada, incluso si ocurren justo en el medio.
Multisync
28

Después de investigar el uso de la solución de KeithB std::istream_iterator, descubrí el std:istreambuf_iterator.

Pruebe el programa para leer toda la entrada canalizada en una cadena, luego escríbala nuevamente:

#include <iostream>
#include <iterator>
#include <string>

int main()
{
  std::istreambuf_iterator<char> begin(std::cin), end;
  std::string s(begin, end);
  std::cout << s;
}
Richard Smith
fuente
7
Esta probablemente debería ser la respuesta canónica.
Kerrek SB
2
Voto en contra. Entonces, ¿cuáles son los pros o los contras en comparación con la solución de KeithB? Sí, está utilizando un en std::istreambuf_iteratorlugar de un std::istream_iterator, pero ¿para qué?
Multisync
std :: istreambuf_iterator omite los espacios en blanco de forma predeterminada y puede ser más rápido
Sopel
5

Probablemente más simple y generalmente eficiente:

#include <iostream>
int main()
{
    std::cout << std::cin.rdbuf();
}

Si es necesario, utilice un flujo de otros tipos std::ostringstreamcomo búfer en lugar del flujo de salida estándar aquí.

FrankHB
fuente
ustedes ya saben, pero puede ser útil para algunos: std::ostringstream std_input; std_input << std::cin.rdbuf(); std::string s = std_input.str(). tienes toda la entrada adentro s(ten cuidado std::stringsi la entrada es demasiado grande)
ribamar
2

Nota al margen triste: decidí usar C ++ IO para ser consistente con el código basado en boost. De las respuestas a esta pregunta elegí while (std::getline(std::cin, line)). Usando g ++ versión 4.5.3 (-O3) en cygwin (mintty) obtuve un rendimiento de 2 MB / s. Microsoft Visual C ++ 2010 (/ O2) lo convirtió en 40 MB / s para el mismo código.

Después de reescribir el IO a C puro, while (fgets(buf, 100, stdin))el rendimiento saltó a 90 MB / s en ambos compiladores probados. Eso marca la diferencia para cualquier entrada mayor de 10 MB ...

liborm
fuente
No es necesario reescribir su código en C puro, solo agregue un std::ios::sync_with_stdio(false);antes de su ciclo while. cin.tie(NULL);también puede ayudar si intercala la lectura y la escritura.
RD
0
while(std::cin) {
 // do something
}
Bryan
fuente
8
Esta es una respuesta terrible, ya que está prácticamente garantizado que resultará en un código incorrecto.
Kerrek SB
@KerrekSB ¿por qué está prácticamente garantizado que dará como resultado un código incorrecto?
matusf
1
@ MatúšFerech: Porque sugiere enfáticamente que // do somethingno contiene verificaciones adicionales de los valores de retorno de las operaciones de E / S, y la verificación que contiene no tiene sentido.
Kerrek SB
0

Espera, ¿te estoy entendiendo correctamente? ¿Está usando cin para la entrada del teclado y desea dejar de leer la entrada cuando el usuario ingresa el carácter EOF? ¿Por qué el usuario escribiría el carácter EOF? ¿O quiso decir que quiere dejar de leer un archivo en el EOF?

Si realmente está tratando de usar cin para leer un carácter EOF, ¿por qué no especificar el EOF como delimitador?

// Needed headers: iostream

char buffer[256];
cin.get( buffer, '\x1A' );

Si quiere dejar de leer de un archivo en el EOF, simplemente use getline y una vez más especifique el EOF como delimitador.

// Needed headers: iostream, string, and fstream

string buffer;

    ifstream fin;
    fin.open("test.txt");
    if(fin.is_open()) {
        getline(fin,buffer,'\x1A');

        fin.close();
    }
uberwulu
fuente
0

Una opción es utilizar un contenedor, por ejemplo

std::vector<char> data;

y redirigir toda la entrada a esta colección hasta que EOFse reciba, es decir

std::copy(std::istream_iterator<char>(std::cin),
    std::istream_iterator<char>(),
    std::back_inserter(data));

Sin embargo, es posible que el contenedor usado necesite reasignar memoria con demasiada frecuencia, o terminará con una std::bad_allocexcepción cuando su sistema se quede sin memoria. Para resolver estos problemas, puede reservar una cantidad fija Nde elementos y procesar esta cantidad de elementos de forma aislada, es decir

data.reserve(N);    
while (/*some condition is met*/)
{
    std::copy_n(std::istream_iterator<char>(std::cin),
        N,
        std::back_inserter(data));

    /* process data */

    data.clear();
}
0xbadf00d
fuente