Buscar..


Introducción

El archivo de C ++ I / O se realiza a través de secuencias . Las abstracciones clave son:

std::istream para leer texto.

std::ostream para escribir texto.

std::streambuf para leer o escribir personajes.

La entrada formateada utiliza el operator>> .

La salida formateada utiliza el operator<< .

Las secuencias utilizan std::locale , por ejemplo, para detalles del formato y para la traducción entre las codificaciones externas y la codificación interna.

Más sobre streams: <iostream> Library

Abriendo un archivo

La apertura de un archivo se realiza de la misma manera para las 3 secuencias de archivos ( ifstream , ofstream y fstream ).

Puedes abrir el archivo directamente en el constructor:

std::ifstream ifs("foo.txt");  // ifstream: Opens file "foo.txt" for reading only.

std::ofstream ofs("foo.txt");  // ofstream: Opens file "foo.txt" for writing only.

std::fstream iofs("foo.txt");  // fstream:  Opens file "foo.txt" for reading and writing.

Alternativamente, puede usar la función de miembro open() stream open() :

std::ifstream ifs;
ifs.open("bar.txt");           // ifstream: Opens file "bar.txt" for reading only.

std::ofstream ofs;
ofs.open("bar.txt");           // ofstream: Opens file "bar.txt" for writing only.

std::fstream iofs;
iofs.open("bar.txt");          // fstream:  Opens file "bar.txt" for reading and writing.

Usted debe comprobar siempre si un archivo ha sido abierto con éxito (incluso cuando se escribe). Las fallas pueden incluir: el archivo no existe, el archivo no tiene los derechos de acceso correctos, el archivo ya está en uso, se produjeron errores en el disco, la unidad se desconectó ... La verificación se puede hacer de la siguiente manera:

// Try to read the file 'foo.txt'.
std::ifstream ifs("fooo.txt");  // Note the typo; the file can't be opened.

// Check if the file has been opened successfully.
if (!ifs.is_open()) {
    // The file hasn't been opened; take appropriate actions here.
    throw CustomException(ifs, "File could not be opened");
}

Cuando la ruta del archivo contenga barras diagonales inversas (por ejemplo, en el sistema Windows), debe eliminarlas correctamente:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:\\folder\\foo.txt"); // using escaped backslashes
C ++ 11

o usar el literal en bruto:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs(R"(c:\folder\foo.txt)"); // using raw literal

o use barras diagonales en su lugar:

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:/folder/foo.txt");
C ++ 11

Si desea abrir un archivo con caracteres no ASCII en la ruta en Windows actualmente, puede usar el argumento de ruta de caracteres anchos no estándar :

// Open the file 'пример\foo.txt' on Windows.
std::ifstream ifs(LR"(пример\foo.txt)"); // using wide characters with raw literal

Leyendo de un archivo

Hay varias formas de leer datos de un archivo.

Si sabe cómo se formatean los datos, puede usar el operador de extracción de flujo ( >> ). Supongamos que tiene un archivo llamado foo.txt que contiene los siguientes datos:

John Doe 25 4 6 1987
Jane Doe 15 5 24 1976

Luego puede usar el siguiente código para leer los datos del archivo:

// Define variables.
std::ifstream is("foo.txt");
std::string firstname, lastname;
int age, bmonth, bday, byear;

// Extract firstname, lastname, age, bday month, bday day, and bday year in that order.
// Note: '>>' returns false if it reached EOF (end of file) or if the input data doesn't
// correspond to the type of the input variable (for example, the string "foo" can't be
// extracted into an 'int' variable).
while (is >> firstname >> lastname >> age >> bmonth >> bday >> byear)
    // Process the data that has been read.

El operador de extracción de flujo >> extrae cada carácter y se detiene si encuentra un carácter que no se puede almacenar o si es un carácter especial:

  • Para los tipos de cadena, el operador se detiene en un espacio en blanco ( ) o en una nueva línea ( \n ).
  • Para los números, el operador se detiene en un carácter no numérico.

