PHP tiene soporte nativo para leer y escribir ficheros CSV con las funciones fgetcsv() y fputcsv(), y la clase SplFileObject ofrece una interfaz orientada a objetos sobre las mismas. Son herramientas esenciales para importaciones y exportaciones de datos.
Leer un CSV con fgetcsv()
<?php
$handle = fopen('productos.csv', 'r');
if ($handle === false) {
throw new RuntimeException('No se puede abrir el fichero');
}
// Saltar cabecera
fgetcsv($handle);
$productos = [];
while (($fila = fgetcsv($handle, 0, ';')) !== false) {
$productos[] = [
'sku' => $fila[0],
'nombre' => $fila[1],
'precio' => (float) str_replace(',', '.', $fila[2]),
'stock' => (int) $fila[3],
];
}
fclose($handle);
echo count($productos) . " productos importadosn";
?>
El tercer parámetro de fgetcsv() es el separador (por defecto coma, pero muchos CSV europeos usan punto y coma).
Importar con conversión de encoding
Los ficheros CSV exportados desde Excel en Windows suelen venir en CP1252 (Windows-1252):
<?php
$handle = fopen('clientes_excel.csv', 'r');
while (($fila = fgetcsv($handle, 0, ';')) !== false) {
// Convertir cada campo de CP1252 a UTF-8
$fila = array_map(
fn($campo) => mb_convert_encoding($campo, 'UTF-8', 'CP1252'),
$fila
);
// Usar $fila...
}
fclose($handle);
?>
Escribir un CSV con fputcsv()
<?php
$pedidos = [
[1001, 'Ana García', '2024-06-01', 149.99],
[1002, 'Luis Martín', '2024-06-02', 89.50],
[1003, 'María López', '2024-06-03', 320.00],
];
$handle = fopen('pedidos.csv', 'w');
// BOM UTF-8 para que Excel lo abra correctamente
fwrite($handle, "xEFxBBxBF");
fputcsv($handle, ['ID', 'Cliente', 'Fecha', 'Total'], ';');
foreach ($pedidos as $pedido) {
fputcsv($handle, $pedido, ';');
}
fclose($handle);
?>
Exportar CSV con descarga directa
<?php
// Cabeceras HTTP para forzar la descarga
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename="pedidos-' . date('Y-m-d') . '.csv"');
header('Cache-Control: no-cache, must-revalidate');
$output = fopen('php://output', 'w');
fwrite($output, "xEFxBBxBF"); // BOM para Excel
fputcsv($output, ['ID', 'Cliente', 'Fecha', 'Total'], ';');
// Consulta a BD (ejemplo con PDO)
$stmt = $pdo->query('SELECT id, cliente, fecha, total FROM pedidos ORDER BY fecha DESC');
while ($fila = $stmt->fetch(PDO::FETCH_ASSOC)) {
fputcsv($output, array_values($fila), ';');
}
fclose($output);
exit;
?>
SplFileObject para lectura orientada a objetos
<?php
$fichero = new SplFileObject('productos.csv');
$fichero->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY | SplFileObject::DROP_NEW_LINE);
$fichero->setCsvControl(';', '"');
$cabecera = null;
foreach ($fichero as $fila) {
if ($cabecera === null) {
$cabecera = $fila;
continue;
}
$registro = array_combine($cabecera, $fila);
echo $registro['nombre'] . ': ' . $registro['precio'] . "n";
}
?>
Leer ficheros CSV grandes sin cargar en memoria
Para ficheros de varios GB, el enfoque de iterar fila a fila con fgetcsv() o SplFileObject mantiene el consumo de memoria constante:
<?php
$fichero = new SplFileObject('ventas_anuales.csv');
$fichero->setFlags(SplFileObject::READ_CSV | SplFileObject::SKIP_EMPTY);
$fichero->setCsvControl(';');
$total = 0.0;
$lineas = 0;
foreach ($fichero as $i => $fila) {
if ($i === 0) continue; // cabecera
$total += (float) $fila[3]; // columna importe
$lineas++;
if ($lineas % 10000 === 0) {
echo "Procesadas $lineas líneas...n";
}
}
echo "Total ventas: " . number_format($total, 2) . " n";
?>
Errores comunes
- Separador incorrecto: Excel guarda con punto y coma en configuraciones europeas. Si el CSV tiene un solo campo gigante, el separador es incorrecto.
- Caracteres especiales y comillas:
fputcsv()entrecomilla automáticamente los campos que contienen el separador o saltos de línea. Si el CSV de origen no lo hace bien, usastr_getcsv()con el parámetroenclosure. - BOM doble: si añades el BOM UTF-8 y el fichero ya lo tiene, Excel verá caracteres extra al inicio. Lee los primeros 3 bytes para verificar antes de añadirlo.
