以前做项目时遇到的,分享一下个人经验
命令
mysqldump -u root -p {DATABASE_NAME} > db.sql
终端
powershell 5.1
问题
中文出现乱码,如正确版本为:
`gender` enum('男','女') NOT NULL,
乱码:
`gender` enum('鐢?,'濂?) NOT NULL,
导入错误原因分析
mysqldump输出UTF-8字节流(utf8mb4,由数据库配置决定),但总之不输出也无法输入UTF-16。
powershell的重定向操作默认使用UTF-16,可通过Format-Hex命令检查。不论是否能看到乱码,用这种方式导入都大概率报错。参见 charset-connection、mysqldump。
对于ASCII字符,以【S】为例,UTF-8的字符【S】被编码为53,UTF-16的字符【S】则会被编码为53 00,mysql导入时会认为这是53和00即【S】 【nul】而抛出:
ASCII '\\0' appeared in the statement, but this is not allowed unless option –binary-mode is enabled and mysql is run in non-interactive mode. Set –binary-mode to 1 if ASCII '\\0' is expected. Query: '?-'.
解决方案
乱码原因分析
事实上,【'鐢?】并非【'男'】从UTF-8强行转换到UTF-16的结果,官方文档中的UTF-16仅解释了导入失败的原因,而没有解释乱码的原因。
首先检查相关字段的编码值,python脚本如下:
ch = ""
for enc in [
"utf-8",
"utf-16le",
"utf-16be",
"utf-16",
"gb2312",
"gbk",
]:
try:
b = ch.encode(enc)
print(enc, b.hex(" "))
except:
pass
输出:
男
utf-8 e7 94 b7
utf-16le 37 75
utf-16be 75 37
utf-16 ff fe 37 75
gb2312 c4 d0
gbk c4 d0
鐢
utf-8 e9 90 a2
utf-16le 22 94
utf-16be 94 22
utf-16 ff fe 22 94
gbk e7 94
女
utf-8 e5 a5 b3
utf-16le 73 59
utf-16be 59 73
utf-16 ff fe 73 59
gb2312 c5 ae
gbk c5 ae
濂
utf-8 e6 bf 82
utf-16le c2 6f
utf-16be 6f c2
utf-16 ff fe c2 6f
gb2312 e5 a5
gbk e5 a5
通过Format-Hex命令检查输出sql文件的内容,为简化输出我们只保留【'鐢?,'濂?】的结果:
00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000 FF FE 27 00 22 94 3F 00 2C 00 27 00 C2 6F 3F 00 .þ'."?.,.'.Âo?.
00000010 0D 00 0A 00 ….
结果确实如官方文档所说是UTF-16编码。【'鐢?,'濂?】中乱码对应的字节码分别为22 94 3F 00和C2 6F 3F 00,这对应的是【鐢】和【濂】的UTF-16 LE编码。这表明乱码并非→UTF-16这一步产生的,→UTF-16这个步骤忠实地将字符【鐢】的编码转换到UTF-16了。乱码发生在更早的步骤。
注意到字符【男】【女】的UTF-8和字符【鐢】【濂】的GBK存在重叠子串,这表明操作过程中存在某个步骤将mysqldump的字节流以GBK编码输出。通过[Console]::OutputEncoding检查:
BodyName : gb2312
EncodingName : 简体中文(GB2312)
HeaderName : gb2312
WebName : gb2312
WindowsCodePage : 936
IsBrowserDisplay : True
IsBrowserSave : True
IsMailNewsDisplay : True
IsMailNewsSave : True
IsSingleByte : False
EncoderFallback : System.Text.InternalEncoderBestFitFallback
DecoderFallback : System.Text.InternalDecoderBestFitFallback
IsReadOnly : False
CodePage : 936
Console.OutputEncoding用于获取或设置控制台用于写入输出的编码,默认代码页由系统区域设置确定,为936。当powershell接收来自mysqldump的字节流后,需要通过代码页将字节转换为unicode进行输出,这个过程中依赖代码页936,对原本UTF-8编码生成的字节流发生了错误转换。如果只是不想看到乱码,解决方法为:
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
即使如此,仍面临UTF-8→UTF-16的00解析问题,所以依然建议采用前述解决方案。
总结
解析流程:UTF-8→{codepage}→UTF-16。
导入失败原因:UTF-8→UTF-16出现mysql不接受的字符。
乱码原因:powershell 5.1默认配置下codepage为936,UTF-8→GBK输出错误。




