2008-10-12 15 views
55

Me gustaría leer todo el contenido de un archivo de texto en un objeto std::string con C++.¿Cuál es la forma más elegante de leer un archivo de texto con C++?

Con Python, puedo escribir:

text = open("text.txt", "rt").read() 

es muy simple y elegante. Odio las cosas feas, así que me gustaría saber: ¿cuál es la forma más elegante de leer un archivo de texto con C++? Gracias.

+21

Si odias las cosas feas, es mejor que no uses C++: P – OregonGhost

+6

Una nota sobre la elegancia, aunque la iostream más elegante la solución aún te parecerá desagradable, puedes encapsular en una función hermosa para que no te lastime los ojos;) – OregonGhost

+2

En cuanto al argumento 'feo': 'while (feo()) encapsulate_more();' –

Respuesta

121

Hay muchas maneras, a elegir cuál es el más elegante para usted.

lectura en char *:

ifstream file ("file.txt", ios::in|ios::binary|ios::ate); 
if (file.is_open()) 
{ 
    file.seekg(0, ios::end); 
    size = file.tellg(); 
    char *contents = new char [size]; 
    file.seekg (0, ios::beg); 
    file.read (contents, size); 
    file.close(); 
    //... do something with it 
    delete [] contents; 
} 

En std :: string:

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

en el vector < carbón >:

std::ifstream in("file.txt"); 
std::vector<char> contents((std::istreambuf_iterator<char>(in)), 
    std::istreambuf_iterator<char>()); 

En cadena, utilizando stringstream:

std::ifstream in("file.txt"); 
std::stringstream buffer; 
buffer << in.rdbuf(); 
std::string contents(buffer.str()); 

file.txt es solo un ejemplo, todo funciona bien para archivos binarios, solo asegúrate de usar ios :: binary en el constructor ifstream.

+1

Me gusta su respuesta incluso mejor que la mía, que no es algo que diga a menudo. ¡Buen trabajo! +1 –

+7

realmente necesita un conjunto adicional de paréntesis alrededor del primer argumento al constructor de contenidos con istreambuf_iterator <> para evitar que se trate como una declaración de función. –

+0

@Greg: gracias, lo arreglé ahora. –

4

Pareces hablar de la elegancia como propiedad definitiva del "pequeño código". Esto es, por supuesto, subjetivo en cierta medida. Algunos dirían que omitir todo el manejo de errores no es muy elegante. Algunos dirían que el código claro y compacto que entiendes de inmediato es elegante.

Escriba su propia función/método de una sola línea que lea el contenido del archivo, pero hágalo de forma rigurosa y segura debajo de la superficie y habrá cubierto ambos aspectos de la elegancia.

Todo lo mejor

/Robert

+3

Corolario : Elegancia es como lo hace la elegancia; las nociones de código elegante difieren entre idiomas y paradigmas.Lo que un programador de C++ podría considerar elegante podría ser horrible para un programador de Ruby o Python, y viceversa. – Rob

0

Me gusta la forma char * de Milán, pero con std :: cadena.


#include <iostream> 
#include <string> 
#include <fstream> 
#include <cstdlib> 
using namespace std; 

string& getfile(const string& filename, string& buffer) { 
    ifstream in(filename.c_str(), ios_base::binary | ios_base::ate); 
    in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit); 
    buffer.resize(in.tellg()); 
    in.seekg(0, ios_base::beg); 
    in.read(&buffer[0], buffer.size()); 
    return buffer; 
} 

int main(int argc, char* argv[]) { 
    if (argc != 2) { 
     cerr << "Usage: this_executable file_to_read\n"; 
     return EXIT_FAILURE; 
    } 
    string buffer; 
    cout << getfile(argv[1], buffer).size() << "\n"; 
} 

(con o sin el ios_base :: binario, dependiendo de si desea o no nuevas líneas tranlated. También puede cambiar a getfile simplemente devolver una cadena de modo que usted no tiene que pasar una cadena de búfer en . a continuación, prueba para ver si el compilador optimiza la copia a cabo al volver)

Sin embargo, esto podría ser un poco mejor (y ser mucho más lento):.


#include <iostream> 
#include <string> 
#include <fstream> 
#include <cstdlib> 
using namespace std; 

