文件中文全是乱码:一次 Linux 字符编码与字节转换的排查复盘

同事用微信发来一个 order.csv 让导数据库,存到 Linux 服务器上 cat 一看满屏鬼画符一个中文都认不出,以为文件传坏了让重发还是乱,可同事说他用 Excel 打开公司名地址清清楚楚全是好好的中文,同一个文件他说正常我说乱码。排查梳理:文件里存的从来不是字是一串字节 0 到 255 的数字,字节本身没有意义要把字节变成人能读的字符必须有一本叫编码的翻译词典,GBK 这本词典说 b9 ab 翻译成公 UTF-8 说公要用 e5 85 ac 三个字节而 b9 ab 查无此字,乱码的本质就是这串字节用 A 编码写下的你却用 B 编码去读文件没坏字节一个没错错的是读时用错了词典;绝大多数文本文件不自带编码标记是什么编码只能靠字节特征去猜,file 看大类对 UTF-8 准中文常猜成 ISO-8859 就该疑 GBK,enca -L zh_CN 识中文准 iconv 试解码通不通最可靠 xxd od 看原始字节,还要留意开头的 UTF-8 BOM 即 EF BB BF 会被程序当内容;终端显示文件用当前 locale 即 LANG 和 LC_CTYPE 指定的编码来解码,同事 GBK 环境读 GBK 文件正常我 UTF-8 环境读同一个 GBK 文件就乱,SSH 还会把本地 locale 带到服务器;治本用 iconv -f 源编码 -t UTF-8 转码 -f 必须填真实编码填错照样乱输出到新文件别覆盖,文件名乱码用 convmv 内容乱码用 iconv 别用混,Windows 来的文件常要 iconv 加 dos2unix 一起处理;编码用错的后果远不止 cat 看着乱,导数据库会让乱码存进去就坏 grep 按字节匹配搜中文搜不到 程序读文件报错或把乱码读进内存 cat 拼接不同编码文件几乎无法挽救。正确做法是外部文本一进系统就 iconv 归一到 UTF-8 之后全程 UTF-8,以及一套文件编码排查纪律。

2022 年,一个"同一个文件,同事打开好好的、我打开全是乱码"的问题,把我对"一个文件里装的是什么"这件事的理解,从头到尾翻新了一遍。那天同事用微信发我一个 order.csv,让我导进数据库。我存到 Linux 服务器上,cat order.csv 一看——满屏 ��˾���� 这种鬼画符,一个中文都认不出来。我以为文件传坏了,让同事重发。他重发了,还是乱码。他很纳闷:"我这边用 Excel 打开,清清楚楚啊,公司名、地址,全是好好的中文。"我俩对着同一个文件,他说正常,我说乱码——这文件到底是好的还是坏的?我把它下载到自己 Windows 电脑上,用记事本打开:也乱。可用另一个编辑器,选了个什么选项,又突然好了。我整个人糊涂了:一个文件,它里头的中文,怎么会【一会儿是好的、一会儿是乱的】?它到底【有没有】那些中文?如果有,为什么我看不到;如果没有,同事又是从哪看到的?我盯着那满屏乱码,第一次意识到:我一直以为,一个文件里"装着字",就像一个盒子里装着东西,打开就能看见。可现在,同一个盒子,有人看见了字,有人只看见一堆乱麻。这件事逼着我把"字节与字符"、什么是编码、fileiconvlocale 这一整套彻底理清了。本文复盘这次实战。

问题背景

环境:CentOS 7,要把一个同事发来的 csv 导入数据库
事故现象:
- cat order.csv 满屏乱码,中文全是 ��˾���� 这种
- ★ 同事说他用 Excel 打开,中文清清楚楚正常
- 同一个文件,他说正常、我说乱码

现场排查:
# 1. cat 看文件 —— 乱码
$ cat order.csv
��˾����,��ϵ��ʽ,��ַ                          # ★ 鬼画符

# 2. ★ 关键:用 file 命令,查这个文件是什么编码
$ file order.csv
order.csv: ISO-8859 text, with CRLF line terminators
# ★ file 说它是 ISO-8859 —— 其实是它认不准,
#   这种"非 UTF-8 的中文文件",多半是 GBK。

# 3. ★ 看我的终端/系统,是按什么编码在解释文件
$ echo $LANG
en_US.UTF-8                                     # ★★ 我的环境是 UTF-8
$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
...
# ★ 我的终端,默认把文件内容【当作 UTF-8】来显示。