Esto significa que la siguiente versión del archivo foo.txt también se leerá con éxito en el código anterior:

John 
Doe 25
4 6 1987


Jane
Doe 
15 5
24
1976

El operador de extracción de flujo >> siempre devuelve el flujo dado a él. Por lo tanto, se pueden encadenar varios operadores para leer datos consecutivamente. Sin embargo, una corriente también se puede utilizar como una expresión booleana (como se muestra en el while bucle en el código anterior). Esto se debe a que las clases de flujo tienen un operador de conversión para el tipo bool . Este operador bool() devolverá true siempre que la secuencia no tenga errores. Si un flujo entra en un estado de error (por ejemplo, porque no se pueden extraer más datos), el operador bool() devolverá el valor false . Por lo tanto, el while de bucle en el código anterior se saldrá después del archivo de entrada se ha leído hasta el final.

Si desea leer un archivo completo como una cadena, puede usar el siguiente código:

// Opens 'foo.txt'.
std::ifstream is("foo.txt");
std::string whole_file;

// Sets position to the end of the file.
is.seekg(0, std::ios::end);

// Reserves memory for the file.
whole_file.reserve(is.tellg());

// Sets position to the start of the file.
is.seekg(0, std::ios::beg);

// Sets contents of 'whole_file' to all characters in the file.
whole_file.assign(std::istreambuf_iterator<char>(is),
  std::istreambuf_iterator<char>());

Este código reserva espacio para la string con el fin de reducir las asignaciones de memoria innecesarias.

Si desea leer un archivo línea por línea, puede usar la función getline() :

std::ifstream is("foo.txt");   

// The function getline returns false if there are no more lines.
for (std::string str; std::getline(is, str);) {
    // Process the line that has been read.
}

Si desea leer un número fijo de caracteres, puede usar la función miembro de la secuencia read() :

std::ifstream is("foo.txt");
char str[4];

// Read 4 characters from the file.
is.read(str, 4);

Después de ejecutar un comando de lectura, siempre debe verificar si el indicador de estado de error failbit se ha establecido, ya que indica si la operación falló o no. Esto se puede hacer llamando a la función miembro de la secuencia de archivos fail() :

is.read(str, 4); // This operation might fail for any reason.

if (is.fail())
    // Failed to read!

Escribiendo en un archivo

Hay varias formas de escribir en un archivo. La forma más sencilla es utilizar una secuencia de archivo de salida ( ofstream ) junto con el operador de inserción de secuencia ( << ):

std::ofstream os("foo.txt");
if(os.is_open()){
    os << "Hello World!";
}

En lugar de << , también puede usar la función miembro write() la secuencia del archivo de salida:

std::ofstream os("foo.txt");
if(os.is_open()){
    char data[] = "Foo";

    // Writes 3 characters from data -> "Foo".
    os.write(data, 3);
}

Después de escribir en un flujo, siempre debe verificar si se ha establecido el indicador de estado de error badbit , ya que indica si la operación falló o no. Esto se puede hacer llamando a la función miembro del flujo de salida del archivo bad() :

os << "Hello Badbit!"; // This operation might fail for any reason.
if (os.bad())
    // Failed to write!

Modos de apertura

Al crear una secuencia de archivos, puede especificar un modo de apertura. Un modo de apertura es básicamente una configuración para controlar cómo la secuencia abre el archivo.

(Todos los modos se pueden encontrar en el std::ios nombres std::ios ).

Se puede proporcionar un modo de apertura como segundo parámetro para el constructor de un flujo de archivos o para su función miembro open() :

std::ofstream os("foo.txt", std::ios::out | std::ios::trunc);

std::ifstream is;
is.open("foo.txt", std::ios::in | std::ios::binary);

Se debe tener en cuenta que debe configurar ios::in o ios::out si desea establecer otros indicadores, ya que no están establecidos de forma implícita por los miembros de iostream aunque tengan un valor predeterminado correcto.

