Lectura y escritura de archivos de texto
El manejo de archivos de texto es una operación fundamental en el desarrollo de aplicaciones, permitiendo almacenar datos de manera persistente, crear registros de actividad, procesar documentos y intercambiar información entre sistemas. C# proporciona una amplia gama de clases y métodos en el espacio de nombres System.IO
que facilitan estas operaciones de forma segura y eficiente.
La capacidad de leer y escribir archivos abre un mundo de posibilidades para nuestras aplicaciones: desde simples logs de depuración hasta complejos sistemas de procesamiento de datos. Dominar estas técnicas es esencial para cualquier desarrollador que quiera crear aplicaciones robustas y funcionales.
Fundamentos del sistema de archivos en .NET
Espacio de nombres System.IO
El espacio de nombres System.IO contiene las clases principales para operaciones de entrada/salida en .NET. Estas clases proporcionan funcionalidades para trabajar con archivos, directorios, rutas y flujos de datos.
Clase principal | Propósito |
---|---|
File | Métodos estáticos para operaciones básicas con archivos |
FileInfo | Información y operaciones sobre archivos específicos |
Directory | Métodos estáticos para operaciones con directorios |
DirectoryInfo | Información y operaciones sobre directorios específicos |
Path | Utilidades para manipular rutas de archivos |
StreamReader | Lectura de archivos de texto de forma eficiente |
StreamWriter | Escritura de archivos de texto de forma eficiente |
Conceptos básicos de rutas
using System;
using System.IO;
// Trabajar con rutas de archivos de forma segura
string rutaCompleta = Path.Combine("C:", "datos", "archivo.txt");
string nombreArchivo = Path.GetFileName(rutaCompleta);
string extension = Path.GetExtension(rutaCompleta);
string directorio = Path.GetDirectoryName(rutaCompleta);
string sinExtension = Path.GetFileNameWithoutExtension(rutaCompleta);
Console.WriteLine($"Ruta completa: {rutaCompleta}");
Console.WriteLine($"Nombre: {nombreArchivo}");
Console.WriteLine($"Extensión: {extension}");
Console.WriteLine($"Directorio: {directorio}");
Console.WriteLine($"Sin extensión: {sinExtension}");
// Obtener directorios especiales del sistema
string documentos = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string escritorio = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string temporal = Path.GetTempPath();
Console.WriteLine($"Documentos: {documentos}");
Console.WriteLine($"Escritorio: {escritorio}");
Console.WriteLine($"Temporal: {temporal}");
Operaciones básicas con archivos
Verificación de existencia
Antes de realizar operaciones con archivos, es importante verificar su existencia para evitar errores:
using System;
using System.IO;
string rutaArchivo = @"C:\temp\mi_archivo.txt";
string rutaDirectorio = @"C:\temp";
// Verificar existencia de archivo
if (File.Exists(rutaArchivo))
{
Console.WriteLine("El archivo existe");
// Obtener información básica
var info = new FileInfo(rutaArchivo);
Console.WriteLine($"Tamaño: {info.Length} bytes");
Console.WriteLine($"Creado: {info.CreationTime}");
Console.WriteLine($"Modificado: {info.LastWriteTime}");
}
else
{
Console.WriteLine("El archivo no existe");
}
// Verificar existencia de directorio
if (Directory.Exists(rutaDirectorio))
{
Console.WriteLine("El directorio existe");
}
else
{
Console.WriteLine("El directorio no existe");
// Crear directorio si no existe
Directory.CreateDirectory(rutaDirectorio);
Console.WriteLine("Directorio creado");
}
Información de archivos y directorios
using System;
using System.IO;
string rutaDirectorio = @"C:\temp";
// Obtener archivos en un directorio
string[] archivos = Directory.GetFiles(rutaDirectorio);
Console.WriteLine($"Archivos en {rutaDirectorio}:");
foreach (string archivo in archivos)
{
Console.WriteLine($" - {Path.GetFileName(archivo)}");
}
// Obtener archivos con filtro
string[] archivosTxt = Directory.GetFiles(rutaDirectorio, "*.txt");
Console.WriteLine($"\nArchivos .txt:");
foreach (string archivo in archivosTxt)
{
var info = new FileInfo(archivo);
Console.WriteLine($" - {info.Name} ({info.Length} bytes)");
}
// Obtener subdirectorios
string[] directorios = Directory.GetDirectories(rutaDirectorio);
Console.WriteLine($"\nSubdirectorios:");
foreach (string dir in directorios)
{
Console.WriteLine($" - {Path.GetFileName(dir)}");
}
Lectura de archivos de texto
Método File.ReadAllText
La forma más simple de leer todo el contenido de un archivo de texto:
using System;
using System.IO;
try
{
// Leer todo el contenido del archivo
string rutaArchivo = @"C:\temp\notas.txt";
string contenido = File.ReadAllText(rutaArchivo);
Console.WriteLine("Contenido del archivo:");
Console.WriteLine(contenido);
// Información adicional
Console.WriteLine($"\nLongitud: {contenido.Length} caracteres");
Console.WriteLine($"Líneas aproximadas: {contenido.Split('\n').Length}");
}
catch (FileNotFoundException)
{
Console.WriteLine("El archivo no se encontró");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("No tienes permisos para leer el archivo");
}
catch (Exception ex)
{
Console.WriteLine($"Error al leer el archivo: {ex.Message}");
}
Método File.ReadAllLines
Para leer un archivo línea por línea en un array:
using System;
using System.IO;
try
{
string rutaArchivo = @"C:\temp\lista_tareas.txt";
string[] lineas = File.ReadAllLines(rutaArchivo);
Console.WriteLine($"El archivo tiene {lineas.Length} líneas:");
for (int i = 0; i < lineas.Length; i++)
{
Console.WriteLine($"{i + 1:D2}: {lineas[i]}");
}
// Procesar líneas que no estén vacías
var lineasNoVacias = lineas.Where(linea => !string.IsNullOrWhiteSpace(linea));
Console.WriteLine($"\nLíneas con contenido: {lineasNoVacias.Count()}");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
StreamReader para archivos grandes
Para archivos grandes o cuando necesitas más control sobre el proceso de lectura:
using System;
using System.IO;
string rutaArchivo = @"C:\temp\archivo_grande.txt";
try
{
using (var reader = new StreamReader(rutaArchivo))
{
int numeroLinea = 1;
string linea;
Console.WriteLine("Leyendo archivo línea por línea:");
// Leer línea por línea hasta el final del archivo
while ((linea = reader.ReadLine()) != null)
{
// Procesar cada línea individualmente
if (!string.IsNullOrWhiteSpace(linea))
{
Console.WriteLine($"Línea {numeroLinea}: {linea.Trim()}");
}
numeroLinea++;
// Opcional: procesar solo las primeras 10 líneas
if (numeroLinea > 10)
{
Console.WriteLine("... (resto del archivo omitido)");
break;
}
}
Console.WriteLine($"\nArchivo procesado. Total de líneas leídas: {numeroLinea - 1}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error al leer el archivo: {ex.Message}");
}
Escritura de archivos de texto
Método File.WriteAllText
Para escribir texto completo en un archivo (sobrescribe el contenido existente):
using System;
using System.IO;
try
{
string rutaArchivo = @"C:\temp\mi_nota.txt";
string contenido = @"Esta es mi primera nota.
Creada desde C#.
Con múltiples líneas de texto.";
// Escribir contenido al archivo
File.WriteAllText(rutaArchivo, contenido);
Console.WriteLine($"Archivo creado exitosamente: {rutaArchivo}");
Console.WriteLine($"Contenido escrito:\n{contenido}");
// Verificar que se escribió correctamente
string contenidoLeido = File.ReadAllText(rutaArchivo);
Console.WriteLine($"\nVerificación - contenido leído:");
Console.WriteLine(contenidoLeido);
}
catch (Exception ex)
{
Console.WriteLine($"Error al escribir el archivo: {ex.Message}");
}
Método File.WriteAllLines
Para escribir múltiples líneas desde un array o colección:
using System;
using System.IO;
using System.Collections.Generic;
try
{
string rutaArchivo = @"C:\temp\lista_compras.txt";
// Lista de elementos para escribir
var listaCompras = new List<string>
{
"Leche",
"Pan integral",
"Manzanas",
"Queso manchego",
"Huevos frescos",
"Aceite de oliva"
};
// Escribir todas las líneas
File.WriteAllLines(rutaArchivo, listaCompras);
Console.WriteLine($"Lista de compras guardada en: {rutaArchivo}");
Console.WriteLine("Contenido:");
// Leer y mostrar el contenido
string[] lineasLeidas = File.ReadAllLines(rutaArchivo);
for (int i = 0; i < lineasLeidas.Length; i++)
{
Console.WriteLine($"{i + 1}. {lineasLeidas[i]}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
StreamWriter para control avanzado
Para escritura con mayor control y eficiencia:
using System;
using System.IO;
string rutaArchivo = @"C:\temp\registro_actividad.txt";
try
{
using (var writer = new StreamWriter(rutaArchivo))
{
// Escribir encabezado
writer.WriteLine("=== REGISTRO DE ACTIVIDAD ===");
writer.WriteLine($"Fecha: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
writer.WriteLine("================================");
writer.WriteLine();
// Simular actividades
for (int i = 1; i <= 5; i++)
{
writer.WriteLine($"Actividad {i}: Procesando elemento {i}");
writer.WriteLine($" Tiempo: {DateTime.Now:HH:mm:ss}");
writer.WriteLine($" Estado: Completado");
writer.WriteLine();
// Forzar escritura al archivo (flush)
writer.Flush();
// Simular trabajo
System.Threading.Thread.Sleep(100);
}
writer.WriteLine("=== FIN DEL REGISTRO ===");
}
Console.WriteLine($"Registro creado: {rutaArchivo}");
// Mostrar el contenido creado
Console.WriteLine("\nContenido del registro:");
string contenido = File.ReadAllText(rutaArchivo);
Console.WriteLine(contenido);
}
catch (Exception ex)
{
Console.WriteLine($"Error al crear el registro: {ex.Message}");
}
Añadir contenido a archivos existentes
File.AppendAllText
Para añadir contenido al final de un archivo existente:
using System;
using System.IO;
string rutaArchivo = @"C:\temp\log_aplicacion.txt";
try
{
// Crear archivo inicial si no existe
if (!File.Exists(rutaArchivo))
{
File.WriteAllText(rutaArchivo, "=== LOG DE APLICACIÓN ===\n");
}
// Añadir nuevas entradas al log
for (int i = 1; i <= 3; i++)
{
string entrada = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] Evento {i}: Operación completada\n";
File.AppendAllText(rutaArchivo, entrada);
Console.WriteLine($"Entrada {i} añadida al log");
System.Threading.Thread.Sleep(1000); // Pausa para mostrar diferentes timestamps
}
// Mostrar el contenido completo del log
Console.WriteLine("\n=== CONTENIDO DEL LOG ===");
string contenidoLog = File.ReadAllText(rutaArchivo);
Console.WriteLine(contenidoLog);
}
catch (Exception ex)
{
Console.WriteLine($"Error al trabajar con el log: {ex.Message}");
}
StreamWriter con modo append
using System;
using System.IO;
string rutaArchivo = @"C:\temp\notas_diarias.txt";
try
{
// Usar StreamWriter en modo append (true)
using (var writer = new StreamWriter(rutaArchivo, append: true))
{
writer.WriteLine($"\n--- Entrada del {DateTime.Now:dd/MM/yyyy} ---");
writer.WriteLine("Hoy he aprendido sobre lectura y escritura de archivos en C#.");
writer.WriteLine("Las clases File, StreamReader y StreamWriter son muy útiles.");
writer.WriteLine("Es importante manejar las excepciones correctamente.");
writer.WriteLine("--- Fin de la entrada ---");
}
Console.WriteLine("Nueva entrada añadida al diario");
// Mostrar las últimas líneas del archivo
string[] lineas = File.ReadAllLines(rutaArchivo);
Console.WriteLine("\nÚltimas 8 líneas del archivo:");
int inicio = Math.Max(0, lineas.Length - 8);
for (int i = inicio; i < lineas.Length; i++)
{
Console.WriteLine(lineas[i]);
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Codificación de caracteres
Especificar codificación
using System;
using System.IO;
using System.Text;
string rutaArchivo = @"C:\temp\texto_unicode.txt";
try
{
// Texto con caracteres especiales
string textoEspecial = "Texto con acentos: ñáéíóú\nCaracteres especiales: €¿¡";
// Escribir con codificación UTF-8
File.WriteAllText(rutaArchivo, textoEspecial, Encoding.UTF8);
Console.WriteLine("Archivo escrito con codificación UTF-8");
// Leer con codificación específica
string contenidoUTF8 = File.ReadAllText(rutaArchivo, Encoding.UTF8);
Console.WriteLine($"Contenido leído (UTF-8):\n{contenidoUTF8}");
// Escribir con diferentes codificaciones para comparar
File.WriteAllText(rutaArchivo + ".ascii", textoEspecial, Encoding.ASCII);
File.WriteAllText(rutaArchivo + ".unicode", textoEspecial, Encoding.Unicode);
Console.WriteLine("\nArchivos creados con diferentes codificaciones:");
Console.WriteLine($"- {rutaArchivo} (UTF-8)");
Console.WriteLine($"- {rutaArchivo}.ascii (ASCII)");
Console.WriteLine($"- {rutaArchivo}.unicode (Unicode)");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Procesamiento de archivos CSV básico
Leer archivo CSV simple
using System;
using System.IO;
using System.Linq;
string rutaCSV = @"C:\temp\empleados.csv";
try
{
// Crear archivo CSV de ejemplo
var datosCSV = @"Nombre,Apellido,Edad,Departamento
Juan,Pérez,30,Desarrollo
María,García,25,Marketing
Carlos,López,35,Ventas
Ana,Martínez,28,Desarrollo";
File.WriteAllText(rutaCSV, datosCSV);
// Leer y procesar el CSV
string[] lineas = File.ReadAllLines(rutaCSV);
// Primera línea son los encabezados
string[] encabezados = lineas[0].Split(',');
Console.WriteLine("Encabezados: " + string.Join(" | ", encabezados));
Console.WriteLine(new string('-', 50));
// Procesar datos línea por línea
for (int i = 1; i < lineas.Length; i++)
{
string[] campos = lineas[i].Split(',');
if (campos.Length == encabezados.Length)
{
Console.WriteLine($"Empleado {i}:");
for (int j = 0; j < encabezados.Length; j++)
{
Console.WriteLine($" {encabezados[j]}: {campos[j]}");
}
Console.WriteLine();
}
}
// Estadísticas básicas
var empleadosDesarrollo = lineas.Skip(1)
.Where(linea => linea.Contains("Desarrollo"))
.Count();
Console.WriteLine($"Empleados en Desarrollo: {empleadosDesarrollo}");
}
catch (Exception ex)
{
Console.WriteLine($"Error al procesar CSV: {ex.Message}");
}
Buenas prácticas y manejo de errores
Uso de using statements
using System;
using System.IO;
// Ejemplo de buenas prácticas
static void ProcesarArchivoSeguro(string rutaArchivo)
{
try
{
// Verificar existencia antes de procesar
if (!File.Exists(rutaArchivo))
{
Console.WriteLine($"El archivo {rutaArchivo} no existe");
return;
}
// Usar using para garantizar liberación de recursos
using (var reader = new StreamReader(rutaArchivo))
{
string linea;
int contador = 0;
while ((linea = reader.ReadLine()) != null)
{
contador++;
// Procesar línea de manera segura
if (!string.IsNullOrWhiteSpace(linea))
{
Console.WriteLine($"Línea {contador}: {linea.Trim()}");
}
// Evitar archivos excesivamente grandes
if (contador > 1000)
{
Console.WriteLine("Archivo demasiado grande, deteniendo lectura");
break;
}
}
}
// El StreamReader se cierra automáticamente aquí
}
catch (IOException ex)
{
Console.WriteLine($"Error de E/S: {ex.Message}");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("No tienes permisos para acceder al archivo");
}
catch (Exception ex)
{
Console.WriteLine($"Error inesperado: {ex.Message}");
}
}
// Ejemplo de uso
string archivo = @"C:\temp\datos.txt";
ProcesarArchivoSeguro(archivo);
Respaldo y recuperación
using System;
using System.IO;
static void EscribirConRespaldo(string rutaArchivo, string contenido)
{
string rutaRespaldo = rutaArchivo + ".backup";
try
{
// Crear respaldo si el archivo existe
if (File.Exists(rutaArchivo))
{
File.Copy(rutaArchivo, rutaRespaldo, overwrite: true);
Console.WriteLine($"Respaldo creado: {rutaRespaldo}");
}
// Escribir nuevo contenido
File.WriteAllText(rutaArchivo, contenido);
Console.WriteLine($"Archivo actualizado: {rutaArchivo}");
// Si todo sale bien, eliminar respaldo (opcional)
if (File.Exists(rutaRespaldo))
{
File.Delete(rutaRespaldo);
Console.WriteLine("Respaldo eliminado (operación exitosa)");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error al escribir archivo: {ex.Message}");
// Restaurar desde respaldo si existe
if (File.Exists(rutaRespaldo))
{
try
{
File.Copy(rutaRespaldo, rutaArchivo, overwrite: true);
Console.WriteLine("Archivo restaurado desde respaldo");
}
catch
{
Console.WriteLine("No se pudo restaurar el respaldo");
}
}
}
}
// Ejemplo de uso
string rutaArchivo = @"C:\temp\documento_importante.txt";
string nuevoContenido = "Este es el nuevo contenido del documento.";
EscribirConRespaldo(rutaArchivo, nuevoContenido);
Resumen
El manejo de archivos de texto en C# es una habilidad fundamental que abre numerosas posibilidades para el almacenamiento y procesamiento de datos. Las clases File
, StreamReader
y StreamWriter
proporcionan métodos versátiles para leer y escribir archivos, desde operaciones simples hasta procesamiento avanzado de grandes volúmenes de datos.
Es crucial recordar la importancia del manejo adecuado de errores, el uso de using
statements para garantizar la liberación de recursos, y la consideración de aspectos como la codificación de caracteres. Estas prácticas aseguran que nuestras aplicaciones sean robustas y confiables al trabajar con el sistema de archivos. Con estos conocimientos, estás preparado para implementar funcionalidades de persistencia de datos y procesamiento de archivos en tus aplicaciones C#.