# 4. ★ 试着用 GBK 来解释这个文件
$ iconv -f GBK -t UTF-8 order.csv | head -1
公司名称,联系方式,地址                          # ★★ 中文出来了!
# ★ 用 GBK 解释 -> 转成 UTF-8 -> 完美。
#   说明这个文件,本来就是 GBK 编码存的。

# 5. ★ 直接看文件的原始字节(以"公"字为例)
$ head -c 2 order.csv | xxd
00000000: b9ab                                  # ★ "公"在文件里
# ★ 字节是 b9 ab —— 这正好是"公"字的 GBK 编码。
#   而 UTF-8 里"公"是 e5 85 ac(3 个字节),对不上。

根因(后来想清楚的):
1. ★ 文件里存的,从来不是"字",是一串【字节】
   (0~255 的数字)。比如 b9 ab 这两个字节。
2. ★ 字节本身,没有"意义"。要把字节变成人能读的
   "字",必须有一个【编码规则】当翻译:b9 ab
   ->(用 GBK 翻译)-> "公"。
3. ★ 这个文件,是同事在 Windows 上用 GBK 编码
   存的。它里头的字节,是【按 GBK 规则】写下的。
4. ★ 同事的 Excel,(中文 Windows 默认)用 GBK
   规则去翻译 -> 翻对了 -> 他看到正常中文。
5. ★ 我的 Linux 终端,LANG 是 UTF-8 -> 它拿 UTF-8
   规则去翻译那串 GBK 字节 -> 规则不对 -> 翻出
   一堆鬼画符。
6. 真相:文件没坏,字节一个没错。错的是【我用
   错了翻译规则】—— 拿 UTF-8 的规则,去读一份
   用 GBK 写下的东西。
不是文件有没有中文的问题,是"同一串字节,用
不同的编码去解释,会得到完全不同的结果"。

修复 1:文件里存的是字节——编码是"字节到字符"的翻译规则

# === ★ 本文最核心的认知:先把"字节"和"字符"分开 ===

# === ★ 一个文件,本质是一串字节 ===
# 不管它是 .txt、.csv 还是别的,一个文件在磁盘上,
#   本质就是一长串【字节】—— 每个字节是 0~255 之间
#   的一个数字。仅此而已。
$ head -c 6 order.csv | xxd
00000000: b9ab cbbe c3fb                        # ★ 文件开头 6 个字节
# ★ 文件里实实在在存着的,就是 b9 ab cb be c3 fb
#   这样一串数字。没有"字",只有数字。

# === ★ 字节,本身不带"意义" ===
# b9 这个字节,它"是什么"?★ 答案是:它什么都不是,
#   它就是个数字 185。
# ★ 它代表一个汉字的一半?代表一个拉丁字母?代表
#   一个符号?—— 字节自己【不知道】,也【不规定】。

# === ★ 编码:就是"字节 -> 字符"的一本翻译词典 ===
# 要把这串没意义的字节,变成人能读的【字符】(字),
#   必须有一本"翻译词典" —— 这就是【编码(encoding)】。
# ★ 编码规定:"看到字节 b9 ab,就翻译成汉字'公'"。
#  - ★ GBK 这本词典说:b9 ab -> "公"。
#  - ★ UTF-8 这本词典说:"公"要用 e5 85 ac 三个
#    字节表示;而 b9 ab 这个组合,UTF-8 词典里
#    查无此字 -> 翻不出来 -> 显示成乱码/替换符。

# === ★ 于是,乱码的本质,一句话说清 ===
# ★ 乱码 = 这串字节,是用【A 编码】写下的,你却
#   用【B 编码】去读它。
# ★ 文件【没有】坏,字节一个没丢、没错。错的只是
#   "读的时候,用错了那本翻译词典"。
# ★ 本文就是:文件是 GBK 写的,我的终端用 UTF-8 读。

# === ★ 一个关键推论:文件不"自带"编码 ===
# ★ 绝大多数文本文件,内部【不记录】"我是什么编码"。
#   它就是一串纯字节。
# ★ 所以"这文件是什么编码",严格说是【猜】出来的 ——
#   靠字节的特征去推断(下一节讲)。也正因如此,
#   "用错编码"才这么常见。