Si no especifica un modo de apertura, se utilizan los siguientes modos predeterminados:

  • ifstream - in
  • ofstream - out
  • fstream - in y out

Los modos de apertura de archivos que puede especificar por diseño son:

Modo Sentido por Descripción
app adjuntar Salida Anexa datos al final del archivo.
binary binario De entrada y salida La entrada y salida se realiza en binario.
in entrada Entrada Abre el archivo para su lectura.
out salida Salida Abre el archivo para escribir.
trunc truncar De entrada y salida Elimina el contenido del archivo al abrirlo.
ate al final Entrada Va al final del archivo al abrir.

Nota: la configuración del modo binary permite que los datos se lean / escriban exactamente como están; no configurarlo permite la traducción del carácter '\n' nueva línea '\n' a / desde una secuencia de final de línea específica de la plataforma.

Por ejemplo, en Windows, la secuencia de final de línea es CRLF ( "\r\n" ).
Escribe: "\n" => "\r\n"
Lee: "\r\n" => "\n"

Cerrando un archivo

Cerrar un archivo explícitamente rara vez es necesario en C ++, ya que una secuencia de archivos cerrará automáticamente su archivo asociado en su destructor. Sin embargo, debe intentar limitar la vida útil de un objeto de flujo de archivos, de modo que no mantenga abierto el manejador de archivos más de lo necesario. Por ejemplo, esto se puede hacer poniendo todas las operaciones de archivo en un ámbito propio ( {} ):

std::string const prepared_data = prepare_data();
{
    // Open a file for writing.
    std::ofstream output("foo.txt");

    // Write data.
    output << prepared_data;
}  // The ofstream will go out of scope here.
   // Its destructor will take care of closing the file properly.

Llamar a close() explícitamente solo es necesario si desea reutilizar el mismo objeto fstream más tarde, pero no desea mantener el archivo abierto entre:

// Open the file "foo.txt" for the first time.
std::ofstream output("foo.txt");

// Get some data to write from somewhere.
std::string const prepared_data = prepare_data();

// Write data to the file "foo.txt".
output << prepared_data;

// Close the file "foo.txt".
output.close();

// Preparing data might take a long time. Therefore, we don't open the output file stream
// before we actually can write some data to it.
std::string const more_prepared_data = prepare_complex_data();

// Open the file "foo.txt" for the second time once we are ready for writing.
output.open("foo.txt");

// Write the data to the file "foo.txt".
output << more_prepared_data;

// Close the file "foo.txt" once again.
output.close();

Flushing un arroyo

Las secuencias de archivos se almacenan en búfer de forma predeterminada, al igual que muchos otros tipos de secuencias. Esto significa que las escrituras en la secuencia pueden no causar que el archivo subyacente cambie inmediatamente. A fin de forzar que todas las escrituras en búfer se realicen inmediatamente, puede vaciar la secuencia. Puede hacerlo directamente invocando el método flush() o mediante el manipulador de flujo std::flush :

std::ofstream os("foo.txt");
os << "Hello World!" << std::flush;

char data[3] = "Foo";
os.write(data, 3);
os.flush();

Hay un manipulador de flujo std::endl que combina la escritura de una nueva línea con la descarga de flujo:

// Both following lines do the same thing
os << "Hello World!\n" << std::flush;
os << "Hello world!" << std::endl;

El almacenamiento en búfer puede mejorar el rendimiento de la escritura en una secuencia. Por lo tanto, las aplicaciones que escriben mucho deben evitar el lavado innecesario. Por el contrario, si la E / S se realiza con poca frecuencia, las aplicaciones deben considerar vaciar con frecuencia para evitar que los datos se atasquen en el objeto de flujo.

Leyendo un archivo ASCII en un std :: string

std::ifstream f("file.txt");

