HOWTO · C++
Leer archivo en cadena en C++
Este artículo demuestra cómo leer un archivo en una cadena en C++
En esta página
Para leer un archivo en un std::string, usa std::ifstream con std::getline si quieres procesarlo línea por línea, o construye la cadena con std::istreambuf_iterator<char> si necesitas leer el flujo completo. Los ejemplos siguientes crean su propio archivo temporal de entrada, por lo que cada programa se puede compilar y ejecutar de forma independiente.
Usa std::ifstream y std::getline para leer un archivo en una cadena en C++
std::getline elimina el delimitador de cada línea. Añadir "\n" después de cada llamada exitosa restablece los separadores, pero también agrega un salto de línea después de la última línea, incluso si el archivo de origen no terminaba con uno. Si esa diferencia importa, registra si había un delimitador o usa uno de los métodos siguientes para leer el flujo completo.
Este ejemplo completo escribe dos líneas en un archivo temporal, lo abre con std::ifstream y añade cada línea a una cadena. El texto anterior al ejemplo describe su uso habitual cuando todo funciona correctamente.
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-lines.txt";
{
std::ofstream sample(path);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
std::ifstream inputFile(path);
if (!inputFile) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
std::string fileContent;
std::string line;
while (std::getline(inputFile, line)) {
fileContent += line + "\n";
}
if (inputFile.bad()) {
std::cout << "Read failed\n";
inputFile.close();
std::filesystem::remove(path);
return 0;
}
std::cout << fileContent;
inputFile.close();
std::filesystem::remove(path);
}
El bucle termina cuando std::getline ya no puede extraer otra línea. Comprobar bad() permite distinguir un error de E/S de bajo nivel del fin normal del flujo. De forma predeterminada, el archivo se abre en modo texto; en los sistemas que traducen los finales de línea, los bytes de la cadena resultante pueden diferir de los almacenados en el archivo. Usa el modo binario si necesitas conservar los bytes exactamente.
Usa istreambuf_iterator para leer un archivo en una cadena en C++
Para leer el flujo completo, construye una cadena a partir de un par de valores std::istreambuf_iterator<char>. El primer iterador lee del búfer del flujo y el iterador construido por defecto marca el final. Así se leen los datos sin dividirlos en líneas y se conservan los caracteres de salto de línea que entrega el flujo.
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-iterator.txt";
{
std::ofstream sample(path, std::ios::binary);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
std::ifstream inputFile(path, std::ios::binary);
if (!inputFile) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
const std::string fileContent{
std::istreambuf_iterator<char>(inputFile),
std::istreambuf_iterator<char>()};
if (inputFile.bad()) {
std::cout << "Read failed\n";
} else {
std::cout << fileContent << '\n';
}
inputFile.close();
std::filesystem::remove(path);
}
alpha
beta
Usa esta forma cuando el archivo completo quepa en memoria y no sea necesario procesarlo línea por línea. Abrirlo en modo binario evita la conversión de finales de línea propia del modo texto. Un std::string puede contener bytes nulos, pero las funciones que tratan c_str() como una cadena de texto terminada en nulo pueden detenerse en el primero.
Usa rdbuf para leer un archivo en una cadena en C++
Otra opción de la biblioteca estándar consiste en insertar el rdbuf() del flujo de entrada en un std::ostringstream y, luego, llamar a str() para obtener su contenido como std::string. Resulta práctico cuando el flujo de cadena ya forma parte del código circundante. Comprueba el flujo de entrada después de la transferencia si necesitas informar de los errores de lectura.
#include <filesystem>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-rdbuf.txt";
{
std::ofstream sample(path, std::ios::binary);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
std::ifstream inputFile(path, std::ios::binary);
if (!inputFile) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
std::ostringstream stream;
stream << inputFile.rdbuf();
if (inputFile.bad()) {
std::cout << "Read failed\n";
} else {
const std::string fileContent = stream.str();
std::cout << fileContent << '\n';
}
inputFile.close();
std::filesystem::remove(path);
}
La inserción copia los caracteres del búfer del archivo al flujo de cadena; str() devuelve la cadena acumulada. Para construir directamente una cadena a partir del archivo completo, el método del iterador requiere menos pasos. En ambos casos hay que comprobar si el archivo se abrió y si se produjo un error de lectura.
Usa la función fread para leer un archivo en una cadena
fread es una función de la biblioteca estándar de C que también se puede usar en C++. Es útil cuando el código ya utiliza flujos FILE* o necesita una API de C orientada a bytes. No es intrínsecamente más rápida que las opciones de flujo de C++; el rendimiento depende de la carga de trabajo y de la implementación.
Este ejemplo usa fseek y ftell para calcular el tamaño de un archivo de muestra que permite búsquedas, reserva una cadena de ese tamaño y lee los bytes en ella. Para flujos que no permiten búsquedas o archivos demasiado grandes para la memoria, lee bloques de tamaño fijo. El tamaño del archivo puede cambiar entre la medición y la lectura, así que también hay que comprobar la cantidad de bytes devuelta y ferror.
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-fread.txt";
{
std::ofstream sample(path, std::ios::binary);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
std::FILE* file = std::fopen(path.string().c_str(), "rb");
if (file == nullptr) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
if (std::fseek(file, 0, SEEK_END) != 0) {
std::cout << "Could not seek sample file\n";
std::fclose(file);
std::filesystem::remove(path);
return 0;
}
const long length = std::ftell(file);
if (length < 0 || std::fseek(file, 0, SEEK_SET) != 0) {
std::cout << "Could not determine sample size\n";
std::fclose(file);
std::filesystem::remove(path);
return 0;
}
std::string content(static_cast<std::size_t>(length), '\0');
const std::size_t bytesRead = std::fread(content.data(), 1, content.size(), file);
const bool failed = std::ferror(file) != 0 || bytesRead != content.size();
std::fclose(file);
std::filesystem::remove(path);
if (failed) {
std::cout << "Read failed or file changed while reading\n";
return 0;
}
std::cout << content << '\n';
}
El tercer argumento indica la cantidad de elementos y el segundo, el tamaño de cada elemento; usar 1 como tamaño del elemento hace que el valor devuelto sea una cantidad de bytes. fread puede devolver menos bytes de los solicitados, así que no des por hecho que una llamada llena el búfer. Este ejemplo presupone un archivo que permite búsquedas y usa el rango long admitido por ftell; para archivos más grandes, las aplicaciones deben usar las API para archivos grandes adecuadas a la plataforma o leer por bloques. La cadena almacena bytes y no añade un terminador nulo para las funciones de cadenas de C.
Usa read para leer un archivo en una cadena
La función read de POSIX trabaja con un descriptor de archivo y un búfer proporcionado por quien la llama. No forma parte del C++ estándar; incluye los encabezados POSIX correspondientes y usa este método solo en plataformas que ofrecen esa API. Una llamada exitosa puede devolver menos bytes de los solicitados, así que repite las llamadas hasta llegar al fin del archivo y gestiona las interrupciones y los errores.
El siguiente programa, específico de POSIX, lee por bloques en vez de depender de una medición previa del tamaño del archivo. Sus llamadas al sistema no están disponibles en todas las plataformas de C++, por lo que debe considerarse un ejemplo para POSIX, no una solución portable en C++.
#include <cerrno>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <fcntl.h>
#include <unistd.h>
int main() {
const auto path = std::filesystem::temp_directory_path() / "cpp-string-read.txt";
{
std::ofstream sample(path, std::ios::binary);
sample << "alpha\nbeta";
if (!sample) {
std::cout << "Could not create sample file\n";
return 0;
}
}
const int fd = ::open(path.c_str(), O_RDONLY);
if (fd < 0) {
std::cout << "Could not open sample file\n";
std::filesystem::remove(path);
return 0;
}
std::string content;
char buffer[4096];
bool failed = false;
for (;;) {
const ssize_t count = ::read(fd, buffer, sizeof buffer);
if (count > 0) {
content.append(buffer, static_cast<std::size_t>(count));
} else if (count == 0) {
break;
} else if (errno != EINTR) {
failed = true;
break;
}
}
if (::close(fd) != 0) {
failed = true;
}
std::filesystem::remove(path);
if (failed) {
std::cout << "Read or close failed\n";
return 0;
}
std::cout << content << '\n';
}
En un archivo regular, read devuelve cero al llegar al final. Con otros tipos de descriptor, el comportamiento depende del dispositivo o del flujo. Cada valor de retorno positivo indica cuántos bytes se deben añadir; un valor negativo señala un error, y EINTR significa que se puede volver a intentar la llamada. Cierra siempre el descriptor y contempla las lecturas parciales: no supongas que una sola llamada llena el búfer solicitado. El ejemplo usa std::string::append para conservar la cantidad exacta de bytes, incluidos los bytes nulos.
Conclusión
En C++ portable, elige std::istreambuf_iterator o rdbuf() cuando quieras convertir el flujo completo en una cadena, y usa std::getline cuando te resulte útil procesar el archivo línea por línea. Recuerda que la extracción de líneas elimina los delimitadores y que volver a añadirlos puede cambiar una última línea sin terminador. Usa fread cuando la interfaz FILE* de C encaje con el código circundante, y recurre a read de POSIX solo cuando corresponda usar su API de descriptores específica de la plataforma. Para archivos grandes, considera procesar bloques en lugar de guardar todo el archivo en memoria, y comprueba tanto el resultado de apertura como el de lectura.