# === ★ "字符"还要再变成"看得见的样子" ===
# 补充一层:编码把字节翻译成"字符"(抽象的'公'这个
#   字),再由【字体】把字符画成屏幕上看得见的形状。
# ★ 本文的乱码,问题出在【字节->字符】这一层
#   (编码用错);如果是字符对了、却显示成方框 □,
#   那才是【字符->字形】那层的事(缺字体)。两层
#   别混。

# === 认知 ===
# ★ 文件里存的是一串没有意义的字节,要把字节变成
#   人能读的字符,必须用一本叫"编码"的翻译词典。
#   同一串字节,用 GBK 和用 UTF-8 两本词典翻译,
#   结果完全不同。乱码的本质就是"用 A 编码写的、
#   却用 B 编码读" —— 文件没坏,是读错了词典。

修复 2:查清一个文件到底是什么编码

# === ★ 既然文件不自带编码,就得想办法"查"出来 ===

# === ★ 第一招:file 命令 ===
$ file order.csv
order.csv: ISO-8859 text, with CRLF line terminators
$ file utf8file.txt
utf8file.txt: UTF-8 Unicode text
# ★ file 会根据字节特征,猜一个编码。
# ★ 但要清醒:file 对 UTF-8 一般准;对中文 GBK
#   常常猜成 "ISO-8859" 或 "unknown-8bit" —— 它
#   只是说"这不是 UTF-8、是某种单/双字节编码",
#   ★ 看到这种结果,中文文件基本就该怀疑 GBK。

# === ★ 第二招:enca(对中文编码识别更准)===
$ yum install enca
$ enca -L zh_CN order.csv
Simplified Chinese National Standard; GBK       # ★ 明确说是 GBK
# ★ enca 加 -L zh_CN(告诉它"按中文来识别"),
#   对 GBK / GB2312 / GB18030 的识别,比 file 准得多。

# === ★ 第三招:试着用某种编码解码,看通不通 ===
# 最朴素也最可靠的办法 —— 拿一种编码去"试读",
#   读出来是人话,就对了:
$ iconv -f GBK -t UTF-8 order.csv | head -1
公司名称,联系方式,地址                          # ★ 通了 -> 它就是 GBK
$ iconv -f UTF-8 -t UTF-8 order.csv >/dev/null
iconv: illegal input sequence at position 0     # ★ 报错 -> 它不是 UTF-8
# ★ iconv 用 -f(from)指定的编码解不通时会报错,
#   这个"报不报错",本身就是一条判断线索。

# === ★ 第四招:直接看原始字节,人工判断 ===
$ head -c 4 order.csv | xxd
00000000: b9ab cbbe                             # ★ 看字节规律
# ★ 一点经验:
#  - 字节大多在 0x00~0x7F(纯英文数字)-> ASCII,
#    什么编码读都一样,不会乱。
#  - 中文字节成对出现、每字节多在 0x81~0xFE
#    -> 很可能是 GBK(中文 2 字节)。
#  - 中文是 3 字节一组、且符合 UTF-8 的位模式
#    -> UTF-8。
$ od -c order.csv | head             # od 也能看原始字节

# === ★ 留意一个隐形的家伙:BOM ===
# 有些 UTF-8 文件开头,有 3 个字节 EF BB BF,叫
#   ★ BOM(字节顺序标记)。
$ head -c 3 file.txt | xxd
00000000: efbb bf                               # ★ 这就是 UTF-8 BOM
# ★ BOM 的坑:它对人眼"不可见",但程序读文件时,
#   会把这 3 个字节当成内容的一部分 —— 常导致
#   "第一列列名对不上""脚本第一行报错"等怪事。
#   排查文本怪问题,记得 head -c 3 看一眼有没有 BOM。

# === 认知 ===
# ★ 文件不自带编码,要靠查:file 看大类(对 UTF-8
#   准、中文常猜成 ISO-8859 就该疑 GBK),enca -L
#   zh_CN 对中文编码识别更准,iconv 试解码"通不通"
#   最可靠,xxd/od 看原始字节人工判断。还要留意
#   开头的 UTF-8 BOM(EF BB BF),它会被程序当内容。

修复 3:locale 与 LANG——你的终端按什么编码"读"

# === ★ 乱码是"两边"的事:文件是一边,你的环境是另一边 ===

# === ★ 终端显示文件,要先"解码" ===
# 你 cat 一个文件,终端拿到的是一串字节。它要把
#   字节显示成字,就得先【解码】—— 那它用哪本词典?
# ★ 答案:用【当前环境的 locale】所指定的编码。
#   在 Linux 上,这主要由环境变量 LANG / LC_CTYPE 决定。

# === ★ 看清自己环境的编码 ===
$ echo $LANG
en_US.UTF-8                          # ★ LANG 末尾的 UTF-8 是关键
$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"               # ★ LC_CTYPE 管"字符如何解释"
...
# ★ LANG 写着 .UTF-8,意思是:这个终端,默认把
#   一切文件内容,【当作 UTF-8】来解码显示。
# ★ 本文乱码的"我这一边"的原因,就在这:我的环境
#   是 UTF-8,而文件是 GBK。

# === ★ 为什么"同一个文件,他正常我乱码" ===
# ★ 现在彻底清楚了:
#  - 同事:中文 Windows,系统默认 GBK -> 用 GBK
#    解码这个 GBK 文件 -> 词典对上 -> 正常。
#  - 我:Linux 终端 LANG=UTF-8 -> 用 UTF-8 解码
#    这个 GBK 文件 -> 词典错位 -> 乱码。
# ★ 文件【始终是同一个】,字节一个没变。变的是
#   "读它的那个环境,用的是哪本词典"。

# === ★ 临时换个编码来读(不改文件、不改系统)===
# 终端模拟器(如 Xshell、iTerm、SecureCRT)自己
#   通常有"会话编码"设置 —— 把它从 UTF-8 切成 GBK,
#   再 cat 这个文件,就正常了。
# ★ 这是"治标":没动文件、也没动系统,只是让
#   "这一次的查看",换对了词典。

# === ★ 一个常被忽略的点:SSH 会把本地 locale 带过去 ===
# 你 SSH 登录服务器时,SSH 客户端常会把【你本地的
#   LANG】发送给服务器。所以你在服务器上看到的
#   locale,可能不是服务器自己的,而是你本地带过去的。
$ ssh user@host 'echo $LANG'         # 看看登过去是什么
# ★ 这解释了一类怪事:"换台电脑 SSH 上同一台服务器,
#   中文显示就不一样了" —— 是本地 locale 不同。

# === ★ 服务器本身的 locale 怎么设 ===
$ localectl status                   # 看系统级 locale
$ localectl set-locale LANG=en_US.UTF-8   # 设系统级 locale
# ★ 现代 Linux 服务器,★ 统一用 UTF-8 是绝对主流、
#   也是最该坚持的选择。GBK 只在跟老 Windows 文件
#   打交道时,临时遇到。

# === 认知 ===
# ★ 终端显示文件时,用"当前 locale(LANG/LC_CTYPE)
#   指定的编码"来解码。乱码是文件编码和环境编码两边
#   对不上 —— 同事 GBK 环境读 GBK 文件正常,我
#   UTF-8 环境读同一个 GBK 文件就乱。SSH 还会把
#   本地 locale 带到服务器。服务器统一用 UTF-8。

修复 4:iconv——把文件从一种编码转成另一种

# === ★ 治本:把文件真正转成 UTF-8 ===

# === ★ 换会话编码 vs 转文件,治标 vs 治本 ===
# ★ 修复 3 里"把终端会话编码切成 GBK" —— 只是治标:
#   你这次看对了,可文件还是 GBK,下次别人/别的
#   程序来读,照样乱。
# ★ 治本:把文件【本身】,从 GBK 转换成 UTF-8 ——
#   把字节,按新词典重写一遍。工具就是 iconv。

# === ★ iconv 基本用法 ===
# iconv -f 源编码 -t 目标编码 输入文件
$ iconv -f GBK -t UTF-8 order.csv -o order_utf8.csv
# ★ -f from(源):文件【现在】是什么编码 = GBK。
# ★ -t to(目标):想转成什么 = UTF-8。
# ★ -o:输出到新文件。★ 强烈建议输出到【新文件】,
#   不要原地覆盖 —— 万一编码猜错了,原文件还在。
$ file order_utf8.csv
order_utf8.csv: UTF-8 Unicode text              # ★ 转成功
$ cat order_utf8.csv | head -1
公司名称,联系方式,地址                          # ★ 现在 cat 也正常了

# === ★ 关键:-f 一定要填"文件真实的编码" ===
# ★ iconv 不会猜。-f 你填错了,它就拿错词典硬转,
#   转出来还是乱(甚至更乱)。
# ★ 所以顺序永远是:先用修复 2 的办法【确定真实
#   编码】,再 iconv。别跳过确认那一步。

# === ★ 转换报错怎么办:遇到无法转换的字符 ===
$ iconv -f GBK -t UTF-8 weird.csv -o out.csv
iconv: illegal input sequence at position 1234
# ★ 报错可能意味着:① 文件根本不是 GBK(-f 填错了);
#   或 ② 文件里混进了个别坏字节。
# ★ 办法之一:加 //IGNORE,跳过无法转换的字节:
$ iconv -f GBK -t UTF-8//IGNORE weird.csv -o out.csv
# ★ //IGNORE 是"丢掉转不了的" —— 应急可用,但要
#   知道它在【丢数据】,重要文件慎用,优先查清
#   -f 是不是填错了。

# === ★ 文件名也可能乱码(不只是内容)===
# 从 Windows 压缩包解压出来的文件,【文件名本身】
#   也可能是 GBK 编码的,在 UTF-8 终端下 ls 出来是乱码。
$ ls
????.txt
# ★ 解决:解压时就指定编码,例如:
$ unzip -O GBK archive.zip            # 解压时按 GBK 解文件名
$ convmv -f GBK -t UTF-8 --notest *   # 或用 convmv 批量转文件名
# ★ convmv 专门转【文件名】的编码,iconv 转【内容】,
#   两个工具,各管一摊,别用混。

# === ★ 顺带:换行符也常一起出问题(CRLF)===
# Windows 文件每行结尾是 \r\n(CRLF),Linux 是 \n。
#   本文 file 输出里那句 "with CRLF line terminators"
#   就是提示。CRLF 会让 Linux 程序读到多余的 \r。
$ dos2unix order_utf8.csv             # 把 CRLF 转成 LF
# ★ 跨平台文本,常常"编码"和"换行符"两个问题一起
#   出 —— 处理 Windows 来的文件,iconv + dos2unix
#   常常要一起上。

# === 认知 ===
# ★ 治本是用 iconv 把文件真正转成 UTF-8:iconv -f
#   源编码 -t UTF-8,-f 必须填文件真实编码(填错
#   照样乱),输出到新文件别原地覆盖。文件名乱码
#   用 convmv 或解压时 -O 指定编码。Windows 来的
#   文件常要 iconv + dos2unix 一起处理。

修复 5:乱码不止于"看"——程序、数据库、grep 全会中招

# === ★ 编码用错,远不止 cat 看着乱这一个后果 ===

# === ★ 中招 1:导入数据库 —— 乱码直接进了库 ===
# 本文最初的任务,就是把这 csv 导进数据库。如果
#   不先转码就导:
#  - ★ 文件是 GBK,数据库表/连接是 utf8mb4 -> 导进去
#    的中文,在库里就是【一堆乱码】,而且是"存进去
#    就坏了",事后很难救。
# ★ 正确做法:导入前,确保【文件编码】和【数据库
#   连接编码】一致 —— 要么先 iconv 把文件转成
#   UTF-8,要么导入时显式指定文件的字符集。
$ mysql --default-character-set=gbk ... < order.csv
# ★ 编码问题,在"数据落库"这一步犯错,代价最大 ——
#   它从"显示问题"升级成了"数据问题"。

# === ★ 中招 2:grep 搜中文搜不到 ===
# 你在一个 GBK 文件里,用 UTF-8 终端 grep 一个中文词:
$ grep '公司' order.csv
(搜不到)
# ★ 为什么:你终端里敲的"公司",是按 UTF-8 编码的
#   字节;文件里的"公司",是 GBK 字节。两串字节
#   根本不一样 -> grep 这种【按字节匹配】的工具,
#   自然匹配不上。
# ★ 解法:先把文件转成 UTF-8(和你终端一致),再 grep。

# === ★ 中招 3:程序读文件报错或读出乱码 ===
# Python、Java 等读文本文件,都要指定(或默认一个)
#   编码。默认编码和文件编码不一致:
#  - Python3:UnicodeDecodeError,直接抛异常;
#  - 有的程序:不报错,但把乱码读进了内存,继续
#    往下跑 —— ★ 这种"不报错的乱码"最危险。
# ★ 写代码读文件,★ 永远【显式】指定编码,别赖
#   默认值:open(path, encoding='gbk') 之类。

# === ★ 中招 4:文件拼接 —— 把两种编码混进一个文件 ===
# ★ 千万别把一个 GBK 文件和一个 UTF-8 文件 cat 到
#   一起:
$ cat gbk.csv utf8.csv > merged.csv      # ★ 危险操作!
# ★ 结果是一个【一半 GBK、一半 UTF-8】的文件 ——
#   这种文件,用任何【单一】编码都读不全,几乎
#   无法挽救。
# ★ 合并多个文本文件前,先确保它们【编码统一】
#   (都 iconv 成 UTF-8),再合并。

# === ★ 一条贯穿始终的原则:尽早归一到 UTF-8 ===
# ★ 最省心的策略:任何外部来的文本文件,★ 一进
#   你的系统,就第一时间 iconv 成 UTF-8。之后所有
#   环节 —— 查看、grep、入库、程序读 —— 全在 UTF-8
#   下进行,编码问题一次性了结。
# ★ 别让 GBK 文件在管道里"漂"很久,每经一个环节
#   都是一次踩坑机会。

# === 认知 ===
# ★ 编码用错的后果远不止 cat 看着乱:导数据库会让
#   乱码"存进去就坏"、grep 按字节匹配搜中文搜不到、
#   程序读文件报错或把乱码读进内存、cat 拼接不同
#   编码的文件几乎无法挽救。原则是外部文本一进系统
#   就 iconv 归一到 UTF-8,之后全程 UTF-8。

修复 6:文件编码排查纪律

# === 这次事故暴露的认知盲区,定几条纪律 ===

# === 1. ★ 文件里存的是字节,编码是"字节->字符"的翻译词典 ===

# === 2. ★ 乱码 = 用 A 编码写的、却用 B 编码读,文件本身没坏 ===

# === 3. ★ 文本文件一般不自带编码,"它是什么编码"是猜出来的 ===

# === 4. 查编码:file 看大类,enca -L zh_CN 识中文,iconv 试解码看通不通 ===
$ file 文件 ;  enca -L zh_CN 文件

# === 5. ★ 终端按当前 locale(LANG/LC_CTYPE)的编码解码显示 ===
$ echo $LANG

# === 6. ★ "他正常我乱码" = 两人环境编码不同,文件是同一个 ===

# === 7. 治本用 iconv -f 源编码 -t UTF-8 转码,-f 必须填真实编码,输出到新文件 ===
$ iconv -f GBK -t UTF-8 in.csv -o out.csv

# === 8. ★ 文件名乱码用 convmv,文件内容乱码用 iconv,别用混 ===

# === 9. ★ 入库前必须让文件编码和数据库连接编码一致,乱码入库代价最大 ===

# === 10. 排查"中文乱码"的步骤链 ===
$ file 文件                          # ① 大致什么编码
$ head -c 3 文件 | xxd               # ② 看有没有 BOM
$ echo $LANG                         # ③ 我的环境是什么编码
$ iconv -f GBK -t UTF-8 文件 | head  # ④ 试 GBK 解码,通了就是它
$ iconv -f 真实编码 -t UTF-8 -o 新文件 # ⑤ 转码归一到 UTF-8
# 按这个顺序,"中文乱码"基本能定位、能根治。

命令速查

需求                        命令
=============================================================
猜文件编码                  file 文件
中文编码精确识别            enca -L zh_CN 文件
看文件原始字节              xxd 文件 | head   或  od -c 文件
查有没有 UTF-8 BOM          head -c 3 文件 | xxd
看当前环境编码              echo $LANG / locale
转换文件编码                iconv -f GBK -t UTF-8 in -o out
转码时跳过坏字节            iconv -f GBK -t UTF-8//IGNORE ...
转换文件名编码              convmv -f GBK -t UTF-8 --notest 文件
解压时指定文件名编码        unzip -O GBK archive.zip
CRLF 换行转 LF              dos2unix 文件
设系统 locale               localectl set-locale LANG=en_US.UTF-8

口诀:文件存的是字节,编码是字节到字符的翻译词典,乱码是用错了词典文件没坏
      file/enca 查编码,iconv 转码归一到 UTF-8,外部文本一进系统就转

避坑清单

  1. 文件里存的从来不是字而是一串字节,字节本身没有意义要靠编码这本词典翻译成字符
  2. 乱码的本质是用 A 编码写下的内容却用 B 编码去读,文件本身没坏字节一个没错
  3. 绝大多数文本文件不自带编码标记,所以它是什么编码只能靠字节特征去猜去试
  4. file 命令对 UTF-8 准对中文常猜成 ISO-8859,enca -L zh_CN 识中文准,iconv 试解码最可靠
  5. 终端按当前 locale 也就是 LANG 和 LC_CTYPE 指定的编码来解码显示文件内容
  6. 同一个文件他正常我乱码,是两人环境编码不同文件始终是同一个字节没变
  7. 治本用 iconv -f 源编码 -t UTF-8 转码,-f 必须填文件真实编码填错照样乱,输出到新文件别覆盖
  8. 文件内容乱码用 iconv,文件名乱码用 convmv 或解压时 -O 指定编码,两个工具别用混
  9. 导入数据库前必须让文件编码和数据库连接编码一致,乱码一旦存进库代价最大很难救
  10. 别用 cat 把不同编码的文件拼到一起,合并文本前先全部 iconv 成 UTF-8 统一编码

总结

这次"同一个文件,他看见中文、我看见乱码"的事故,纠正了我一个关于"内容"的、近乎本能的错觉。在我过去的脑子里,一个文件,它"装着什么内容",是一件【铁板钉钉、客观存在】的事。这个 csv 文件里"有公司名称这几个中文字"——在我看来,这就像一块石头有它的重量一样,是文件【自身的、不依赖任何人】的属性。我打开它看见乱码,我的第一反应因此非常自然:文件坏了。因为如果"内容"是文件自带的客观事实,那我看见乱码,只能说明这个事实本身被破坏了。可同事那句"我这边好好的",像一根针,把我这个想法戳破了。同一个文件,字节一个没差,他那儿是公司名称,我这儿是鬼画符——如果内容是客观的,它怎么能对一个人成立、对另一个人不成立?现场逼着我承认一件我从没想过的事:文件里,根本就没有"中文"。它里头躺着的,自始至终,只是一串数字——b9 abcb be……一堆冰冷的、什么都不是的字节。"公司名称"这四个字,不在文件里。它诞生于一个【动作】:有人拿着一本叫"编码"的词典,去翻译那串字节的那一刻。同事用 GBK 那本词典翻,翻出了"公司";我用 UTF-8 那本词典翻,翻出了乱码。我们俩看到的东西不一样,不是因为文件对我们俩不一样,而是因为我们【各自带着一本不同的词典】,去读了同一串沉默的数字。复盘到根上我才明白,我一直把两样东西焊成了一个:一样是文件里【实际存着的】(那串字节),另一样是我【最终读到的】(那些字)。我以为后者就是前者,以为"读到"只是"把已经在那儿的内容,原样取出来"。可这次我才看清,"读到"从来不是"取出",而是"翻译"——中间永远隔着一本词典,而那本词典,是【我自己带来的】,不是文件给的。文件只负责把字节老老实实存好;至于这串字节"是什么意思",从来是【读的人,用他选的那本词典,赋予的】。这次最大的收获,是我对"我看到的,就是它本来的样子"这个念头,生出了一份警觉。我看到的从来不是"它本身",而是"它,经过我这本词典翻译之后的样子"。一份数据、一条消息、一个别人传来的东西,我读出来的含义,里头一定掺着我自己那套"解读规则"——我的预设、我的语境、我的默认编码。当我读出来的是"乱码"、是不通、是荒谬时,我过去总下意识地判定"是它坏了、是它错了";可这次教会我先停一下,问另一个问题:会不会,东西是好的,只是我【用错了那本词典】?发出这串字节的人,他当初是【按哪本词典】写下的?所以下一次,当某样东西在我眼里"不成立"、读起来全是乱麻时,我不会再急着说它坏了。我会先低头看看自己手里这本词典,再去问:写下它的那个人,用的是不是同一本?因为很多时候,东西一个字节都没错——错的,只是我拿来翻译它的那本词典。把词典换对,满屏的鬼画符,会一瞬间,变回清清楚楚的人话。

—— 别看了 · 2026
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理 邮箱1846861578@qq.com。
Linux教程

云盘扩容了 df 还是旧的:一次 Linux 磁盘分区与文件系统扩容的复盘

2026-5-21 0:27:30

Linux教程

rsync 一跑备份全没了:一次 --delete 与源目录未挂载的事故复盘

2026-5-21 0:38:16

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索