if (f)
{
  std::stringstream buffer;
  buffer << f.rdbuf();
  f.close();

  // The content of "file.txt" is available in the string `buffer.str()`
}

El método rdbuf() devuelve un puntero a un streambuf que puede streambuf en el buffer través de la función miembro stringstream::operator<< .


Otra posibilidad (popularizada en Effective STL por Scott Meyers ) es:

std::ifstream f("file.txt");

if (f)
{
  std::string str((std::istreambuf_iterator<char>(f)),
                  std::istreambuf_iterator<char>());

  // Operations on `str`...
}

Esto es bueno porque requiere poco código (y permite leer un archivo directamente en cualquier contenedor STL, no solo cadenas) pero puede ser lento para archivos grandes.

NOTA : los paréntesis adicionales alrededor del primer argumento del constructor de cadenas son esenciales para evitar el problema de análisis más desconcertante .


Por último, si bien no menos importante:

std::ifstream f("file.txt");

if (f)
{
  f.seekg(0, std::ios::end);
  const auto size = f.tellg();

  std::string str(size, ' ');
  f.seekg(0);
  f.read(&str[0], size); 
  f.close();

  // Operations on `str`...
}

que es probablemente la opción más rápida (entre las tres propuestas).

Leyendo un archivo en un contenedor

En el siguiente ejemplo, usamos std::string y operator>> para leer los elementos del archivo.

    std::ifstream file("file3.txt");

    std::vector<std::string>  v;

    std::string s;
    while(file >> s) // keep reading until we run out
    {
        v.push_back(s);
    }

En el ejemplo anterior, simplemente estamos iterando a través del archivo leyendo un "elemento" a la vez utilizando el operator>> . Este mismo efecto se puede lograr utilizando el std::istream_iterator que es un iterador de entrada que lee un "elemento" a la vez desde el flujo. Además, la mayoría de los contenedores se pueden construir utilizando dos iteradores, por lo que podemos simplificar el código anterior para:

    std::ifstream file("file3.txt");

    std::vector<std::string>  v(std::istream_iterator<std::string>{file},
                                std::istream_iterator<std::string>{});

Podemos extender esto para leer cualquier tipo de objeto que nos guste simplemente especificando el objeto que queremos leer como el parámetro de plantilla para std::istream_iterator . Por lo tanto, podemos simplemente extender lo anterior para leer líneas (en lugar de palabras) como esta:

// Unfortunately there is  no built in type that reads line using >>
// So here we build a simple helper class to do it. That will convert
// back to a string when used in string context.
struct Line
{
    // Store data here
    std::string data;
    // Convert object to string
    operator std::string const&() const {return data;}
    // Read a line from a stream.
    friend std::istream& operator>>(std::istream& stream, Line& line)
    {
        return std::getline(stream, line.data);
    }
};


    std::ifstream file("file3.txt");

    // Read the lines of a file into a container.
    std::vector<std::string>  v(std::istream_iterator<Line>{file},
                                std::istream_iterator<Line>{});

Leyendo un `struct` desde un archivo de texto formateado.

C ++ 11
struct info_type
{
    std::string name;
    int age;
    float height;
    
    // we define an overload of operator>> as a friend function which
    // gives in privileged access to private data members 
    friend std::istream& operator>>(std::istream& is, info_type& info)
    {
        // skip whitespace
        is >> std::ws;
        std::getline(is, info.name);
        is >> info.age;
        is >> info.height;
        return is;
    }
};

void func4()
{
    auto file = std::ifstream("file4.txt");

    std::vector<info_type> v;

    for(info_type info; file >> info;) // keep reading until we run out
    {
        // we only get here if the read succeeded
        v.push_back(info);
    }

    for(auto const& info: v)
    {
        std::cout << "  name: " << info.name << '\n';
        std::cout << "   age: " << info.age << " years" << '\n';
        std::cout << "height: " << info.height << "lbs" << '\n';
        std::cout << '\n';
    }
}

file4.txt

