跳到内容

如何在 Excel 中正确打开和保存 CSV|UTF-8 BOM 支持以防止乱码

分类:数据处理·Excel
本文目前仅提供日文版本。我们正在进行翻译工作。

「从系统导出的CSV文件在Excel中打开时日文字符乱码了」——这个问题在网络开发、数据分析和业务系统领域每天都在发生。根本原因通常是UTF-8和Shift_JIS(CP932)字符编码的差异。本文从字符乱码的根本原因开始,系统地讲解了使用UTF-8 BOM CSV的解决方案、Excel导入步骤、PHP和Python的实现方法,以及换行符代码问题。

Excel 与 CSV 乱码解决流程 UTF-8 CSV Web 输出 BOM? EF BB BF 乱码 縺ゅ>縺・▲ ... 正常显示 あいうえお No Yes 解决方法 1. 输出带 BOM 的 UTF-8 2. 使用 Excel 数据导入向导 3. 转换为 Shift_JIS (CP932) 输出 (旧版) Mac Excel BOM 识别不稳定 — 推荐 UTF-8 LF
图1: Excel 打开 UTF-8 CSV 时的乱码判定与解决路径

问题:Excel 默认期望 Shift_JIS

在 Windows 版 Excel 中双击打开 CSV 文件时,会根据操作系统的区域设置自动检测编码。在日语 Windows 上,此默认编码为 <strong>Shift_JIS(代码页 932)</strong>。

因此,当您直接双击打开以UTF-8保存的CSV文件时,多字节字符(如日文)无法正确解释,导致乱码。这就是为什么「工程师在网络系统中生成的UTF-8 CSV被非工程师用Excel打开时出现乱码」这个问题反复发生的原因。

字符编码的对应关系可以整理如下。

字符编码 别名 Excel 中的处理 在网络和 Linux 中的处理
Shift_JIS CP932、Windows-31J 日本语版本的标准 遗留。不推荐使用
UTF-8 (无 BOM) 字符损坏(旧版本) 网络标准
UTF-8 with BOM(含 BOM) UTF-8 with BOM 正确识别 BOM 可能会作为多余字符引起问题
UTF-16 LE with BOM(含 BOM) 正确识别 很少使用

防止 UTF-8 BOM 格式 CSV 文件乱码的方法

BOM(字节顺序标记)是添加在文件开头的特殊字节序列,用作识别文本文件字符编码的签名。UTF-8 BOM 是 3 字节:<code>EF BB BF</code>(十六进制)。

Excel 在打开文件时检测此 BOM 并判断 "此文件是用 UTF-8 编写的"。这就是为什么<strong>使用带 BOM 的 UTF-8 CSV 可以防止 Excel 中的文本乱码</strong>。

要验证带 BOM 的 UTF-8 和无 BOM 的 UTF-8 之间的差异,请使用文本编辑器或 <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)

在 Excel 中从 "数据" → "文本文件" 导入的步骤

要在 Excel 中正确打开无 BOM 的 UTF-8 CSV 文件,请使用"导入数据"功能而不是双击。步骤因版本而异,但以下步骤适用于 Excel 2016 及更高版本(包括 Microsoft 365)。

  1. 启动 Excel 并打开新工作簿。
  2. 点击「数据」选项卡 → 「从文本或CSV」。
  3. 选择目标 CSV 文件,然后点击「导入」。
  4. 显示预览屏幕。从"文件来源"下拉菜单中选择 <strong>65001: Unicode (UTF-8)</strong>。
  5. 确认分隔符设置为「逗号」,然后点击「加载」。

对于 Excel 2013 及更早版本,使用 "数据" → "导入外部数据" → "文本文件",并在 "文本文件向导"的第 2 步中将字符编码更改为 UTF-8 (65001)。

PHP中输出带UTF-8 BOM的CSV的代码

当需要从 Web 系统允许下载 CSV 时,用 PHP 输出带 BOM 的 UTF-8 CSV 是最简单的解决方法。

// 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');

请注意,<code>fputcsv()</code> 默认使用逗号分隔和双引号转义。如果要更改分隔符,可以用第三个参数指定(例如:制表符分隔使用 <code>"\t"</code>)。

Python 中的字符编码转换代码

Python 便于转换现有 CSV 文件的字符编码,或将以 Shift_JIS 接收的数据转换为 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)

在 Python 的 <code>open()</code> 中指定 <code>encoding='utf-8-sig'</code> 时,BOM 在写入时自动添加,在读取时自动删除。

