第一次写segmentfault,欢迎大家提意见以便改进。
今天要讨论的是关于字符编码的一些问题,源自一次项目周会,因网络上没有相关文章,现在刚好能总结一下。
1. 首先来看几张有意思的现象:
(1) 下面这幅图中出现一些“叠起来”的字体,来自微信公众号“麦当劳”于2017年6月28日的推送,感兴趣的同学可以去看看。
比如第一个“叠起来”的字,分别由以下两个字体组成,其它的“特技字体”也一样。
(2) 再看下面这个字,我把它复制粘贴到这:
小⃠
在Chrome浏览器看到的可能是下面这样:
在iPhone微信和Safari等平台或浏览器上看到的可能是:
在某些Android微信上看到的可能是:
你可以复制这个网站地址,或者可以复制字符串"小⃠"到不同平台上观察显示的效果。
(3) 连续打两个字符,如:
Chrome浏览器显示:
Sarafi浏览器显示:
在写这篇blog的时候,我也很苦恼,怎么写出这个"?"符号,因为经常会和前面的一个字符“叠起来”,会造成误解。
(4) 复制“小⃠”到Chrome地址栏时,显示倒是“正常”:
但是,上图是地址栏获得焦点时的情况,如果此时失去焦点会发生如下情况:
2. 分析
(1) 不妨先用内置的encodeURIComponent()
和 decodeURIComponent()
探个究竟。
总的来说,这个“叠起来”的字符解码之后是这个结果(为了便于查看,这里用Safari进行测试):
至于%E2%83%A0
,我用Chrome控制台输出,因为在Safari下编辑器处理这段编码的结果会把写注释的'/'叠上,如图:
(2) 翻阅了许多资料终于在Wikipedia找到相关内容,https://en.wikipedia.org/wiki...
其中讲到一些背景因素,以及一些奇葩的组合类型:
In digital typography, combining characters are characters that are intended to modify other characters. The most common combining characters in the Latin script are the combining diacritical marks (including combining accents).
Unicode also contains many precomposed characters, so that in many cases it is possible to use both combining diacritics and precomposed characters, at the user's or application's choice. This leads to a requirement to perform Unicode normalization before comparing two Unicode strings and to carefully design encoding converters to correctly map all of the valid ways to represent a character in Unicode to a legacy encoding to avoid data loss.
In Unicode, the main block of combining diacritics for European languages and the International Phonetic Alphabet is U+0300–U+036F. Combining diacritical marks are also present in many other blocks of Unicode characters. In Unicode, diacritics are always added after the main character, so it is possible to add several diacritics to the same character, although as of 2010, few applications support correct rendering of such combinations.
3. 看到这也大致了解了 Combining Character
,不妨再看看Web开发中常见的乱码:
有一次,我在微信公众号开发项目中,指定回复消息为文本格式的时候,尝试了几种换行方式都不行,最终了解即XML的换行应使用:



是字符实体编号(16进制),可以用于处理XML中文本的换行。
对应的正确代码在第9行(部分文字有修改):
<xml>
<ToUserName><![CDATA[<%= toUserName %>]]></ToUserName>
<FromUserName><![CDATA[<%= fromUserName %>]]></FromUserName>
<CreateTime><% createTime %></CreateTime>
<MsgType><![CDATA[<%= msgType %>]]></MsgType>
<% if (msgType === 'text') { if(content!=="zs") { %>
<Content><![CDATA[<%= content %>]]></Content>
<% } else { %>
<Content>欢迎来到报名图书馆暑假工!


报名步骤:

①将招聘推文转发至朋友圈或者40人以上的群,让更多同学了解本招聘。为招聘方宣传以找到更多优质学生员工。

→<a href="http://a.xiumi.us/board/v5/29Ndm/47380885">点此进入招聘推文</a>

②回复你的资料:报名+姓名+电话号码+深圳哪个区+可上班时间


</Content>
<% }} else if (msgType === 'zs') { %>
<Content><a href="http://www.youzan.com">ddwadwada</a></Content>
<% } else if (msgType === 'image') { %>
<Image>
<MediaId><![CDATA[<%= content.mediaId %>]]></MediaId>
</Image>
...
</xml>
由这个问题,我们想到web开发中还有一些类似的“乱码”,这些乱码又有哪些规律呢?
(1) 字符实体
字符实体是XML和HTML中的字符编码方式,也就是上面事例中提到的,格式为:
& + 实体名称 + ;
& + (# + unicode编码) + ;
实体名称一般是有意义的词,方便大家记忆,比如小于号<的实体名称是lt,也就是less than的缩写。只有部分符号是有实体名称的,使用unicode编码是更通用的写法。
像文字类一般不会采用这种编码方式,主要用于在HTML或XML文档中输出一些保留字符和空格,比如我想在HTML中展示一段html代码就需要使用字符实体。
比如我们要展示<p>情深深雨蒙蒙</p>
以下两种表示是等效的:
<pre>
<p>情深深雨蒙蒙</p>
<p>情深深雨蒙蒙</p>
</pre>
总而言之,字符实体是HTML和XML中的编码方式,比如在HTML文档中写入:我,那么最终页面上看到的是我这个汉字。
(2) unicode字符
编程语言中的unicode字符的格式为:
\u + 16进制unicode编码
绝大多数编程语言,包括CSS中都支持unicode字符,不过HTML和XML是不支持的。那么什么时候使用unicode字符呢?一般来说有两种场景:
- 避免文件保存时采用不同编码导致的乱码,因为u已经声明了是unicode。
- 正则匹配中的一些应用:Unicode编码及在正则表达式中的使用
在JS中可以使用charCodeAt()获取字符串的10进制unicode编码
(3) URL编码
类似%E6%88%91
这样的,叫做URL编码,在链接的参数里非常常见
网络标准RFC 1738做了硬性规定:
“只有字母和数字[0-9a-zA-Z]、一些特殊符号”$-_.+!*'(),”[不包括双引号]、以及某些保留字,才可以不经过编码直接用于URL。”
所以像汉字,空格这些都必须经过转码。上面讲的unicode字符,字符实体用的都是unicode编号,而URL编码用的则是utf-8, 规则是将utf-8编码每隔两个字符加一个%
UTF 是英文 Unicode Transformation Format 的缩写,意为把 Unicode 字符转换为某种格式。unicode和utf-8并不是同一种东西,但是又存在着联系:unicode是信源编码,对字符集数字化; utf-8,utf-16这些是信道编码,为更好的存储和传输。
简单说,unicode就是一组数字,每一个数字对应一个字符。utf-8就是对字符的传输和保存时的规则。比如说“我”这个字,unicode码(16进制)是6211,utf-8是E68891, 那么对应的URL编码就是%E6%88%91;
{
Unicode编码: 0x6211,
UTF8编码: E68891,
UTF16编码: FEFF6211,
UTF32编码: 0000FEFF00006211
URL编码: %E6%88%91
}
(4) 本段小结:
Web开发中常见的几种乱码包括:Unicode字符、字符实体、URL编码。如以下情况都表示“我”:
Unicode字符: \u6211
字符实体编号(16进制):我
字符实体编号(10进制):我
URL编码:%E6%88%91
这些编码规则的本质都是一些特殊符号 + Unicode编码 所组成。
4. 总结
从【1】中展示的,各种奇怪现象我们找到原因和资料是Combining Character以及编码相关问题,其次我们也拓展了一下Web开发中常见的一些”乱码”以及相关的技术背景。
欢迎大家指正或提意见以便改进。
5. 延伸阅读
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。