UTF-8人人用,乱码却频频出现,这5个坑你避开了吗?

老实说,UTF-8这东西天天见,但真搞明白的人不多。我以前也以为它就是个标准编码,结果写代码时总遇到奇奇怪怪的问题。今天就用大白话聊聊它的原理和常见的坑。

打开网易新闻 查看精彩图片

先说说UTF-8的原理,其实不难理解。它设计成变长的,就是为了兼容英文的ASCII码,同时又能表示全世界各种字符。比如英文字母用1个字节,中文字用3个字节,表情符号用4个字节。这样既省空间,又能统一。

怎么区分一个字符占几个字节呢?看第一个字节的开头几个位就行。0开头就是1字节,110开头就是2字节,1110开头是3字节,11110开头是4字节。后面的字节都以10开头。这个规则记不住也没事,重点是要知道字符不是等长的。

打开网易新闻 查看精彩图片

我踩过的第一个坑就是误把字节数当字符数。有次做文本输入框,限制用户输入100个字符,我直接用strlen()判断长度。结果中文用户输入几个字就提示超了,因为一个汉字占3个字节。正确的做法是遍历统计码点个数,或者用语言自带的字符数函数。

打开网易新闻 查看精彩图片

第二个坑是半字符截断。从网络流里按固定字节大小读取数据,经常把一个汉字切成两半,导致后面全是乱码。解决方法是读取时找字符边界,或者用专门的库函数安全截断。

打开网易新闻 查看精彩图片

第三个坑是正则表达式匹配失败。我写过一段代码用[a-z]匹配字母,结果带重音的é匹配不上。后来才知道正则默认是逐字节匹配,要启用Unicode模式,比如加个u标志,或者用\p{L}匹配所有字母。

打开网易新闻 查看精彩图片

第四个坑是BOM引发的幻觉。Windows的记事本默认会给UTF-8文件开头加三个字节(EF BB BF),Linux下用cat看文件第一行会莫名其妙出现。更坑的是脚本文件开头如果有BOM,shebang行就失效了。解决办法是在读写时明确指定编码,比如Python用utf-8-sig自动处理。

打开网易新闻 查看精彩图片

第五个坑是数据库编码不统一。有次把UTF-8数据存到MySQL的latin1列里,取出来全是问号。后来统一把表、连接、客户端都设成utf8mb4才解决。

打开网易新闻 查看精彩图片

说到BOM,很多人觉得它没用还添乱。BOM本身是Unicode用来标记字节顺序的,但在UTF-8里它唯一的作用就是告诉别人这文件是UTF-8编码。支持的人说它能消除歧义,特别在Windows环境很常见。反对的人说它破坏格式、浪费空间、Unix工具不兼容。我个人的看法是,看使用场景:团队内部文件可以不带BOM,跟Windows交换文档就带上,Web资源和服务端脚本一律无BOM。

打开网易新闻 查看精彩图片

为了避免这些问题,我养成了一些习惯。项目一初始化就用.editorconfig强制编码为UTF-8,编辑器设置成无BOM保存。Git里配好autocrlf,防止换行符捣乱。写代码时遇到字符串处理就写单元测试,专门测截断、长度和正则。用户上传的文件,我都先检测编码,不是UTF-8就转码。

打开网易新闻 查看精彩图片

数据库迁移脚本里明确声明字符集,Web服务器加Content-Type头。部署后用file -i命令检查文件实际编码,心里有个底。

打开网易新闻 查看精彩图片

说到底,UTF-8不是学一次就完事的知识,得在项目里反复验证。遇到乱码别用玄学解释,查编码、看字节,一步步排查。记住一条:永远别相信默认编码,明确指定比凭直觉靠谱多了。

打开网易新闻 查看精彩图片