Módulo 10: Ficheros y entrada/salida
Objetivos del módulo
- Leer y escribir archivos de texto
- Trabajar con rutas y directorios
- Usar streams para archivos grandes
- Serializar y deserializar JSON con
System.Text.Json - Aplicar
usingpara gestión segura de recursos
1. Leer y escribir archivos de texto
Métodos simples de File
La clase File (de System.IO) ofrece métodos estáticos rápidos para operaciones sencillas:
// Escribir un archivo completo
File.WriteAllText("saludo.txt", "Hola, mundo!\nBienvenido al curso.");
// Leer un archivo completo
string contenido = File.ReadAllText("saludo.txt");
Console.WriteLine(contenido);
// Escribir líneas (un array de strings)
string[] lineas = { "Primera línea", "Segunda línea", "Tercera línea" };
File.WriteAllLines("lineas.txt", lineas);
// Leer líneas (devuelve un array)
string[] lineasLeidas = File.ReadAllLines("lineas.txt");
foreach (string linea in lineasLeidas)
{
Console.WriteLine(linea);
}
// Añadir texto al final (no sobreescribe)
File.AppendAllText("saludo.txt", "\nEsta línea se añade al final.");
File.AppendAllLines("lineas.txt", new[] { "Línea añadida" });
| Método | Acción |
|---|---|
WriteAllText(ruta, texto) | Escribe (sobreescribe) texto completo |
ReadAllText(ruta) | Lee todo el archivo como un string |
WriteAllLines(ruta, líneas) | Escribe un array de líneas |
ReadAllLines(ruta) | Lee todas las líneas como array |
AppendAllText(ruta, texto) | Añade texto al final |
AppendAllLines(ruta, líneas) | Añade líneas al final |
Exists(ruta) | Comprueba si el archivo existe |
Delete(ruta) | Elimina el archivo |
Copy(origen, destino) | Copia un archivo |
Move(origen, destino) | Mueve/renombra un archivo |
2. Comprobar antes de actuar
string ruta = "datos.txt";
if (File.Exists(ruta))
{
string contenido = File.ReadAllText(ruta);
Console.WriteLine(contenido);
}
else
{
Console.WriteLine($"El archivo '{ruta}' no existe.");
}
3. Trabajar con rutas: Path
La clase Path ayuda a construir y manipular rutas de archivo de forma segura:
string ruta = @"C:\Users\Ana\Documentos\datos.txt";
Console.WriteLine(Path.GetFileName(ruta)); // datos.txt
Console.WriteLine(Path.GetFileNameWithoutExtension(ruta)); // datos
Console.WriteLine(Path.GetExtension(ruta)); // .txt
Console.WriteLine(Path.GetDirectoryName(ruta)); // C:\Users\Ana\Documentos
// Combinar rutas (forma segura)
string carpeta = @"C:\Users\Ana";
string archivo = "datos.txt";
string rutaCompleta = Path.Combine(carpeta, "Documentos", archivo);
Console.WriteLine(rutaCompleta); // C:\Users\Ana\Documentos\datos.txt
// Ruta temporal
string temp = Path.GetTempFileName();
Console.WriteLine(temp); // C:\Users\Ana\AppData\Local\Temp\tmp1234.tmp
💡 Consejo: Siempre usa
Path.Combine()para construir rutas. Nunca concatenes strings con+porque puedes olvidar separadores.
4. Trabajar con directorios: Directory
string carpeta = "MisArchivos";
// Crear directorio
if (!Directory.Exists(carpeta))
{
Directory.CreateDirectory(carpeta);
Console.WriteLine($"Carpeta '{carpeta}' creada.");
}
// Listar archivos
string[] archivos = Directory.GetFiles(carpeta);
foreach (string archivo in archivos)
{
Console.WriteLine(archivo);
}
// Listar archivos con filtro
string[] textos = Directory.GetFiles(carpeta, "*.txt");
string[] todos = Directory.GetFiles(carpeta, "*.*", SearchOption.AllDirectories);
// Listar subcarpetas
string[] subcarpetas = Directory.GetDirectories(carpeta);
// Obtener directorio actual
string actual = Directory.GetCurrentDirectory();
Console.WriteLine($"Directorio actual: {actual}");
5. Streams: lectura y escritura eficiente
📘 Concepto: Los streams (flujos) permiten leer y escribir datos poco a poco, en lugar de cargar todo en memoria. Son esenciales para archivos grandes.
StreamWriter: escribir
// Opción 1: using declaration (recomendado en C# moderno)
using StreamWriter writer = new StreamWriter("registro.txt");
writer.WriteLine("Línea 1");
writer.WriteLine("Línea 2");
writer.WriteLine($"Fecha: {DateTime.Now}");
// El archivo se cierra automáticamente al salir del ámbito
// Opción 2: using block (clásico)
using (StreamWriter writer2 = new StreamWriter("registro2.txt", append: true))
{
writer2.WriteLine("Texto añadido al final");
}
// writer2 se cierra aquí automáticamente
StreamReader: leer
using StreamReader reader = new StreamReader("registro.txt");
// Leer línea por línea (eficiente para archivos grandes)
string? linea;
int numLinea = 1;
while ((linea = reader.ReadLine()) is not null)
{
Console.WriteLine($"{numLinea}: {linea}");
numLinea++;
}
📘 Concepto:
usingasegura que el stream se cierra siempre, incluso si ocurre una excepción. Es equivalente a usartry-finallyconClose().
6. JSON con System.Text.Json
JSON (JavaScript Object Notation) es el formato estándar para intercambiar datos. C# incluye System.Text.Json para serializarlo y deserializarlo.
Serializar: objeto → JSON
using System.Text.Json;
// Definir una clase
class Producto
{
public string Nombre { get; set; } = "";
public double Precio { get; set; }
public int Stock { get; set; }
public bool Disponible { get; set; }
}
// Crear un objeto
var producto = new Producto
{
Nombre = "Teclado mecánico",
Precio = 89.99,
Stock = 150,
Disponible = true
};
// Serializar a JSON
var opciones = new JsonSerializerOptions
{
WriteIndented = true // JSON bonito, con indentación
};
string json = JsonSerializer.Serialize(producto, opciones);
Console.WriteLine(json);
// {
// "Nombre": "Teclado mec\u00E1nico",
// "Precio": 89.99,
// "Stock": 150,
// "Disponible": true
// }
// Guardar JSON en archivo
File.WriteAllText("producto.json", json);
Deserializar: JSON → objeto
// Leer JSON de archivo
string jsonArchivo = File.ReadAllText("producto.json");
// Deserializar a objeto
Producto? prod = JsonSerializer.Deserialize<Producto>(jsonArchivo);
if (prod is not null)
{
Console.WriteLine($"Producto: {prod.Nombre}");
Console.WriteLine($"Precio: {prod.Precio:C2}");
}
Listas de objetos
var productos = new List<Producto>
{
new() { Nombre = "Ratón", Precio = 29.99, Stock = 200, Disponible = true },
new() { Nombre = "Monitor", Precio = 299.00, Stock = 50, Disponible = true },
new() { Nombre = "Webcam", Precio = 59.50, Stock = 0, Disponible = false }
};
// Serializar lista
string jsonLista = JsonSerializer.Serialize(productos, opciones);
File.WriteAllText("productos.json", jsonLista);
// Deserializar lista
string jsonLeido = File.ReadAllText("productos.json");
List<Producto>? lista = JsonSerializer.Deserialize<List<Producto>>(jsonLeido);
if (lista is not null)
{
foreach (var p in lista)
{
Console.WriteLine($"{p.Nombre}: {p.Precio:C2} ({p.Stock} uds)");
}
}
Opciones de serialización
var opciones = new JsonSerializerOptions
{
WriteIndented = true, // Indentación
PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // camelCase en JSON
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, // Ignorar nulls
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping // Acentos sin escapar
};
7. Información de archivos: FileInfo
FileInfo info = new FileInfo("producto.json");
if (info.Exists)
{
Console.WriteLine($"Nombre: {info.Name}");
Console.WriteLine($"Tamaño: {info.Length} bytes");
Console.WriteLine($"Creado: {info.CreationTime}");
Console.WriteLine($"Modificado: {info.LastWriteTime}");
Console.WriteLine($"Extensión: {info.Extension}");
Console.WriteLine($"Directorio: {info.DirectoryName}");
Console.WriteLine($"Solo lectura: {info.IsReadOnly}");
}
8. Ejemplo práctico: agenda de contactos en JSON
using System.Text.Json;
class Contacto
{
public string Nombre { get; set; } = "";
public string Telefono { get; set; } = "";
public string Email { get; set; } = "";
}
const string ARCHIVO = "contactos.json";
var opciones = new JsonSerializerOptions
{
WriteIndented = true,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
// Cargar contactos existentes o crear lista vacía
List<Contacto> contactos;
if (File.Exists(ARCHIVO))
{
string json = File.ReadAllText(ARCHIVO);
contactos = JsonSerializer.Deserialize<List<Contacto>>(json) ?? new();
}
else
{
contactos = new();
}
bool salir = false;
while (!salir)
{
Console.WriteLine("\n--- AGENDA ---");
Console.WriteLine("1. Ver contactos");
Console.WriteLine("2. Añadir contacto");
Console.WriteLine("3. Buscar contacto");
Console.WriteLine("4. Salir");
Console.Write("Opción: ");
switch (Console.ReadLine())
{
case "1":
if (contactos.Count == 0)
{
Console.WriteLine("No hay contactos.");
}
else
{
for (int i = 0; i < contactos.Count; i++)
{
var c = contactos[i];
Console.WriteLine($" {i + 1}. {c.Nombre} | {c.Telefono} | {c.Email}");
}
}
break;
case "2":
Console.Write("Nombre: ");
string nombre = Console.ReadLine() ?? "";
Console.Write("Teléfono: ");
string telefono = Console.ReadLine() ?? "";
Console.Write("Email: ");
string email = Console.ReadLine() ?? "";
contactos.Add(new Contacto { Nombre = nombre, Telefono = telefono, Email = email });
// Guardar en archivo
string jsonGuardar = JsonSerializer.Serialize(contactos, opciones);
File.WriteAllText(ARCHIVO, jsonGuardar);
Console.WriteLine("Contacto guardado.");
break;
case "3":
Console.Write("Buscar: ");
string buscar = Console.ReadLine() ?? "";
var encontrados = contactos.FindAll(c =>
c.Nombre.Contains(buscar, StringComparison.OrdinalIgnoreCase));
Console.WriteLine($"Se encontraron {encontrados.Count} resultado(s):");
foreach (var c in encontrados)
{
Console.WriteLine($" {c.Nombre} | {c.Telefono} | {c.Email}");
}
break;
case "4":
salir = true;
break;
}
}
9. Ejercicios
Ejercicio 1: Registro de notas
Crea un programa que gestione las notas de alumnos en un archivo de texto. Cada línea: nombre;nota1;nota2;nota3. Permite: añadir alumno, ver todos, calcular medias, encontrar mejor/peor alumno.
Ejercicio 2: Gestor de tareas JSON
Crea un gestor de tareas (To-Do) que almacene en JSON: título, descripción, fecha de creación, completada (sí/no), prioridad (enum). Permite: añadir, listar, completar, eliminar, filtrar por estado/prioridad.
Ejercicio 3: Analizador de logs
Lee un archivo de log con formato [FECHA] [NIVEL] Mensaje (niveles: INFO, WARNING, ERROR). Muestra: total de entradas por nivel, todos los errores, estadísticas por día.
Ejercicio 4: Copia de seguridad
Crea una herramienta que copie todos los archivos de una carpeta a otra (carpeta backup), añadiendo la fecha al nombre. Si la carpeta destino no existe, la crea. Muestra un resumen final.
Resumen
| Concepto | Método / Clase |
|---|---|
| Leer texto | File.ReadAllText(), File.ReadAllLines() |
| Escribir texto | File.WriteAllText(), File.WriteAllLines() |
| Añadir texto | File.AppendAllText() |
| Comprobar existencia | File.Exists(), Directory.Exists() |
| Crear directorio | Directory.CreateDirectory() |
| Rutas seguras | Path.Combine(), Path.GetFileName() |
| Stream escritura | StreamWriter + using |
| Stream lectura | StreamReader + using |
| Serializar JSON | JsonSerializer.Serialize(obj) |
| Deserializar JSON | JsonSerializer.Deserialize<T>(json) |
| Info de archivo | FileInfo |