2010-04-08 12 views
472

Necesito leer un archivo completo en la memoria y colocarlo en C++ std::string.Lea todo el archivo ASCII en C++ std :: string

Si tuviera que leerlo en un char[], la respuesta sería muy simple:

std::ifstream t; 
int length; 
t.open("file.txt");  // open input file 
t.seekg(0, std::ios::end); // go to the end 
length = t.tellg();   // report location (this is the length) 
t.seekg(0, std::ios::beg); // go back to the beginning 
buffer = new char[length]; // allocate memory for a buffer of appropriate dimension 
t.read(buffer, length);  // read the whole file into the buffer 
t.close();     // close file handle 

// ... Do stuff with buffer here ... 

Ahora, yo quiero hacer exactamente lo mismo, pero usando un std::string en lugar de un char[]. Quiero evitar bucles, es decir, que no quiero:

std::ifstream t; 
t.open("file.txt"); 
std::string buffer; 
std::string line; 
while(t){ 
std::getline(t, line); 
// ... Append line to buffer and go on 
} 
t.close() 

¿Alguna idea?

+1

Siempre habrá un bucle implicado, pero puede estar implícito como parte de la biblioteca estándar. Es eso aceptable? ¿Por qué estás tratando de evitar los bucles? –

+2

Creo que el afiche sabía que leer bytes implicaba bucles. Solo quería un equivalente fácil, estilo perl * gulp *. Eso implicó escribir un código pequeño. – unixman83

+0

Este código tiene errores, en el caso de que std :: string no use un búfer continuo para sus datos de cadena (lo que está permitido): http://stackoverflow.com/a/1043318/1602642 –

Respuesta

409

Actualización: Resulta que este método, aunque sigue bien las expresiones idiomáticas STL, ¡es sorprendentemente ineficiente! No hagas esto con archivos grandes. (Ver: http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html)

Usted puede hacer un iterador streambuf fuera del archivo e inicializar la cadena con él:

#include <string> 
#include <fstream> 
#include <streambuf> 

std::ifstream t("file.txt"); 
std::string str((std::istreambuf_iterator<char>(t)), 
       std::istreambuf_iterator<char>()); 

No está seguro de dónde obtiene la sintaxis de t.open("file.txt", "r"). Por lo que sé, ese no es un método que tiene std::ifstream. Parece que lo has confundido con C fopen.

Editar: También tenga en cuenta el paréntesis adicional alrededor del primer argumento para el constructor de cadenas. Estos son esenciales. Evitan el problema conocido como "most vexing parse", que en este caso no le dará un error de compilación como suele hacerlo, pero le dará resultados interesantes (leer: incorrecto).

Siguiendo punto de KeithB en los comentarios, he aquí una manera de hacerlo que asigna toda la memoria por adelantado (en lugar de depender de reasignación automática de la clase string):

#include <string> 
#include <fstream> 
#include <streambuf> 

std::ifstream t("file.txt"); 
std::string str; 

t.seekg(0, std::ios::end); 
str.reserve(t.tellg()); 
t.seekg(0, std::ios::beg); 

str.assign((std::istreambuf_iterator<char>(t)), 
      std::istreambuf_iterator<char>()); 
+4

open es definitivamente un método de ifstream, sin embargo, el 2do parámetro es incorrecto. http://www.cplusplus.com/reference/iostream/ifstream/open/ – Joe

+1

Derecha. Estaba diciendo que 'ifstream' no tiene un método con la firma' open (const char *, const char *) ' –

+0

Esto simplemente está haciendo explícito el bucle explícito. Como el iterador es un iterador directo, se leerá un carácter a la vez. Además, dado que no hay forma de que el constructor de cadenas conozca la longitud final, probablemente dará lugar a varias asignaciones y copias de los datos. – KeithB

-3

no creo que pueda Haga esto sin un bucle explícito o implícito, sin leer en una matriz char (o algún otro contenedor) primero y diez construyendo la cadena. Si no necesita las otras capacidades de una cadena, podría hacerlo con vector<char> de la misma manera que está utilizando actualmente un char *.

+12

-1 No es verdad ... Ver más arriba – unixman83

604

Hay un par de posibilidades. Uno me gusta usar un stringstream como un intermediario:

std::ifstream t("file.txt"); 
std::stringstream buffer; 
buffer << t.rdbuf(); 

Ahora el contenido de "archivo.txt" están disponibles en una cadena como buffer.str().

Otra posibilidad (aunque sin duda no me gusta así) es mucho más como el original:

std::ifstream t("file.txt"); 
t.seekg(0, std::ios::end); 
size_t size = t.tellg(); 
std::string buffer(size, ' '); 
t.seekg(0); 
t.read(&buffer[0], size); 

Oficialmente, esto no es necesario trabajar bajo el estándar de C++ 98 o 03 (no se requiere cadena para almacenar datos de forma contigua), pero de hecho funciona con todas las implementaciones conocidas, y C++ 11 y posterior requieren almacenamiento contiguo, por lo que está garantizado para trabajar con ellos.