string getfile(const string& filename) { 
    ifstream in(filename.c_str(), ios_base::binary); 
    in.exceptions(ios_base::badbit | ios_base::failbit | ios_base::eofbit); 
    return string(istreambuf_iterator<char>(in), istreambuf_iterator<char>()); 
} 

int main(int argc, char* argv[]) { 
    if (argc != 2) { 
     cerr << "Usage: this_executable file_to_read\n"; 
     return EXIT_FAILURE; 
    } 
    cout << getfile(argv[1]).size() << "\n"; 
} 
10

Hay another thread sobre este tema.

Mis soluciones de este hilo (tanto de una sola línea):

El agradable (véase la segunda solución de Milán):

string str((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>()); 

y el ayuno:

string str(static_cast<stringstream const&>(stringstream() << ifs.rdbuf()).str()); 
+0

en realidad, el primero es más rápido porque opera en el búfer istream directamente, y este último se basa en el primero, pero agrega algunos bits de estado de falla. –

+0

@ t.g. El primero usa una copia muy ineficiente para construir la cadena sin asignación previa, lo que conduce a una gran cantidad de reasignaciones. El segundo preasigna un buffer del tamaño requerido. –

+1

Acabo de probarlo con VC++ 10. De hecho, depende Depende del tamaño del archivo, el primero es más rápido para archivos más pequeños y el segundo es más rápido para los archivos más grandes, lo que parece demostrar lo que usted dijo. –

1

Pero ten en cuenta que un C++ - string (o más concreto: un STL-string) es tan poco como un C-String capaz de contener una cadena de longitud arbitraria - ¡por supuesto que no!

Eche un vistazo al miembro max_size() que le proporciona la cantidad máxima de caracteres que una cadena puede contener. Este es un número de implementación definido y puede no ser portátil entre diferentes plataformas. Visual Studio da un valor de aproximadamente 4gigs para cadenas, otros pueden darte solo 64k y en plataformas de 64 bits, ¡podría darte algo realmente grande! Depende y, por supuesto, normalmente se encontrará con una excepción bad_alloc por agotamiento de memoria mucho antes de alcanzar el límite de 4gig ...

BTW: max_size() también es miembro de otros contenedores STL. Le dará el número máximo de elementos de un tipo determinado (para los que ha instanciado el contenedor) que este contenedor (en teoría) podrá contener.

lo tanto, si usted está leyendo desde un archivo de origen desconocido se debe:
- Compruebe su tamaño y asegurarse de que es más pequeño que max_size()
- captura y proceso bad_alloc-excepciones

y otro point: ¿Por qué le gusta leer el archivo en una cadena? Esperaría procesarlo aún más analizándolo incrementalmente o algo, ¿verdad? Entonces, en lugar de leerlo en una cadena, también podría leerlo en una cadena (que básicamente es solo un poco de azúcar sintáctico para una cadena) y hacer el procesamiento. Pero luego podrías hacer el procesamiento directamente desde el archivo también. Porque si se programa correctamente, la secuencia de cadenas podría ser reemplazada sin problemas por una cadena de archivos, i. mi. por el archivo en sí. ¡O también por cualquier otro flujo de entrada, todos comparten los mismos miembros y operadores y por lo tanto pueden intercambiarse sin problemas!

Y para el procesamiento en sí: ¡también hay mucho que puede haber automatizado el compilador! P.ej. digamos que quieres tokenizar la cadena. Al definir una plantilla adecuada las siguientes acciones:
- Lectura de un archivo (o una cadena o cualquier otro flujo de entrada)
- tokenizing el contenido
- empujar todas las fichas que se encuentran en un STL-contenedor
- ordenar las fichas alfabéticamente
- eleminando cualquier valor doble
pueden lograrse todos (!!) en una sola línea (!) de código C++ (¡dejen de lado la plantilla en sí y el manejo de errores)! ¡Es solo una llamada de la función std :: copy()! Simplemente google para "token iterator" y obtendrás una idea de lo que quiero decir. Así que esto me parece ser aún más "elegante" que solo leer un archivo ...

+0

De nota, 'max_size()' se define en relación con el tamaño de 'size_t', que es relativo al tamaño de bit de su plataforma.Se define de esta manera para permitir que una cadena sea tan grande como su plataforma pueda abordar. –

Cuestiones relacionadas