2008-10-14 22 views
65

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 desde la entrada estándar. Estaba considerando usar cin.get(input, '\0') pero '\0' no es realmente el carácter EOF, que solo lee hasta EOF o '\0', lo que ocurra primero.Cómo leer hasta EOF desde cin en C++

¿O está utilizando los bucles la única manera de hacerlo? Si es así, ¿cuál es la mejor manera?

Respuesta

2

Puede usar la función std::istream::getline() (o preferiblemente la version that works on std::string) para obtener una línea completa. Ambos tienen versiones que le permiten especificar el delimitador (carácter de fin de línea). El valor predeterminado para la versión de cadena es '\ n'.

57

La única forma en que puede leer una cantidad variable de datos desde stdin es utilizando bucles. Siempre he encontrado que la función std::getline() funciona muy bien:

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

Por defecto getline() lee hasta que 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 hacer una llamada a getline().

+5

Algo que tener cuidado de que aquí es cuando se está leyendo a partir de archivos que han sido canalizados a través de la entrada estándar, es que esta y la mayoría de las respuestas proporcionadas agregarán un avance de línea adicional al final de su entrada. No todos los archivos terminan con un avance de línea, pero cada iteración del ciclo agrega "std :: endl" a la transmisión. Esto puede no ser un problema en la mayoría de los casos, pero si la integridad del archivo es un problema, esto puede volver a morderte. – Zoccadoum

+0

Esta no debería ser la respuesta más votada. Consulte la respuesta wiki de la comunidad a continuación. Además, tal vez vea esta pregunta: https://stackoverflow.com/questions/3203452/how-to-read-entire-stream-into-a-stdstring – loxaxs

40

Puede hacerlo sin bucles explícitos mediante el uso de iteradores de flujo. Estoy seguro de que utiliza 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; 
} 
+2

Nice. En cuanto a "estoy seguro de que utiliza algún tipo de bucle internamente", cualquier solución lo haría. –

+0

Esto parece eliminar los caracteres de nueva línea de la entrada, incluso si ocurren justo en el medio. – Multisync

36

mediante 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)) 
{ 
    ... 
} 

resource

1
while(std::cin) { 
// do something 
} 
+7

Este es un desastre terrible de una respuesta, ya que está prácticamente garantizado que se obtenga un código incorrecto. –

2

triste nota: yo decidimos utilizar C++ IO para ser coherente con el código basado impulso. 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 2 MB/s de rendimiento. Microsoft Visual C++ 2010 (/ O2) lo convirtió en 40 MB/s para el mismo código.

Después de volver a escribir la IO a C puro while (fgets(buf, 100, stdin)) el rendimiento saltó a 90 MB/s en ambos compiladores ensayadas. Eso hace una diferencia para cualquier entrada de más de 10 MB ...

+0

No es necesario volver a escribir el código en C puro, simplemente agregue un 'std :: ios :: sync_with_stdio (false);' antes de su while-loop. 'cin.tie (NULL);' también puede ayudar si intercalas lectura y escritura. –

0

Espera, ¿te entiendo correctamente? ¿Estás utilizando cin para ingresar el teclado, y quieres dejar de leer la entrada cuando el usuario ingresa el carácter EOF? ¿Por qué el usuario alguna vez ingrese 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 simplemente especifica el EOF como el delimitador?

// Needed headers: iostream 

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

Si se refiere a dejar de leer desde un archivo en el EOF, a continuación, sólo tiene que utilizar getline y una vez más especificar 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(); 
    } 
0

Una opción es utilizar un contenedor, p.

std::vector<char> data; 

y redirigir todas las entradas en esta colección hasta EOF se recibe, es decir,

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

Sin embargo, el recipiente usado puede ser que necesite para reasignar la memoria con demasiada frecuencia, o puede acabar con una excepción std::bad_alloc cuando el sistema se pone fuera de la memoria. Con el fin de resolver estos problemas, se podría reserva una cantidad fija N de elementos y procesar estos cantidad de elementos en 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(); 
} 
3

más simple estimado y generalmente eficiente:

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

Si es necesario , utilice la secuencia de otros tipos como std::ostringstream como búfer en lugar de flujo de salida estándar aquí.

+0

ustedes ya lo 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 en 's' (ten cuidado con' std :: string' si la entrada es demasiado grande) – ribamar

14

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

Programa de prueba para leer todas las entradas por tubería en una cadena, a continuación, escriba de nuevo:

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

int main() 
{ 
    std::istreambuf_iterator<char> begin(std::cin), end; 
    std::string s(begin, end); 
    std::cout << s; 
} 
+2

Probablemente esta sea la respuesta canónica. –

+0

Downvoted. ¿Cuáles son los pros o los contras en comparación con la solución de KeithB? Sí, está utilizando un 'std :: istreambuf_iterator' en lugar de' std :: istream_iterator', pero ¿para qué? – Multisync