macOS 版 Excel 的注意事项

macOS 版 Excel (Microsoft 365 for Mac) 与 Windows 版存在行为差异。

  • <strong>带 UTF-8 BOM 的 CSV</strong>:即使在 Excel for macOS 中也能通过双击正确打开(从相对较新的版本开始)。
  • <strong>较早版本(Excel 2016 for Mac 等)</strong>:即使使用 UTF-8 BOM,也可能出现字符乱码。在这种情况下,请使用「数据导入」功能
  • <strong>在"另存为"中选择 CSV</strong>:在 macOS 版本中,可能会保存为不带 UTF-8 BOM 的格式。传递到 Windows 环境的 CSV 需要重新验证。
  • <strong>与 Numbers.app 混用</strong>:macOS 的 Numbers 将 UTF-8 作为标准,但需要注意与 Excel 的兼容性。

换行码问题 (<code>CRLF</code> vs <code>LF</code>)

除了 CSV 中的乱码,换行符的差异也可能会导致问题。

换行码 字节序列 主要环境 CSV 中的处理
CRLF 0D 0A Windows、HTTP RFC 4180 中定义的标准
LF 0A Linux、macOS、Git 默认值 在大多数情况下也可在 Excel 中使用
CR 0D 旧版 macOS(9 之前) 可能仅在较旧的 Excel 版本中存在问题

尽管 CSV 的标准规范 RFC 4180 规定使用 CRLF,但现代 Excel 可以轻松处理仅使用 LF 的 CSV 文件。但是,当 CSV 包含字段内的换行符时,换行符的处理变得很重要。PHP 的 <code>fputcsv()</code> 默认使用 LF,但如果强调 Windows 兼容性,可以考虑使用 <code>str_replace("\n", "\r\n", $output)</code> 在输出后进行转换。

// 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;
}

总结:无乱码CSV文件分发的最佳实践

  • 向 Excel 用户分发的 CSV 应使用 UTF-8 BOM 格式输出
  • 在PHP中,使用<code>fputs($output, "\xEF\xBB\xBF")</code>在开头添加BOM
  • 在 Python 中指定 <code>encoding='utf-8-sig'</code>
  • 要检查接收到的 CSV 的字符编码,请使用 <code>chardet</code> 或 <code>file</code> 命令
  • 将无 BOM 的 UTF-8 CSV 提供给 Excel 用户时,请引导他们完成 "导入数据" 过程。
  • <code>CRLF</code> 是 RFC 标准的换行码,但现代 Excel 也接受 <code>LF</code>
  • macOS 版 Excel 可能存在行为差异,建议在接收端进行操作确认

本文中可用的测试文件

  • <a href="/ja/files/encoding/" class="text-primary-600 dark:text-primary-400 hover:underline">字符编码测试文件列表</a> — UTF-8 BOM 有/无、Shift_JIS 等各种编码样本
  • <a href="/ja/files/csv/" class="text-primary-600 dark:text-primary-400 hover:underline">测试用 CSV 文件列表</a> — 换行符·字符编码·BOM 有无的组合示例
  • <a href="/ja/files/newline/" class="text-primary-600 dark:text-primary-400 hover:underline">换行符代码测试文件列表</a> — 确认 CRLF / LF / CR 各种模式

相关文章

  • <a href="/ja/blog/csv-encoding-trouble-guide/" class="text-primary-600 dark:text-primary-400 hover:underline">彻底解决CSV文字乱码问题!字符编码、BOM、换行符的基础知识</a>
  • <a href="/ja/blog/file-format-quick-reference/" class="text-primary-600 dark:text-primary-400 hover:underline">开发者文件格式快速参考</a>
  • <a href="/ja/blog/base64-size-increase/" class="text-primary-600 dark:text-primary-400 hover:underline">Base64编码为什么会增加文件大小33%</a>

常见问题

为什么在 Excel 中打开 CSV 时会出现乱码?

Excel 默认以 Shift_JIS (Windows 环境) 读取文件,导致 UTF-8 CSV 出现乱码。使用 UTF-8 BOM 保存可以解决此问题。

什么是 BOM(字节顺序标记)?

文件开头附加的3字节标记(EF BB BF)用来告诉软件这是UTF-8编码。这是Excel正确识别UTF-8所必需的。

如何在 macOS 版 Excel 中正确打开 CSV?

即使在 macOS 版 Excel 中,使用「数据」选项卡中的「文本文件导入」功能,并指定字符编码为 UTF-8 进行读取也是可靠的。