En cuanto a por qué no me gusta este último también: primero, porque es más largo y más difícil de leer.En segundo lugar, porque requiere que inicialices el contenido de la cadena con datos que no te interesan, inmediatamente escribe esos datos (sí, el tiempo para inicializar suele ser trivial en comparación con la lectura, por lo que probablemente no importe) , pero para mí todavía se siente un poco mal). Tercero, en un archivo de texto, la posición X en el archivo no necesariamente significa que habrá leído X caracteres para llegar a ese punto; no es necesario tener en cuenta cosas como las traducciones de final de línea. En los sistemas reales que hacen tales traducciones (por ejemplo, Windows), el formulario traducido es más corto que el contenido del archivo (es decir, "\ r \ n" en el archivo se convierte en "\ n" en la cadena traducida) así que todo lo que ha hecho está reservado un poco de espacio extra que nunca usas. Una vez más, realmente no causa un problema importante, pero se siente un poco mal de todos modos.

+27

¡El trípode funciona como un amuleto! –

+66

Esto debería haber sido marcado como la respuesta. – unixman83

+27

Nota importante para algunos, al menos en mi implementación, el tríptico funciona al menos tan bien como la alternativa de C fopen para archivos de menos de 50 KB. Pasado eso, parece perder rendimiento rápidamente. En ese caso, solo use la segunda solución. – dcousens

1

que podía hacerlo de esta manera:

void readfile(const std::string &filepath,std::string &buffer){ 
    std::ifstream fin(filepath.c_str()); 
    getline(fin, buffer, char(-1)); 
    fin.close(); 
} 

Si esto es algo que debe ser mal visto, por favor hágamelo saber por qué

+3

char (-1) probablemente no sea una forma portátil de denotar EOF. Además, las implementaciones getline() no son necesarias para soportar el pseudo carácter EOF "inválido" como un carácter delimitador, creo. – reddish

45

creo que la mejor manera es utilizar la corriente de cuerdas. simple y rápido!

ifstream inFile; 
inFile.open(inFileName);//open the input file 

stringstream strStream; 
strStream << inFile.rdbuf();//read the file 
string str = strStream.str();//str holds the content of the file 

cout << str << endl;//you can do anything with the string!!! 
+1

Simple y rápido, ¡cierto! http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html – Narek

+3

Recuerde cerrar la secuencia después ... –

+14

@YngveSneenLindal O deje que el destructor lo haga automáticamente - ¡aprovecha C++! –

3

Me di cuenta de otra manera que funciona con la mayoría de las corrientes isotérmicas, incluida std :: cin!

std::string readFile() 
{ 
stringstream str; 
ifstream stream("Hello_World.txt"); 
if(stream.is_open()) 
{ 
    while(stream.peek() != EOF) 
    { 
     str << (char) stream.get(); 
    } 
    stream.close(); 
    return str.str(); 
} 
} 
4

Pruebe uno de estos dos métodos:

string get_file_string(){ 
    std::ifstream ifs("path_to_file"); 
    return string((std::istreambuf_iterator<char>(ifs)), 
        (std::istreambuf_iterator<char>())); 
} 

string get_file_string2(){ 
    ifstream inFile; 
    inFile.open("path_to_file");//open the input file 

    stringstream strStream; 
    strStream << inFile.rdbuf();//read the file 
    return strStream.str();//str holds the content of the file 
} 
7

Es posible que no encontrará en cualquier libro o sitio, pero descubrí que funciona bastante bien:

ifstream ifs ("filename.txt"); 
string s; 
getline (ifs, s, (char) ifs.eof()); 
+5

Casting 'eof' to' (char) 'es un poco dudoso, lo que sugiere algún tipo de relevancia y universalidad que es ilusoria. Para algunos valores posibles de 'eof()' y 'char' con signo, dará resultados definidos por la implementación. Uso directo, p. 'char (0)'/''\ 0'' sería más robusto y honestamente indicativo de lo que está sucediendo. –

+2

@TonyD. Buen punto para convertir eof() en un char. Supongo que para los conjuntos de caracteres ascii de la vieja escuela, pasar cualquier valor negativo (msb establecido en 1) funcionaría. Pero pasar \ 0 (o un valor negativo) no funcionará para archivos de entrada de ancho o multibyte. – riderBill

+1

Esto solo funcionará, siempre que no haya caracteres "eof" (por ejemplo, 0x00, 0xff, ...) en su archivo. Si los hay, solo leerá parte del archivo. –

1

Si Si usa glibmm puede probar Glib::file_get_contents.

#include <iostream> 
#include <glibmm.h> 

int main() { 
    auto filename = "my-file.txt"; 
    try { 
     std::string contents = Glib::file_get_contents(filename); 
     std::cout << "File data:\n" << contents << std::endl; 
    catch (const Glib::FileError& e) { 
     std::cout << "Oops, an error occurred:\n" << e.what() << std::endl; 
    } 

    return 0; 
}