Wogger Wabbit
2
6.2
Bilbo Baggins
111
81.3
Mary Poppins
29
154.8

Salida:

name: Wogger Wabbit
 age: 2 years
height: 6.2lbs

name: Bilbo Baggins
 age: 111 years
height: 81.3lbs

name: Mary Poppins
 age: 29 years
height: 154.8lbs

Copiando un archivo

std::ifstream  src("source_filename", std::ios::binary);
std::ofstream  dst("dest_filename",   std::ios::binary);
dst << src.rdbuf();
C ++ 17

Con C ++ 17, la forma estándar de copiar un archivo es incluir el encabezado <filesystem> y usar copy_file :

std::fileystem::copy_file("source_filename", "dest_filename");

La biblioteca del sistema de archivos se desarrolló originalmente como boost.filesystem y finalmente se fusionó con ISO C ++ a partir de C ++ 17.

¿Revisar el final del archivo dentro de una condición de bucle, mala práctica?

eof devuelve true solo después de leer el final del archivo. NO indica que la próxima lectura será el final de la transmisión.

while (!f.eof())
{
  // Everything is OK

  f >> buffer;

  // What if *only* now the eof / fail bit is set?

  /* Use `buffer` */
}

Podrías escribir correctamente:

while (!f.eof()) 
{  
  f >> buffer >> std::ws;

  if (f.fail())
    break;

  /* Use `buffer` */
}

pero

while (f >> buffer)
{
  /* Use `buffer` */
}

Es más sencillo y menos propenso a errores.

Otras referencias:

  • std::ws : descarta los espacios en blanco iniciales de un flujo de entrada
  • std::basic_ios::fail : devuelve true si se ha producido un error en la secuencia asociada

Escribir archivos con configuraciones locales no estándar

Si necesita escribir un archivo con una configuración regional diferente a la predeterminada, puede usar std::locale y std::basic_ios::imbue() para hacer eso para una secuencia de archivos específica:

Guía de uso:

  • Siempre debe aplicar un local a una secuencia antes de abrir el archivo.
  • Una vez que se haya imbuido el flujo, no debe cambiar la configuración regional.

Razones para las restricciones: Imbuir una secuencia de archivos con una configuración regional tiene un comportamiento indefinido si la configuración regional actual no es independiente del estado o no apunta al principio del archivo.

Las transmisiones UTF-8 (y otras) no son independientes del estado. Además, una secuencia de archivos con una configuración regional UTF-8 puede intentar leer el marcador de la lista de materiales del archivo cuando se abre; así que solo abrir el archivo puede leer los caracteres del archivo y no estará al principio.

#include <iostream>
#include <fstream>
#include <locale>

int main()
{
  std::cout << "User-preferred locale setting is "
            << std::locale("").name().c_str() << std::endl;

  // Write a floating-point value using the user's preferred locale.
  std::ofstream ofs1;
  ofs1.imbue(std::locale(""));
  ofs1.open("file1.txt");
  ofs1 << 78123.456 << std::endl;

  // Use a specific locale (names are system-dependent)
  std::ofstream ofs2;
  ofs2.imbue(std::locale("en_US.UTF-8"));
  ofs2.open("file2.txt");
  ofs2 << 78123.456 << std::endl;

  // Switch to the classic "C" locale
  std::ofstream ofs3;
  ofs3.imbue(std::locale::classic());
  ofs3.open("file3.txt");
  ofs3 << 78123.456 << std::endl;
}

El cambio explícito a la configuración regional clásica "C" es útil si su programa utiliza una configuración regional predeterminada diferente y desea garantizar un estándar fijo para leer y escribir archivos. Con una configuración regional preferida de "C", el ejemplo escribe

78,123.456
78,123.456
78123.456

Si, por ejemplo, el entorno local preferido es alemán y, por lo tanto, utiliza un formato de número diferente, el ejemplo escribe

78 123,456
78,123.456
78123.456

(note la coma decimal en la primera línea).



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow