👇 连享会 · 推文导航 | www.lianxh.cn
🍓 课程推荐:连享会-TFP专题:估计、识别与分解 嘉宾:董展育 (中山大学);李旭超 (武汉大学) 时间:2026 年 1 月 10, 11, 17 日 咨询:王老师 18903405450(微信)
作者: 连小白 (连享会) 邮箱: lianxhcn@163.com
Title : 中文乱码不再棘手:用 Unicode 和 UTF-8 打通 Stata、Python、R Keywords : 字符编码, ASCII, 字符串编码, 中文字符, 十进制, 十六进制, R语言
温馨提示: 文中链接在微信中无法生效。请点击底部 「阅读原文」 。或直接长按/扫描如下二维码,直达原文:
在数据分析中,大家经常遇到中文乱码问题,比如:
在 .txt 和 .xlsx 文件中能够正常显示的数据文件,导入 Stata 或 Python 后,中文字段变成了 “å¦ç”Ÿ”“æ•°æ�®” 一类的乱码; 在 Stata、Python、R 之间导入/导出数据时,明明在 Stata 中显示正常,但读入 R 或 Python 后就变成乱码; 用 Python 绘图时,图例或标题中的中文显示异常; 本文的目标是相对系统地整理这一块内容,帮助读者做到三件事:
理解 ASCII、Unicode、UTF-8 之间的关系,知道“编码问题”到底在折腾什么。 掌握一套通用的判断与选择原则:什么时候用 UTF-8,什么时候不得不用 GBK,如何识别编码错误。 在 Stata、Python、R 中,学会用少量命令完成“检测编码 → 转成 UTF-8 → 正确读写”的基本流程,从而在实际项目中减少踩坑。
1. 导言:棘手的乱码问题 很多同学第一次接触“编码问题”,往往是在如下几种情况下:
从问卷平台或历史系统导出 CSV,导入 Python 或 R 时出现 UnicodeDecodeError ;
从 Python 导出的 CSV 在 Stata 中打开后,所有中文字段都变成了“å¦ç”Ÿ”“æ•°æ�®”一类的字符串; R 写出的 CSV 在 Excel 中打开,中文出现问号或方块; Stata 14 之前的旧版本与新版本之间互传数据,部分中文字段显示异常。 这些问题的共通点在于: “字符集统一”与“存储方式匹配”没有处理好 。本质上,我们需要回答两个问题:
这份文本数据采用什么“字符集合”?也就是里面允许出现哪些符号(仅英文,还是中日韩等多语言)。 这些字符是用什么“规则”存进文件里的?即:是单字节、双字节,还是可变长度编码。 ASCII、Unicode、UTF-8 正是围绕这两个问题给出的三个不同层面的“标准”。理解这三者的关系,是解决所有编码问题的起点。
在技术层面上,一旦确定了目标策略—— “项目内部统一采用 UTF-8 编码,所有中间文本文件都转换为 UTF-8 保存” ——剩下的事情就变成了:
下面先从编码基础讲起,然后再回到 Stata、Python、R 的具体命令。
2. 编码基础:ASCII、Unicode 与 UTF-8 的核心逻辑 本节只抓住几个最关键的概念,并通过极简的例子帮助读者建立直观印象。想要进一步延伸阅读,可以参考:
ASCII 的历史与设计可以参见 ASCII 词条 ([维基百科][1]) Unicode 的整体框架与设计原则可参见 Unicode 联盟官方页面 The Unicode Standard ([Unicode][2])
UTF-8 的技术细节可以阅读 RFC 3629 “UTF-8, a transformation format of ISO 10646”([RFC Editor][3]) 2.1 ASCII:字符编码的基础框架 ASCII(American Standard Code for Information Interchange)是最早被广泛采用的字符编码标准之一,用 7 比特(即 0–127)来表示最常见的英文字母、数字、基本标点以及控制字符(如换行、回车等)。([维基百科][1])
一个简单例子 :
字符 A 的 ASCII 码是十进制 65 ,十六进制 0x41 ; 字符 a 的 ASCII 码是十进制
97 ,十六进制 0x61 ; 换行符 LF 的 ASCII 码是十进制 10 ,十六进制 0x0A 。 可以把 ASCII 理解为一张只有 128 行的小表格:每一行对应一个字符及其数值编码。后来的许多编码(包括 Unicode)都在这个基础之上扩展。
2.2 Unicode:多语言字符的统一编码体系 随着计算机在全球普及,仅用 128 个字符远远不够。不同国家和地区分别制定了各自的扩展编码(如 ISO-8859 系列、GB2312、BIG5、Shift-JIS 等),导致同一个字节序列在不同系统中可能被解释为完全不同的字符。([维基百科][4])
Unicode 的核心目标,是从“字符集合”的层面统一这些编码方案。它做了两件关键的事情:
为几乎所有语言的字符分配一个唯一的编号(码点),通常写作 U+4F60 这类形式;
不再把字符编码空间与具体语言绑定,而是试图用一个统一的标准覆盖所有文字系统。 因此可以理解为:
Unicode 是一个“巨大的字典”,里面记录了世界上常见字符的“统一编号”; 它并不直接规定“如何把这个编号写入文件”,后者由具体的存储编码(如 UTF-8、UTF-16)来完成。([Unicode][5]) 一个简单例子 :
英文字母 A 的 Unicode 码点为 U+0041 ; 汉字 “你” 的 Unicode 码点为 U+4F60 ; 汉字 “数” 的 Unicode 码点为 U+6570 。 这里仅仅是给每个字符贴了一个“全球统一的编号”,至于这个编号如何转成字节序列,则交给 UTF-8、UTF-16 等具体实现。
2.3 UTF-8:Unicode 的主流存储方式
UTF-8 是目前互联网上最常用的 Unicode 存储编码方式。它是一种可变长度编码:([RFC Editor][3])
对 ASCII 范围内的字符(0–127),UTF-8 使用 1 个字节,且编码方式与 ASCII 完全一致; 对其他字符(包括中文),使用 2–4 个字节进行表示。 这一设计带来的直接好处包括:
向后兼容 ASCII :原有只支持 ASCII 的系统,通常可以“无感知”地读取 UTF-8 编码的英文文件; 节省空间 :对于英文为主的文本,UTF-8 不会引入额外的存储开销。 一个简单例子 :
字符 A 在 UTF-8 中的字节序列是 0x41 (与 ASCII 完全相同); 汉字 “你”(码点
U+4F60 )在 UTF-8 中的字节序列是 0xE4 0xBD 0xA0 ; 汉字 “数”(码点 U+6570 )在 UTF-8 中的字节序列是 0xE6 0x95 0xB0 。 如果你用十六进制查看一个 UTF-8 文件,会看到英文字符仍然是熟悉的 ASCII 数值,而中文字符则被拆成多个字节。
2.4 ASCII、Unicode 与 UTF-8 的关联与区别 简要总结三者之间的关系,可以用下面的比喻:
ASCII:一本只有 128 个词汇的小字典,每个词有一个固定的编号,且编号用 1 个字节存储。 Unicode:一本巨大的多语言词典,给所有语言的字符都分配了唯一的编号。 UTF-8:一种“写法”,规定如何把 Unicode 的编号编码成字节序列,并保存到文件中。 从这个角度看:
Unicode 是“抽象层面的字符集合和编号标准”; ASCII 既是一种字符集,也是一种存储编码,同时又是 UTF-8 的一个子集。 在跨工具处理数据时,我们通常建议
统一采用 UTF-8 编码来存储文本文件 ,并让各工具在读写时显式声明这一点。
3. 编码应用原则:乱码识别和编码选择 3.1 编码错误的典型表现形式 在实践中,编码不匹配通常表现为以下几类情形:
乱码字符 :正常中文变成“å¦ç”Ÿ”“æ•°æ�®”一类的串; 问号或方块 :中文被显示为 ? 或空心方块,表明当前环境无法找到对应字符; 读取报错 :在 Python 中出现 UnicodeDecodeError: 'utf-8' codec can't decode bytes in position ... ,提示某些字节序列不符合 UTF-8 规范;
字段长度异常 :同样的中文字符串,在不同编码下占用的字节数不同,可能影响字符串截断、对齐或长度检查。 一旦出现这些迹象,就可以基本判断: 文件实际编码与工具在解码时的假设不一致 。
3.2 不同场景下的编码选择依据 现代数据分析环境中,编码选择的总体原则是: 能用 UTF-8,就不要用其他编码 。具体来说:
新建项目、自己生成的所有文本文件 (CSV、TXT、JSON、Markdown、Quarto 等)应统一保存为 UTF-8; 与旧系统或特定平台交互 时,如果对方只能导出/接收 GBK 等本地编码,则先按其要求生成,再在自己的工作流中转换为 UTF-8; Excel 与 Windows 环境 中,如果需要兼顾兼容性,可以使用 UTF-8-BOM,但仍应保持“内部工作文件”为 UTF-8。 判断标准很简单:
如果最终要在 Stata、Python、R 之间多次交互,统一用 UTF-8 可以大幅减少复杂度。
3.3 跨工具协作的编码统一原则 结合上述考虑,可以为跨工具协作形成几条简明的规范:
项目内所有脚本文件(.do、.py、.R、.qmd、.md)统一保存为 UTF-8; 所有中间 CSV、TXT 文件,统一转换为 UTF-8 并显式声明编码; 工具之间直接传递 .dta 文件时,一般无需额外关心编码; 对来源复杂的历史数据(问卷、业务系统导出文件等),先进行编码检测,再转换为 UTF-8 纳入工作流。 下面以 Stata、Python、R 为例,给出“检测 → 转码 → 正确读写”的基本操作流程。
4. 多工具编码实操:检测、转码与数据读写 本节给出的 Stata、Python、R 示例,都围绕“检测编码 → 转成 UTF-8 → 正确读写”这一条主线。每当提到具体命令或包时,尽量附上官方文档链接,便于读者进一步查阅参数细节。
4.1 Stata 中的编码处理 Stata 14 之后采用 Unicode 版本,内部已经使用 Unicode 表示字符;编码问题主要出现在 导入和导出文本文件 时。Stata 的官方手册中对 unicode analyze 和
unicode translate 有较系统的说明。([Stata][6])
4.1.1 编码检测 Stata 提供了 unicode analyze 命令,可以对文本文件进行编码分析。下面构造一个简单示例,用 GBK 编码保存 CSV,再检测其编码。
* 生成示例数据 clear input str10 name str8 value "张三" "100" "李四" "200" "王五" "300" end * 导出为 GBK 编码的 CSV(模拟旧系统或问卷平台) export delimited using "example_stata_gbk.csv", /// replace encoding(gbk) * 检测文件编码 unicode analyze "example_stata_gbk.csv" unicode analyze 会返回检测结果,当文件为 GBK 编码时,输出中会给出相应提示。
4.1.2 编码转换 将 GBK 编码文件转换为 UTF-8,可以使用 unicode translate :
* 设置原始编码为 GBK,转换为 UTF-8 unicode encoding set gbk unicode translate "example_stata_gbk.csv", /// output("example_stata_utf8.csv") 转换完成后,
example_stata_utf8.csv 即为 UTF-8 编码文件,可被其他工具直接读取。
4.1.3 数据读写的编码设置 在导入和导出文本文件时,应显式指定编码格式。以下是推荐写法:
* 导入 UTF-8 编码的 CSV 文件 import delimited using "example_stata_utf8.csv", /// clear encoding(utf-8) * 导出 UTF-8 编码的 CSV 文件 export delimited using "output_stata_utf8.csv", /// replace encoding(utf-8) * 保存 .dta 文件(内部为 Unicode 编码) save "output_stata.dta", replace 需要强调的是:
.dta 文件是 Stata 自己的二进制格式,内部会记录必要的编码信息;([Stata][7]) 在 Stata 与 Python、R 之间传递 .dta 时,一般不需要再手动处理编码问题; 编码问题主要集中在 CSV、TXT 等纯文本文件上。 4.2 Python 中的编码处理 在 Python 3 中,字符串类型默认就是 Unicode,编码问题主要出现在 文件读写 阶段。常见的做法是:
使用编码检测库(如 chardet )对已有文件进行检测; ([GitHub][8]) 在 open 、 pandas.read_csv 等操作中显式指定 encoding 参数;其中 pandas 的底层设计可参见 McKinney 关于数据结构的论文。([proceedings.scipy.org][9]) 4.2.1 编码检测 chardet 是一个常用的编码检测库,可以对字节序列进行统计推断。需要注意,它给出的是“最佳猜测”,对于短文件或混合编码文件可能不够准确。
import chardet import pandas as pd # 生成示例数据,并以 GBK 保存 data = { "name" : [ "张三" , "李四" , "王五" ], "value" : [ 100 , 200 , 300 ]} df = pd.DataFrame(data) df.to_csv( "example_python_gbk.csv" , index= False , encoding= "gbk" ) # 检测文件编码 with open( "example_python_gbk.csv" , "rb" ) as f: result = chardet.detect(f.read()) print( "文件编码:" , result[ "encoding" ]) print( "置信度:" , result[ "confidence" ]) 如果检测结果为 GBK 或类似编码,就可以按该编码去解码并转换为 UTF-8。
4.2.2 编码转换 将 GBK 编码文件转换为 UTF-8 的基本思路是:用正确的原始编码读入为 Python 字符串,再用 UTF-8 编码写出。
# 将 GBK 编码文件转为 UTF-8 编码 with open( "example_python_gbk.csv" , "r" , encoding= "gbk" ) as f: text = f.read() with open( "example_python_utf8.csv" , "w" , encoding= "utf-8" ) as f: f.write(text) 对于大文件,可以按行或按块处理,但原理完全相同。
4.2.3 数据读写的编码设置 在日常数据分析中,可以通过 pandas
统一处理 CSV 与 .dta 文件。([proceedings.scipy.org][9])
import pandas as pd # 读取 UTF-8 编码 CSV 文件 df_utf8 = pd.read_csv( "example_python_utf8.csv" , encoding= "utf-8" ) print(df_utf8) # 读取 Stata 生成的 .dta 文件 df_stata = pd.read_stata( "example_stata.dta" ) print(df_stata) # 导出 UTF-8 编码 CSV 文件 df_utf8.to_csv( "output_python_utf8.csv" , index= False , encoding= "utf-8" ) # 导出 .dta 文件供 Stata 使用 df_utf8.to_stata( "output_python.dta" , write_index= False ) 在一个以 UTF-8 为主的项目中,推荐做法是:
所有中间 CSV、TXT 文件一律用 UTF-8 存储; 读写时显式写出 encoding="utf-8" ,避免依赖系统默认值。 4.3 R 中的编码处理 R 在不同操作系统上的默认编码并不完全一致(尤其是 Windows 环境)。为确保跨平台与跨工具的一致性,建议始终在读写文本文件时显式指定 fileEncoding 。
本节使用的两个主要扩展包是:
stringi :高性能、跨平台的字符串处理工具包; ([统计软件期刊][10]) haven :基于 ReadStat 的 .dta 等格式读写包,是 tidyverse 生态的重要组成部分。([Haven][11]) 4.3.1 编码检测 stringi 包提供了方便的文件编码检测函数 stri_enc_detect_file 。下面同样先构造 GBK 文件,再进行检测。
library (stringi) library (haven) # 生成示例数据,并写出为 GBK 编码的 CSV data name = c( "张三" , "李四" , "王五" ), value = c( 100 , 200 , 300 ) ) write.csv( data, "example_r_gbk.csv" , row.names = FALSE , fileEncoding =
"GBK" ) # 检测文件编码 enc_result "example_r_gbk.csv" ) print(enc_result) enc_result 会给出若干候选编码及其置信度,通常第一行的编码即为最可能的结果。
4.3.2 编码转换 在确认原始编码为 GBK 后,可以用 read.csv 读入,再用 UTF-8 写出:
# 读取 GBK 编码 CSV 文件 df_gbk "example_r_gbk.csv" , fileEncoding = "GBK" , stringsAsFactors = FALSE ) # 写出为 UTF-8 编码 CSV 文件 write.csv( df_gbk, "example_r_utf8.csv" , row.names = FALSE , fileEncoding = "UTF-8" ) 如果考虑到 Windows 下 Excel 的兼容性,可以在导出给 Excel 使用时改用 UTF-8-BOM :
write.csv( df_gbk, "example_r_utf8_bom.csv" , row.names = FALSE , fileEncoding = "UTF-8-BOM" )
这样在多数中文 Windows 环境中,Excel 直接双击打开即可正确显示中文。
4.3.3 数据读写的编码设置 在基于 R 的日常工作流中,可以按如下方式组织读写操作:
library (haven) # 读取 UTF-8 编码 CSV 文件 df_utf8 "example_r_utf8.csv" , fileEncoding = "UTF-8" , stringsAsFactors = FALSE ) print(df_utf8) # 读取 Stata 生成的 .dta 文件 df_stata "example_stata.dta" ) print(df_stata) # 导出 UTF-8 编码 CSV 文件 write.csv( df_utf8, "output_r_utf8.csv" , row.names = FALSE , fileEncoding = "UTF-8" ) # 导出 UTF-8-BOM 编码 CSV 文件(面向 Excel 用户) write.csv( df_utf8, "output_r_utf8_bom.csv" , row.names = FALSE , fileEncoding = "UTF-8-BOM" ) # 导出 .dta 文件供 Stata 使用 write_dta(df_utf8, "output_r.dta" ) 通过这样的设置,可以确保 R 与 Stata 之间基于 .dta 文件的交互尽量不受编码问题影响,而与其他工具之间的文本交互则统一采用 UTF-8。
5. 常见问题与问答
Q1:如何快速判断一个 CSV 文件的编码?
在 Stata 中,可以用 unicode analyze 文件名 ;
在 Python 中,可用 chardet.detect 对文件字节进行检测;
在 R 中,可以用 stringi::stri_enc_detect_file 。
虽然检测结果并非绝对准确,但结合数据来源信息通常足够使用。
Q2:Stata 打开 Python 生成的 CSV 文件出现中文乱码怎么办?
首先,在 Python 保存时显式指定 encoding="utf-8" ,例如
df.to_csv( "file.csv" , index= False , encoding= "utf-8" )` 然后,在 Stata 中导入时使用
import delimited using "file.csv", clear encoding(utf-8) 这样可以确保写入和读取双方对编码的假设一致。
Q3:R 导出的 CSV 文件在 Excel 中显示异常怎么办?
如果目标是 Windows 上的 Excel,可以在 R 中导出时使用 fileEncoding = "UTF-8-BOM" ,例如:
write.csv(df, "file.csv", row.names = FALSE, fileEncoding = "UTF-8-BOM") 同时,在 Excel 中也可以通过 “数据 → 自文本/CSV” 导入时手动指定编码为 UTF-8。
Q4:旧版 Stata(13 及以下)如何处理中文数据?
旧版 Stata 不是 Unicode 版本,处理中文时需要更多人工干预。一般做法是将中文文本转为 GBK 等本地编码,在导入文本文件时使用
encoding(gbk) 等参数;从长期看,更推荐升级到 Stata 14 及以上版本,并在项目中全面采用 UTF-8。([Stata][7])
如果有大量 Stata 13 及以下版本生成的 .do 和 .dta 文件,可以参考如下推文进行转换:
于翔, 连玉君, 2020, Stata中文乱码转码顽疾解决方法-批量转换. 王美庭, 2020, 赶尽杀绝:Stata中文乱码之转码. 连享会, 2020, Stata15-Unicode:一次性转码解决中文乱码问题. Q5:跨工具传输 .dta 文件是否需要调整编码?
通常不需要。 .dta 是 Stata 的二进制数据格式,内部会记录必要的元信息。Python( pandas.read_stata )、R( haven::read_dta
)都可以直接读取,并自动将内容映射为各自环境下的字符串类型。编码问题主要集中在 CSV、TXT 等纯文本文件上。
6. 结语 字符编码问题看起来琐碎,但处理得好,可以让整个数据分析和文本处理工作流更加稳健、可重复;处理不好,则会在项目的各个环节制造隐蔽且难以复现的错误。
本篇文章试图在“原理”与“实操”之间找到一个平衡点:
一方面,通过梳理 ASCII、Unicode、UTF-8 的基本逻辑,帮助读者建立一个清晰的概念框架; 另一方面,通过 Stata、Python、R 的具体代码示例,展示在主流工具中如何完成编码检测、转码和统一读写。 如果要用一句话概括本文的核心建议,那就是:
在跨工具数据分析项目中,尽量统一采用 UTF-8 编码,所有中间文本文件在进入工作流前先转换为 UTF-8,并在各工具读写时显式声明编码。
只要遵循这条简单的原则,配合文中给出的检测与转码命令,绝大多数“中文乱码”问题都可以被系统性地避免,而不再是一次次随机出现的“隐性 bug”。
7. 参考文献 Needleman, M. (2000). The Unicode Standard. Serials Review , 26(2), 51–54. Link, PDF, Google.
Yergeau, F. (2003). UTF-8, a transformation format of ISO 10646. RFC 3629 . Link, PDF, Google.
Gagolewski, M. (2022). stringi: Fast and portable character string processing in R. Journal of Statistical Software , 103(2), 1–59. Link, PDF, Google.
McKinney, W. (2010). Data Structures for Statistical Computing in Python. In Proceedings of the 9th Python in Science Conference , 56–61. Link, PDF, Google.
Wickham, H., Miller, E., & Smith, D. (2018). Haven: Import and Export “SPSS”, “Stata” and “SAS” Files. The R Journal , 10(2), 395–411. Link, PDF, Google.
8. 相关推文
Note:产生如下推文列表的 Stata 命令为: lianxh unicode 字符 乱码 编码, ex(地图 API) md2 nocat 安装最新版 lianxh 命令: ssc install lianxh, replace
于翔, 连玉君, 2020, Stata中文乱码转码顽疾解决方法-批量转换. 左祥太, 2021, Stata数据处理:快速合并与编码-encodefrom. 李依, 2022, Stata数据处理:将字符变量编码为数值变量-encoder. 林佳辉, 2020, Stata数据处理:字符型日期变量的转换. 汪京, 2024, jregex:用正则表达式快速实现匹配和替换. 王美庭, 2020, 赶尽杀绝:Stata中文乱码之转码. 胡雨霄, 2020, Stata:产生唯一数据编码的三种方法. 连享会, 2020, Stata15-Unicode:一次性转码解决中文乱码问题. 连玉君, 2020, Stata基础:一些漂亮的字符画. 连玉君, 2021, Stata安装路径中不要包含中文字符和空格:stacktrace not available错误信息. 连玉君, 2021, Stata错误信息-stacktrace not available:安装路径中不要包含中文字符和空格. 连玉君, 2024, TeXStudio:无法正常显示中文字符怎么办?. 连玉君, 2023, 字符函数ustrregexm():文字变量匹配中的英文字母大小写问题. 连玉君, 2020, 字符编码问题三个不可见的字符(0xEF-0xBB-0xBF,即BOM). 陈勇吏, 2020, 七条建议:用Stata处理文字变量和字符变量.
🍓 课程推荐:连享会:2025 文本分析专题 嘉宾:陈婷,香港浸会大学 时间:2025 年 11.22, 11.29, 12.6 日 咨询:王老师 18903405450(微信)
连享会微信小店上线啦! Note:扫一扫进入“连享会微信小店”,你想学的课程在这里······
尊敬的老师 / 亲爱的同学们:
连享会致力于不断优化和丰富课程内容,为了更精准地满足您的学习需求,我们诚挚地邀请您参与到我们的课程规划中来。 请您在下面的问卷中,分享您感兴趣的学习主题或您希望深入了解的知识领域 。感谢您抽出宝贵时间,与我们共同塑造更加精彩的学习旅程!https://www.wjx.cn/vm/YgPfdsJ.aspx# 再次感谢大家宝贵的意见!
New! Stata 搜索神器: lianxh 和
songbl GIF 动图介绍 搜: 推文、数据分享、期刊论文、重现代码 …… 👉 安装: . ssc install lianxh . ssc install songbl 👉 使用: . lianxh DID 倍分法 . songbl all
🍏 关于我们 直通车: 👉【 百度一下: 连享会 】即可直达连享会主页。亦可进一步添加 「知乎」,「b 站」,「面板数据」,「公开课」 等关键词细化搜索。