Please enable Javascript to view the contents

乱码了?-- 我的乱码遭遇史

 ·  ☕ 14 分钟  ·  🐞 neochin · 👀... 阅读

前言

乱码是一个让人头痛的问题,码虫们或多或少都会遇到。
在度娘上搜答案,往往不能让开发者满意,我列出了一些外网搜索有帮助的关键词:

  • unreadable characters
  • gibberish
  • garbled text
  • mojibake

本文从我学习到工作的经历中,选择了一部分的有趣的乱码遭遇,供大家围观。

[leo@leo-m ~]$ printf "\U0001F92A\U0001F92A\U0001F92A"
🤪🤪🤪

电脑求救狂喊烫烫烫

这应该是每一个c/c++初学者高概率遇到过的问题。

1
2
3
4
5
6
#include <iostream>
int main()
{
    char s[3] = {'a', 'b', 'c'};
    std::cout << s << std::endl;
}

使用visual studio 2019Debug调试模式运行结果:

我遇到这个问题时,天下还是windows xp的,开发环境使用的古老的vc++ 6.0
虽然这是一个典型问题,c/c++的字符串没有结尾,导致输出了字符串后面的一段非法内存数据,但是为什么输出“烫烫烫”呢?
作为小白的我,当然不会care这种问题,只管“照着正确的方式做就行了”。况且依当年的互联网环境可能也搜不出答案,就算是搜出答案,我也大概率看不懂。

多年以后才明白,原来是visual studio在调试模式下,使用0xCC(INT3调试指令的操作码)标注未初始化的栈上内存,以方便错误检查。
查看Why does the not allocated memory is marked like 0xCC?

那么0xCC与“烫”是什么关系?其实“烫”的GBK编码就是0xCC 0xCC,在linux下我用两种方式展示字符编码与文字的转化:

[leo@leo-m ~]$ echo -e "\xcc\xcc"|iconv -f gbk
[leo@leo-m ~]$ echo -ne "烫" | iconv -t gbk | hexdump
0000000 cccc                                   
0000002

命令解释:

  • man echo命令可以查看echo的帮助文档,-e是表示解析反斜杠转义符号(escape,这里就是指后面的\x),cc就是一个字节(BYTE)的编码,两个cc才能表示一个“烫”。这里可以使用echo输出,也可以使用printf命令。
  • man iconv命令可以查看iconv的帮助文档,-f表示指定输入的字符编码(不指定时,默认为本地locale环境所指定的编码),-t表示转换后输出的编码。
  • |管道,表示左边命令的输出作为右边命令的标准输入(stdin)。
  • hexdump 以16进制显示数据,这里可以用xxd工具替换。两者都很好用。

Linux文件名乱码

第一次尝试学习使用Linux发行版本是ubuntu 8.04,没用虚拟机,傻傻地直接安装在笔记本上,加上校园网客户端对Linux的不友好,很折腾。
各种不符合Win习惯的命令操作,加上移动硬盘上部分文件名乱码,直接把人整懵了。对于名声赫赫的Linux,我打心底觉得“真是难用”。

古老的乱码问题不知道原因,也找不到具体环境了,但文件名乱码问题偶尔还是会遇到。比如在Windows下,我使用7z压缩文件夹testtest.zip,目录结构如下:

test
├── 中文名.txt
├── 中文文件夹
│   └── 中文1.txt
└── xx.txt

Linux上使用unzip解压,便出现了乱码:

[leo@leo-m charset]$ unzip test.zip
Archive:  test.zip
   creating: test/
 extracting: test/xx.txt             
 extracting: test/╓╨╬─├√.txt  
   creating: test/�����ļ���/
 extracting: test/�����ļ���/����1.txt 

[leo@leo-m test]$ tree
.
├── \326\320\316\304\316\304\274\376\274\320
│   └── \326\320\316\3041.txt
├── ╓╨╬─├√.txt
└── xx.txt

1 directory, 3 files


WTF? 都2021年了,这么基础的操作也乱码?

这个问题也是字符编码与解码不一致造成的,在Win7z压缩是使用GBK编码,而在本地Linux上,默认使用的utf-8解码。在Linux上查看本地编码设置:

[leo@leo-m ~]$ echo $LANG
zh_CN.UTF-8

那么如何解决这个乱码问题呢?在archlinux有一个iconv的解压软件包unzip-iconv(其他发行版应该也有),能够指定编码。
使用unzip-iconv显示压缩包文件内容:

[leo@leo-m test]$ unzip -O GBK -l ../test.zip
Archive:  ../test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2021-08-23 11:42   test/
        4  2021-08-19 10:13   test/xx.txt
        9  2021-08-17 10:05   test/中文名.txt
        0  2021-08-23 11:42   test/中文文件夹/
        0  2021-08-23 11:41   test/中文文件夹/中文1.txt
---------                     -------
       13                     5 files

Win下Python输出乱码

前面的乱码问题,都是小问题,就算遇到了,睁一只眼闭一只眼,继续吃的好睡得香。可是开发过程中遇到乱码问题不解决,老板是会打屁屁的。
那时主流用python2python3还不稳定,用的人少。平时在Windows上开发,最后运行在Linux上。有时,同样的代码,Win上出问题,Linux上完好。

运行不了,出现\xHH乱码

现在看例子(代码文件用utf-8格式保存),在Windows下运行python2

1
2
s = "中文"
print(s)

运行出现错误,字符串出现了\xHH

C:\Users\leo\Desktop\test>python main.py
  File "main.py", line 1
SyntaxError: Non-ASCII character '\xe4' in file main.py on line 1, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

好吧,此时我才知道utf8这种东西,以前最多停留在c/c++charwcharvisual studio里的多字节编码Unicode,转码函数multibytetowidecharmbstowcs这种基本使用上。

我们先看中文utf-8编码,第一个字节为\xe4

[leo@leo-m ~]$ printf 中文|xxd
00000000: e4b8 ade6 9687                           ......

python2时代,默认的字符串解析,使用ASCII编码,最大表示0x7F,遇到\xe4自然不认识。查看码表:

[leo@leo-m ~]$ ascii
Usage: ascii [-adxohv] [-t] [char-alias...]
   -t = one-line output  -a = vertical format
   -d = Decimal table  -o = octal table  -x = hex table  -b binary table
   -h = This help screen -v = version information
Prints all aliases of an ASCII character. Args may be chars, C \-escapes,
English names, ^-escapes, ASCII mnemonics, or numerics in decimal/octal/hex.

Dec Hex    Dec Hex    Dec Hex  Dec Hex  Dec Hex  Dec Hex   Dec Hex   Dec Hex  
  0 00 NUL  16 10 DLE  32 20    48 30 0  64 40 @  80 50 P   96 60 `  112 70 p
  1 01 SOH  17 11 DC1  33 21 !  49 31 1  65 41 A  81 51 Q   97 61 a  113 71 q
  2 02 STX  18 12 DC2  34 22 "  50 32 2  66 42 B  82 52 R   98 62 b  114 72 r
  3 03 ETX  19 13 DC3  35 23 #  51 33 3  67 43 C  83 53 S   99 63 c  115 73 s
  4 04 EOT  20 14 DC4  36 24 $  52 34 4  68 44 D  84 54 T  100 64 d  116 74 t
  5 05 ENQ  21 15 NAK  37 25 %  53 35 5  69 45 E  85 55 U  101 65 e  117 75 u
  6 06 ACK  22 16 SYN  38 26 &  54 36 6  70 46 F  86 56 V  102 66 f  118 76 v
  7 07 BEL  23 17 ETB  39 27 '  55 37 7  71 47 G  87 57 W  103 67 g  119 77 w
  8 08 BS   24 18 CAN  40 28 (  56 38 8  72 48 H  88 58 X  104 68 h  120 78 x
  9 09 HT   25 19 EM   41 29 )  57 39 9  73 49 I  89 59 Y  105 69 i  121 79 y
 10 0A LF   26 1A SUB  42 2A *  58 3A :  74 4A J  90 5A Z  106 6A j  122 7A z
 11 0B VT   27 1B ESC  43 2B +  59 3B ;  75 4B K  91 5B [  107 6B k  123 7B {
 12 0C FF   28 1C FS   44 2C ,  60 3C <  76 4C L  92 5C \  108 6C l  124 7C |
 13 0D CR   29 1D GS   45 2D -  61 3D =  77 4D M  93 5D ]  109 6D m  125 7D }
 14 0E SO   30 1E RS   46 2E .  62 3E >  78 4E N  94 5E ^  110 6E n  126 7E ~
 15 0F SI   31 1F US   47 2F /  63 3F ?  79 4F O  95 5F _  111 6F o  127 7F DEL

修改编码,依旧乱码

既然是解析问题,我们让python2utf-8来解析字符串,添加注释# -*- coding: utf-8 -*-

1
2
3
# -*- coding: utf-8 -*-
s = "中文"
print(s)

这次代码,在Linux上运行良好,但是在Win上奇奇怪怪:

C:\Users\leo\Desktop\test>python main.py
涓枃

看着屏幕上自己的倒影,蹭亮的脑袋格外显眼,虽然物抗法抗反甲与生俱来,但是连机器世界的大佬都骗人,我们还要怎么做才能防火防盗防师妹呢?说好的跨平台,说好的移植性好呢?
然后内心防线崩溃了,开始口吐芬芳。支持个中文这么难,就像很多开源工具一样,“文件路径”不能有“空格”,不能有“中文”,python2看来也是“美帝良心”。
其实,这个问题也不是中文出现,在谷歌上,拥有世界的大韩民族,小日子过得还不错的大和民族,都跟爱嘲讽的我们一样。整个世界的非英语国家都受到了暴击。

网上最流行的方案,也是最合适的方案,就是标注字符串为unicode string

# -*- coding: utf-8 -*-
s = u"中文"
print(s)

现在问题解决了,看起来也是编码问题,这个原因要怎么理解?
Win上默认使用GBK编码,命令行中使用chcp命令,查看代码页(Code page,代码页其实就是在WinIBM等系列产品下,字符编码的另一套框架实现。cp936GBK一个是从实现框架上说,一个是字符集映射上来说,但我们简单将cp936看作是GBK编码的):

C:\Users\leo\Desktop\test>chcp
活动代码页: 936

Linux上使用UTF-8编码:

[leo@leo-m ~]$ echo $LANG
zh_CN.UTF-8

看到环境不一样,就能想到差异的由来:

  • 代码中虽然指定了coding: utf-8,但只是告诉python2如何来认识代码脚本。我们的代码文件是使用utf-8格式保存的,那就应该用utf-8来解析,不要看到越界(0 - 127, ASCII)的字符就报错。这就是使用coding: utf-8的目的。
  • 字符串中文在内存中虽然以utf-8的数据表示(0xe4 0xb8 0xad 0xe6 0x96 0x87)
  • 但是在Win上使用(比如显示、打印字符)时,根据当前代码页设置,又使用GBK编码来映射字符,所以乱码了。
  • Linux上,当前编码同样是utf-8,所以不会乱码。如果不是utf-8,乱码也会出现。

我们可以验证下,确认中文utf-8数据,用GBK编码解析看到相同的乱码:

[leo@leo-m ~]$ printf "中文"|iconv -f GBK -c
涓枃

在Linux上模拟乱码

使用LANG=zh_CN.gbk xterm命令,目地是使用zh_CN.gbk编码上下文,打开一个新的xterm终端。注意不要直接在xterm中用export LANG=zh_CN.gbk修改环境,这样没有效果,因为xterm本身不在这个环境变量下。

[leo@leo-m ~]$ echo $LANG
zh_CN.gbk
[leo@leo-m ~]$ 
[leo@leo-m ~]$ python2 /tmp/main.py 
涓枃

其他解决办法

其他解决办法类似,这里提出来,只是帮助理解这个乱码问题。

  • 使用unicode()
1
2
3
# -*- coding: utf-8 -*-
s = unicode("中文", "UTF-8")
print(s)

使用unicode()函数创建了个unicode string

  • 使用decode()
1
2
3
# -*- coding: utf-8 -*-
s = "中文".decode("UTF-8")
print(s)

utf-8编码的中文,使用utf-8解码为新的unicode string

  • 使用gbk编码
1
2
3
# -*- coding: gbk -*-
s = "中文"
print(s)

coding改为gbk,最后使用GBK编码保存文件。

网页显示乱码

有了python2的一段乱码经历,对于解决乱码问题,知道一个编码要与解码对应起来。现在觉得这个问题简单,但以前知识太薄,解决乱码很迷茫。
几年前,同事写了个网页,遇到了乱码,我拿起utf-8的刀子就开始切,最后还是受到了社会无情的鞭打。
Win上,使用gbk保存的一段html网页代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
</head>
<body>
  <p> this is 中文</p>
  <p> this is yingwen </p>
</body>
</html>

这个乱码很简单,Win上很多工具默认的文件编码为gbk,所以指定charset="UTF-8"解码不正确。
两种解决办法:
一,将文件用utf-8格式保存。推荐。
二,继续用gbk保存,把代码中的charset指定为gbk

网站系统前后端交互乱码

这是两年前遇到的问题,后端对字符串一个字节一个字节地做异或、乱序、base64/unbase64等操作,然后前端js逆向操作解码出字符串。操作很简单,但是中文出现乱码。

后端使用openresty代替nginx,虽然其它后端语言一样会出现这个问题,但我还是拿原来的环境来说明。
nginx通常被用做静态网站的容器,以及反向代理的网关。openrestynginx基础上,添加了lua脚本的支持,使之有动态内容的处理能力。比如在高并发网站系统中,与redis结合,形成后端系统的第一层缓存。

搭建openresty

这里我们测试时,可以使用docker来简单搭建环境,端口使用18880

docker run -d --name openresty -p 18880:80 \
  -v /opt/docker/openresty/conf.d:/etc/nginx/conf.d \
  openresty/openresty:alpine

lua在基础配置上添加一段location,返回一段字符串。(基础配置default.conf)如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
server {
    listen       80;
    server_name  localhost;
 
   ...

    location /test {
        content_by_lua '
            ngx.say("hello 看看中文 world");
        ';
    }
    ...

将配置文件放置到/opt/docker/openresty/conf.d/目录下。docker restart openresty重启openresty

linux终端下,测试后端配置正确,能正常访问:

[leo@leo-m ~]$ curl http://localhost:18880/test 2>/dev/null
hello 看看中文 world

测试成功后,先在浏览器访问http://localhost:18880,之后在调试窗口中才可以发起同源地址的请求。在浏览器调试窗口中发起请求:

1
2
3
fetch('http://localhost:18880/test')
.then( resp => resp.text())
.then( t => console.info( t ))


返回结果正常。

复现问题

default.conf配置中

1
ngx.say("hello 看看中文 world");

添加base64编码,改为

1
ngx.say(ngx.encode_base64("hello 看看中文 world"));

重启openresty后(docker restart openresty),终端中测试请求并解码:

[leo@leo-m ~]$ curl http://localhost:18880/test 2>/dev/null
aGVsbG8g55yL55yL5Lit5paHIHdvcmxk
[leo@leo-m ~]$ curl http://localhost:18880/test 2>/dev/null \
  | base64 --decode
hello 看看中文 world

测试成功后,在浏览器调试窗口中发起请求应使用atob解码:

1
2
3
fetch('http://localhost:18880/test')
.then( resp => resp.text())
.then( t => console.info( atob(t)  ))

中文显示乱码:

hello 看看中文 world

问题来了,为什么不使用base64时,中文正常显示,加上后却乱码了?眼花了,还是神经错乱?
现在知道为什么程序员虽是“农名工”却要拿高工资了吧?颈椎病事小,精神出问题事大。

乱码原因

还得搬出“编码不匹配”这把屠龙宝刀。

后端发送数据是utf-8编码,那么js的字符串使用什么编码?答案是utf-16(What is the default JavaScript character encoding)。

不加base64时为什么显示正常

简单一句话(在MDN上有明确描述Response.text()),在text()函数中,默认按utf-8编码转码了请求的返回数据。

但是我没有找到fetch发送数据时,转码为utf-8的描述。大佬不说,我就手动抓包测试一下。
准备发送数据的代码:

1
2
3
4
5
6
7
8
fetch('http://localhost:18880/test', {
  method: 'POST',
  headers: {
    "Content-type": "text/plain; charset=UTF-8",
  },
  body: '中文***************************************'
})
.then(resp => resp.text())

然后开启抓包(因为服务在本地,访问时也使用localhost,所以抓包的网络接口为lo的回环网口),再发送数据,得到:

[leo@leo-m ~]$ sudo tcpdump -A -X "tcp port 18880" -i lo
...
0x0000:  4500 02d8 7a25 4000 4006 bff8 7f00 0001  E...z%@.@.......
0x0010:  7f00 0001 96e6 49c0 cb75 9300 df86 66d9  ......I..u....f.
0x0020:  8018 0200 00cd 0000 0101 080a 97a0 1f94  ................
0x0030:  97a0 1f94 504f 5354 202f 7465 7374 2048  ....POST./test.H
0x0040:  5454 502f 312e 310d 0a48 6f73 743a 206c  TTP/1.1..Host:.l
0x0050:  6f63 616c 686f 7374 3a31 3838 3830 0d0a  ocalhost:18880..
0x0060:  5573 6572 2d41 6765 6e74 3a20 4d6f 7a69  User-Agent:.Mozi
0x0070:  6c6c 612f 352e 3020 2858 3131 3b20 4c69  lla/5.0.(X11;.Li
0x0080:  6e75 7820 7838 365f 3634 3b20 7276 3a39  nux.x86_64;.rv:9
0x0090:  312e 3029 2047 6563 6b6f 2f32 3031 3030  1.0).Gecko/20100
0x00a0:  3130 3120 4669 7265 666f 782f 3931 2e30  101.Firefox/91.0
0x00b0:  0d0a 4163 6365 7074 3a20 2a2f 2a0d 0a41  ..Accept:.*/*..A
0x00c0:  6363 6570 742d 4c61 6e67 7561 6765 3a20  ccept-Language:.
0x00d0:  656e 2d55 532c 656e 3b71 3d30 2e35 0d0a  en-US,en;q=0.5..
0x00e0:  4163 6365 7074 2d45 6e63 6f64 696e 673a  Accept-Encoding:
0x00f0:  2067 7a69 702c 2064 6566 6c61 7465 0d0a  .gzip,.deflate..
0x0100:  5265 6665 7265 723a 2068 7474 703a 2f2f  Referer:.http://
0x0110:  6c6f 6361 6c68 6f73 743a 3138 3838 302f  localhost:18880/
0x0120:  0d0a 436f 6e74 656e 742d 5479 7065 3a20  ..Content-Type:.
0x0130:  7465 7874 2f70 6c61 696e 3b63 6861 7273  text/plain;chars
0x0140:  6574 3d55 5446 2d38 0d0a 4f72 6967 696e  et=UTF-8..Origin
0x0150:  3a20 6874 7470 3a2f 2f6c 6f63 616c 686f  :.http://localho
0x0160:  7374 3a31 3838 3830 0d0a 436f 6e74 656e  st:18880..Conten
0x0170:  742d 4c65 6e67 7468 3a20 3435 0d0a 436f  t-Length:.45..Co
0x0180:  6e6e 6563 7469 6f6e 3a20 6b65 6570 2d61  nnection:.keep-a
0x0190:  6c69 7665 0d0a 436f 6f6b 6965 3a20 6578  live..Cookie:.ex
0x01a0:  7065 7269 6d65 6e74 6174 696f 6e5f 7375  perimentation_su
0x01b0:  626a 6563 745f 6964 3d49 6a67 305a 6a5a  bject_id=Ijg0ZjZ
0x01c0:  6959 6a4d 784c 5751 3259 546b 744e 4745  iYjMxLWQ2YTktNGE
0x01d0:  334d 4330 344f 5445 794c 5467 3059 5749  3MC04OTEyLTg0YWI
0x01e0:  304e 546b 3259 7a45 795a 4349 2533 442d  0NTk2YzEyZCI%3D-
0x01f0:  2d65 3961 3363 6465 3666 6361 3764 6265  -e9a3cde6fca7dbe
0x0200:  3036 6136 6437 6132 6533 3137 3062 6235  06a6d7a2e3170bb5
0x0210:  6238 6639 3932 3761 333b 2073 6964 6562  b8f9927a3;.sideb
0x0220:  6172 5f63 6f6c 6c61 7073 6564 3d66 616c  ar_collapsed=fal
0x0230:  7365 0d0a 5365 632d 4665 7463 682d 4465  se..Sec-Fetch-De
0x0240:  7374 3a20 656d 7074 790d 0a53 6563 2d46  st:.empty..Sec-F
0x0250:  6574 6368 2d4d 6f64 653a 2063 6f72 730d  etch-Mode:.cors.
0x0260:  0a53 6563 2d46 6574 6368 2d53 6974 653a  .Sec-Fetch-Site:
0x0270:  2073 616d 652d 6f72 6967 696e 0d0a 5072  .same-origin..Pr
0x0280:  6167 6d61 3a20 6e6f 2d63 6163 6865 0d0a  agma:.no-cache..
0x0290:  4361 6368 652d 436f 6e74 726f 6c3a 206e  Cache-Control:.n
0x02a0:  6f2d 6361 6368 650d 0a0d 0ae4 b8ad e696  o-cache.........
0x02b0:  872a 2a2a 2a2a 2a2a 2a2a 2a2a 2a2a 2a2a  .***************
0x02c0:  2a2a 2a2a 2a2a 2a2a 2a2a 2a2a 2a2a 2a2a  ****************
0x02d0:  2a2a 2a2a 2a2a 2a2a                      ********
...

看到最后几行,****前面的字节(...e696 87...),确实是utf-8编码:

[leo@leo-m ~]$ printf '\xe6\x96\x87'

看来发送时,也会默认把utf-16编码转为utf-8

为什么乱码

看到乱码,像是西欧字符,并且与Extended ASCII中字符相对应。latin系列就是玩西欧字符的,那就在终端中测试latin1解码这个utf-8数据:

[leo@leo-m ~]$ printf "hello 看看中文 world"|iconv -f latin1
hello çç中æ world

printf是在zh_CN.UTF-8编码下输出的数据,在用latin1来解析这段数据,得到相同的乱码文字。

看看中文utf-8编码:

[leo@leo-m conf.d]$ printf 看看中文| xxd
00000000: e79c 8be7 9c8b e4b8 ade6 9687            ............

再看看第一个字节\xe7,在浏览器调试窗口中,我们输入"\xe7",可以看到对应的ç乱码。

atob解码后,应该是还原了utf8的编码数据。
但是,上面不是说jsutf-16来识别这段数据吗,应该得到如下的乱码呀:

[leo@leo-m ~]$ printf 'hello 看看中文 world'|iconv -f UTF-16
敨汬鳧讜룤螖眠牯摬

这又是为什么呢?

其实atob解码后,将数据按BYTE填充UTF-16的双字节,类似与下面一段操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var s_u8 = [
  0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0xe7, 0x9c,
  0x8b, 0xe7, 0x9c, 0x8b, 0xe4, 0xb8, 0xad, 0xe6,
  0x96, 0x87, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64]
var arr = new Uint16Array(s_u8.length)
for (var i = 0; i < s_u8.length; i++) {
  arr[i] = s_u8[i]
}

var s_u16 = '';
for (var i = 0; i < arr.length; i++) {
  s_u16 += String.fromCharCode(arr[i]);
}

console.info(s_u16)

其中s_u8看看中文utf-8编码,其中每个字节都会对应到utf-16的单个字符(一个字符占两字节)。最终得到那串乱码:

然后又有一个问题,utf-16怎么就显示latin1字符了?其实utf-16latin1编码扩展,前者的低字节部分就是后者的编码。

[leo@leo-m ~]$ printf '\u00e7'
ç

修复乱码

修复该乱码问题,主要是实现utf-8utf-16转码,可以手动写,也可以用TextDecoder(),但要考虑浏览器兼容性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
fetch('http://localhost:18880/test')
.then(resp => resp.text())
.then(t => atob(t))
.then(t => {
  var decoder = new TextDecoder('utf-8');
  var arr = new Uint8Array(t.length);
  for (var i = 0; i < arr.length; i++) {
    arr[i] = t.charCodeAt(i);
  }
  return decoder.decode(arr);
})
.then(t => console.log(t))

操作说明:

  • 使用Uint8Array,还原utf-8的字符串Buffer
  • 使用TextDecoder转码。

最后

乱码问题定位,即简单又复杂。它可能有多个原因:

  • 有些乱码可能不是乱码,只是一种特殊字串。比如浏览器地址的中特殊字符转义(对应ASCII值):%3d表示=。比如linux终端的颜色格式化:\033[1;31mred text\033[0m用于输出红色的red text
  • 数据本身不是合法字符,所有编码都不能表示它。
  • 编码不匹配。编码与解码不匹配,数据与显示环境解码不匹配。文件数据可以使用chardet工具来猜测编码。通过换编码、换工具等来解决。
  • 字体不支持。西欧字体不支持中文显示,字体不支持emoji表情字体等。通过换字体、换工具来解决。

参考资料

打不开链接的,请科学上网。

您的鼓励是我最大的动力
alipay QR Code
wechat QR Code

neochin
作者
neochin
BUG Developer