Cómo abrir y guardar correctamente CSV en Excel | Soporte UTF-8 BOM para evitar corrupción de caracteres
「Cuando exporté un archivo CSV del sistema y lo abrí en Excel, los caracteres japoneses se corrompieron」——este problema ocurre diariamente en el campo del desarrollo web, análisis de datos y sistemas empresariales. La causa raíz suele ser la diferencia en la codificación de caracteres entre UTF-8 y Shift_JIS (CP932). Este artículo explica sistemáticamente la causa fundamental de la corrupción de caracteres, soluciones mediante CSV con BOM UTF-8, procedimientos de importación en Excel, métodos de implementación en PHP y Python, y problemas de códigos de línea.
Problema: Excel espera Shift_JIS de forma predeterminada
Al abrir archivos CSV con doble clic en Excel para Windows, la codificación se detecta automáticamente según la configuración de idioma del SO. En Windows japonés, esta codificación predeterminada es <strong>Shift_JIS (página de códigos 932)</strong>.
Por esta razón, cuando abre directamente un CSV guardado en UTF-8 haciendo doble clic, los caracteres multibyte (como el japonés) no se interpretan correctamente, lo que resulta en texto corrupto. Esta es la razón por la que el problema de "un no ingeniero abriendo un CSV UTF-8 generado por un ingeniero en un sistema web con Excel y encontrando texto corrupto" ocurre repetidamente.
La correspondencia de codificaciones de caracteres se puede organizar de la siguiente manera.
| Codificación de Caracteres | Alias | Manejo en Excel | Manejo en Web y Linux |
|---|---|---|---|
| Shift_JIS | CP932、Windows-31J | Estándar en la versión en japonés | Heredado. No se recomienda su uso |
| UTF-8 (sin BOM) | — | Corrupción de caracteres (versión anterior) | Estándares web |
| UTF-8 con BOM | UTF-8 with BOM | Se reconoce correctamente | La BOM puede causar problemas como caracteres adicionales |
| UTF-16 LE con BOM | — | Se reconoce correctamente | Raramente utilizado |
Cómo evitar problemas de codificación de caracteres con CSV UTF-8 con BOM
La BOM (Byte Order Mark) es una secuencia especial de bytes añadida al principio de un archivo, que sirve como firma para identificar la codificación de caracteres de un archivo de texto. La BOM UTF-8 son 3 bytes: <code>EF BB BF</code> (en hexadecimal).
Excel detecta este BOM al abrir un archivo y determina "este archivo está escrito en UTF-8". Esta es la razón por la que <strong>usar CSV UTF-8 con BOM evita texto corrupto en Excel</strong>.
Para verificar la diferencia entre UTF-8 con BOM y UTF-8 sin BOM, comprueba los bytes del encabezado del archivo usando un editor de texto o el comando <code>hexdump</code>.
# Linux / macOS での確認
hexdump -C sample.csv | head -1
# BOM なし UTF-8 の出力例:
# 00000000 e5 90 8d e5 89 8d 2c e5 ...
# BOM 付き UTF-8 の出力例:
# 00000000 ef bb bf e5 90 8d e5 89 ...(先頭に ef bb bf)
Pasos para importar desde "Datos" → "Archivo de texto" en Excel
Para abrir archivos CSV UTF-8 sin BOM correctamente en Excel, usa la función "Importar datos" en lugar de hacer doble clic. Los pasos varían según la versión, pero el siguiente procedimiento funciona para Excel 2016 y posteriores (incluido Microsoft 365).
- Inicie Excel y abra un nuevo libro de trabajo.
- Haga clic en la pestaña 「Datos」 → 「Desde texto o CSV」.
- Selecciona el archivo CSV objetivo y haz clic en <strong>Importar</strong>.
- Se muestra una pantalla de vista previa. Seleccione <strong>65001: Unicode (UTF-8)</strong> en el menú desplegable "Origen del archivo".
- Verifique que el delimitador esté configurado en 「coma」 y haga clic en 「Cargar」.
Para Excel 2013 y anteriores, use "Datos" → "Importar datos externos" → "Archivo de texto", y cambie la codificación de caracteres a UTF-8 (65001) en el paso 2 del "Asistente de archivos de texto".
Código para generar CSV con BOM UTF-8 en PHP
Cuando se permite descargar un CSV desde un sistema web, la solución más simple es generar un CSV UTF-8 con BOM en PHP.
// UTF-8 BOM 付き CSV のダウンロード出力
function outputCsvWithBom(array $headers, array $rows, string $filename = 'export.csv'): void
{
// キャッシュ無効化・ダウンロードヘッダーを設定
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Cache-Control: no-cache, no-store, must-revalidate');
$output = fopen('php://output', 'w');
// UTF-8 BOM を出力(EF BB BF)
fputs($output, "\xEF\xBB\xBF");
// ヘッダー行を出力
fputcsv($output, $headers);
// データ行を出力
foreach ($rows as $row) {
fputcsv($output, $row);
}
fclose($output);
exit;
}
// 使用例
$headers = ['名前', 'メールアドレス', '登録日'];
$rows = [
['山田 太郎', 'taro@example.com', '2026-04-14'],
['鈴木 花子', 'hanako@example.com', '2026-04-13'],
];
outputCsvWithBom($headers, $rows, 'users_' . date('Ymd') . '.csv');
Tenga en cuenta que <code>fputcsv()</code> usa separadores de comas y escape con comillas dobles por defecto. Si desea cambiar el delimitador, puede especificarlo con el tercer argumento (por ejemplo, para valores separados por tabulación use <code>"\t"</code>).
Código de conversión de codificación de caracteres en Python
Python es conveniente para convertir la codificación de caracteres de archivos CSV existentes o convertir datos recibidos en Shift_JIS a UTF-8.
import csv
import codecs
# BOM なし UTF-8 → BOM 付き UTF-8 に変換して保存
def add_bom_to_utf8_csv(input_path: str, output_path: str) -> None:
with open(input_path, 'r', encoding='utf-8') as infile:
content = infile.read()
with open(output_path, 'w', encoding='utf-8-sig') as outfile:
# 'utf-8-sig' は自動的に BOM を付加する
outfile.write(content)
# Shift_JIS CSV → UTF-8 BOM 付き CSV に変換
def convert_sjis_to_utf8_bom(input_path: str, output_path: str) -> None:
with open(input_path, 'r', encoding='shift_jis', errors='replace') as infile:
rows = list(csv.reader(infile))
with open(output_path, 'w', encoding='utf-8-sig', newline='') as outfile:
writer = csv.writer(outfile)
writer.writerows(rows)
# 文字コードを自動検出して変換(chardet を使用)
# pip install chardet
import chardet
def detect_and_convert(input_path: str, output_path: str) -> None:
with open(input_path, 'rb') as f:
raw_data = f.read()
detected = chardet.detect(raw_data)
encoding = detected['encoding'] or 'utf-8'
print(f'検出した文字コード: {encoding}(信頼度: {detected["confidence"]*100:.0f}%)')
content = raw_data.decode(encoding, errors='replace')
with open(output_path, 'w', encoding='utf-8-sig', newline='') as outfile:
outfile.write(content)
Al especificar <code>encoding='utf-8-sig'</code> en <code>open()</code> de Python, la BOM se añade automáticamente al escribir y se elimina automáticamente al leer.
Consideraciones de Excel para macOS
Excel para macOS (Microsoft 365 for Mac) tiene diferencias de comportamiento en comparación con la versión de Windows.
- <strong>CSV con BOM UTF-8</strong>: Se abre correctamente con doble clic incluso en Excel para macOS (a partir de versiones relativamente recientes).
- <strong>Versiones antiguas (Excel 2016 para Mac, etc.)</strong>: La corrupción de caracteres puede ocurrir incluso con UTF-8 BOM. En tales casos, usa la función "Importar datos"
- <strong>Seleccionar CSV en "Guardar como"</strong>: En versiones de macOS, puede guardarse sin BOM UTF-8. Los archivos CSV pasados a entornos Windows requieren reverificación.
- <strong>Uso mixto con Numbers.app</strong>: Numbers en macOS trata UTF-8 como estándar, pero la compatibilidad con Excel requiere precaución.
Problemas de Código de Salto de Línea (<code>CRLF</code> vs <code>LF</code>)
Junto con caracteres garrapateados en CSV, las diferencias en los finales de línea también pueden ser problemáticas.
| Código de Salto de Línea | Cadena de bytes | Entornos principales | Manejo en CSV |
|---|---|---|---|
| CRLF | 0D 0A |
Windows、HTTP | Estándar definido en RFC 4180 |
| LF | 0A |
Predeterminado de Linux, macOS, Git | Funciona en la mayoría de los casos con Excel |
| CR | 0D |
macOS antiguo (9 y anteriores) | Solo puede ser un problema con versiones antiguas de Excel |
Aunque RFC 4180, la especificación estándar para CSV, especifica CRLF, Excel moderno maneja archivos CSV solo con LF sin problemas. Sin embargo, cuando CSV contiene saltos de línea dentro de campos, el manejo de los finales de línea se vuelve importante. El <code>fputcsv()</code> de PHP usa LF de manera predeterminada, pero si la compatibilidad con Windows es importante, considera convertir con <code>str_replace("\n", "\r\n", $output)</code> después de la salida.
// PHP で CRLF 改行の CSV を出力する方法
function outputCsvCrlfWithBom(array $headers, array $rows, string $filename = 'export.csv'): void
{
header('Content-Type: text/csv; charset=UTF-8');
header('Content-Disposition: attachment; filename="' . $filename . '"');
// 一旦バッファに書き出して CRLF に変換する
ob_start();
$output = fopen('php://output', 'w');
fputs($output, "\xEF\xBB\xBF");
fputcsv($output, $headers);
foreach ($rows as $row) {
fputcsv($output, $row);
}
fclose($output);
$csv = ob_get_clean();
// LF を CRLF に変換(すでに CRLF になっているものは除外)
$csv = str_replace(["\r\n", "\n"], "\r\n", $csv);
echo $csv;
exit;
}
Resumen: Mejores prácticas para distribuir archivos CSV sin corrupción de caracteres
- Exportar CSV con UTF-8 BOM al distribuir a usuarios de Excel
- En PHP, agregue BOM al principio usando <code>fputs($output, "\xEF\xBB\xBF")</code>
- En Python, especifique <code>encoding='utf-8-sig'</code>
- Para verificar la codificación de caracteres de un CSV recibido, utiliza el comando <code>chardet</code> o <code>file</code>
- Al proporcionar CSV UTF-8 sin BOM a usuarios de Excel, guíelos a través del proceso "Importar datos".
- <code>CRLF</code> es el estándar RFC para códigos de salto de línea, pero Excel moderno también acepta <code>LF</code>
- Excel para macOS puede tener un comportamiento diferente; se recomienda verificar el funcionamiento en el lado receptor
Archivos de prueba para este artículo
- <a href="/ja/files/encoding/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de archivos de prueba de codificación de caracteres</a> — Ejemplos de varias codificaciones incluyendo UTF-8 con/sin BOM, Shift_JIS y más
- <a href="/ja/files/csv/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de archivos CSV de prueba</a> — Ejemplos de combinaciones de terminaciones de línea, codificación de caracteres y presencia/ausencia de BOM
- <a href="/ja/files/newline/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de archivos de prueba de códigos de salto de línea</a> — Verificar patrones CRLF / LF / CR
Artículos relacionados
- <a href="/ja/blog/csv-encoding-trouble-guide/" class="text-primary-600 dark:text-primary-400 hover:underline">¡Resuelve completamente los problemas de codificación de caracteres en CSV! Conocimientos básicos de códigos de caracteres, BOM y saltos de línea</a>
- <a href="/ja/blog/file-format-quick-reference/" class="text-primary-600 dark:text-primary-400 hover:underline">Referencia rápida de formatos de archivo para desarrolladores</a>
- <a href="/ja/blog/base64-size-increase/" class="text-primary-600 dark:text-primary-400 hover:underline">Por qué Base64 aumenta el tamaño de archivo en un 33%</a>