首页 程序笔记 源代码中文注释出现:“烫烫烫”,“屯屯屯”,“锟斤拷”等中文乱码字符

源代码中文注释出现:“烫烫烫”,“屯屯屯”,“锟斤拷”等中文乱码字符

摘要

在开发中,经常打开一个有中文注释的源代码,注释中经常会出现诸如“烫烫烫”,“屯屯屯”,“锟斤拷”等很奇怪的中文字符,毫无疑问,你遇到中文乱码了,为什么会乱码字符呢?那就得从文件的编码方式说起了。

典型乱码

乱码、问号、方块:用文本编辑器打开一个文本文件,如果文件的编码方式不兼容,有时候会看到???的东西,有时候会看到很混乱或者不认识的文字,通常我们就统称乱码了。怎么用编码的知识来理解呢?
很多编码方式都用的是变长字节编码,很多字节都要结合它的上下文去解释才是对的。例如:用UTF-8的算法去解析GBK的文件,就很容易发些这么些种情况:

  • 一个字节序列并不是合法的UTF-8字符,比如以11111110开头的字节序列。
  • 一个字节序列碰巧符合UTF-8规则。
  • 反过来看,用GBK的算法去解析UTF-8的文件其实也差不多,遇到第一种情况在显示的时候可能就用问号代替,而遇到第二种情况就是出现一些风马牛不相及的杂乱文字。

方块其实和问号本质上一样的,但方块在现代浏览器里还有个很常见的情况,就是一个字符的编号在字体当中并没有定义,于是在排版和渲染的适合“智能”地用一个方块来表示它了。看到方块可以结合上下文,如果上下文当中的非英字符显示正确的,那么方块可能是一些特殊符号,比如Emoji。

在写服务端程序的时候“半个字符”的问题要小心处理。例如,我们在前级对超长的数据进行截断处理,刚好将一个变长编码的字节序列截断掉了,就会出现“半个字符”。一般半个字符都是肯定会乱码的,一些容错比较差的程序甚至会直接挂掉,比如一些做的不好的PHP的C扩展,严重的时候会出core。所以程序不懂编码就别瞎截,甚至考虑到某些语言文字里的组合字符,就是知道编码也别瞎截(真是细思恐极);

BOM

BOM(Byte-Order Mark,字节序标记)是Unicode码点U+FEFF。它被定义来放在一个UTF-16文件的开头,如果字节序列是FEFF那么这个文件就是大端序,如果字节序列是FFFE那么这个文件就是小端序。

UTF-8本身是没有字节序的问题的(因为它是以单个字节为最小单位),但是Windows里面诸如记事本之类的很多编辑器会多此一举的在UTF-8文件开头加入EF BB FF也就是U+FEFF的UTF-8编码。

如果你的源代码文件里面有一个这东西你就倒了大霉了,可能会:

  • 什么也看不见,可能是PHP引擎根本处理不了这个源代码。
  • 页面展现错乱的情况,一般是因为在之前输出的非空格内容造成了浏览器选择错误的doctype。
  • 页面上面有及格乱七八糟的字符,浏览器把它当字符展示出来了。

于是建议在Windows上做开发的同学,一定要选择“使用UTF-8无BOM格式”保存,所以用记事本写代码装X就不好使了,用Notepad++的可以注意选一下,它支持的文件编码格式挺丰富的,用一些比较先进的跨平台编辑器比如WebStorm、SublimeText它们都是没BOM的。

锟斤拷

乱码之所以叫乱码,就是因为它是“乱”的,或完全无含义的。但是乱码当中最出名的就是“锟斤拷”,他出现次数太多了以至于看起来根本就没那么“乱”。这就纳了闷了,为什么全中国的网站乱码里面都会有这个?

原因是,在将一些国家语言编码体系,比如GB、BIG-5、EUC-JP等,转换为Unicode的过程中,多少有一些字符是不在Unicode中的(比如一些偏旁部首在Unicode里是后来才收录的),甚至它本身在原来的编码体系里面就是非法字符的情况。
Unicode规定了U+FFFD当作一个占位符用来表示这些字符,用UTF-8编码它就是EF BF BD。连续多个这样的字节序列出现就成了EF BF BD EF BF BD。如果是一个UTF-8的解析程序还好,而如果用一个GB的解析程序去打开,一个汉字2字节,就成了“锟斤拷”。这里就是一个例子,用UTF-8编码打开是问号,用GBK编码打开的话就会看到锟斤拷,用hexdump或者UltraEdit这类任何16进制编辑器看的话就能看到里面都是EF BF BD。

要避免锟斤拷一个重要的点就是尽量减少程序当中的编码转换。比如输入是UTF-8,但是一个旧的模块是GBK,把UTF-8转成GBK交给旧的模块处理,处理过程中旧模块多多少少有些BUG的可能,再转回来的时候就容易锟斤拷了。一个项目的源代码在团队里面被不同的人(他们编辑器配置不尽相同)开来开去,存来存去,也很容易出现锟斤拷。

烫烫烫、屯屯屯

这两个汉字和编码转换其实并没有关系,在VC的DEBUG模式下,会把未初始化的栈内存全部填成0xCC,未初始化的堆内存填成0xCD,这样做是让你一眼就能看出来你开了内存没初始化。而用GBK编码时,CC CC就是“烫”,CD CD就是“屯”。

URL Encode和Base64

URL Encode

URL Encode又称为“百分号编码”它主要用来在URI里面将特殊字符进行转义,因为像/、、=等等这类字符在URI里面本身是有功能性的。
对于ASCII字符的编码很简单就是用%后跟ASCII编码的16进制表示,例如/的ASCII char code是47,16进制表示是2F,于是它的URL Encode结果就是%2F。

对于非ASCII字符,将它的每个字节进行相同规则的转换,例如中文“编码”的Unicode char code是U+7F16 7801,UTF-8编码的字节序列是E7 BC 96 E7 A0 81,所以它按照UTF-8编码的URL Encode结果就是%E7%BC%96%E7%A0%81。

可以看出,URL Encode编码非ASCII字符的时候,结果与使用的字符编码有关。因此,在页面上提交表单、发起Ajax请求等操作的时候需要注意编码。浏览器会按照当前页面所使用的字符编码对表单体提交进行URL Encode,但使用JavaScript的encodeURI和encodeURIComponent的时候则总是会使用UTF-8(参考MDN)。

表单提交的时候编码是非常非常重要的,一旦错了服务端解开数据的时候就会跪。比如Github在它们的搜索表单里面放了一个,其中那个对钩✓是U+2713,UTF-8编码是E2 9C 93,他们可以在服务端检测这个参数的值对不对从而对URL里用的编码进行一个初步检测。虽然我没有看到他们使用其他编码的情况,不过这样也算是一个编码协商和Check的手段吧。

在JavaScript中使用escape也可以达到URL Encode的效果,但是它对于非ASCII字符使用了一种非标准的的实现,例如“编码”会被escape成%u7F16%u7801这种%uxxxx奇怪的表示,W3C把这个函数废弃了,身为一名前端还用是打脸的哦。

Base64

Base64是一种用可见字符表示二进制数据的方法。它用了64个可见字符[A-Za-z0-9+/]。

Base64的编码程序非常简单,由于64=2^6,6和8的最小公倍数是24,也就是3byte,因此对输入数据以3byte为一个单位,查表把它转换成4个可见字符。

如果输入末尾不足3byte,那就补足,补1个byte就在输出末尾添加一个=,补2个byte同理。

Base64经常用来在一些文本协议里面保存二进制数据,比如HTTP协议,或者电子邮件的附件啊什么的。同时因为它的输出对于人类而言不可读,可以起到一些“混淆加密”的作用,事实上就有修改64个字符的排布来做一个变形Base64实现一个简单加密算法的例子。从密码学的角度看它基本上没什么强度可言,但是足够简单,可以起到防君子不防小人的作用。

由于一个字符只能编码6bit,自身却占了8bit,8/6=1.33,因此使用Base64来表示数据的时候会浪费1/3的体积。对于在CSS里面用Base64的data-url方式表示图片,用之前不妨简单估算一下,膨胀的体积和一个HTTP请求头比起来会相差多少,说不定涨太多了已经损失掉省一个请求的收益了。

后记

最后,还是要感谢原文作者的辛苦整理和解释。佩服并感谢一下ISO和Unicode联盟,做了这么伟大的事情将全世界的语言文字统一收录和编码,而其中包括了那么多我们根本没听说过的奇怪的语言文字。正是因为他们的努力,才奠定了互联网是一个无国界的领域,每天我们都能通过它获得来自任何地方任何语言的信息。

2

站心网

摘要 在开发中,经常打开一个有中文注释的源代码,注释中经常会出现诸如“烫烫烫”,“屯屯屯”,“锟斤拷..

为您推荐

代码照进现实:对公司管理策略的技术性解构

上学的时候觉得计算机专业的一些理论晦涩难懂,跟现实世界的关联太少,每当遇到一些精妙的设计时都会发出一种感叹:究竟是什么脑袋才能想出这么有意思的东西。一晃工作十年,阅历渐丰,隐约发现其实社会中的一些现象..

编写优秀 CSS 代码的 8 个策略

编写基本的CSS和HTML是我们作为Web开发人员学习的首要事情之一。然而,我遇到的很多应用程序显然没有人花时间真正考虑前端开发的长久性和可维护性。我认为这主要是因为许多开发人员对组织CSS / HTML和JavaScript的策..

天天写业务代码,如何成为技术大牛?

不管是开发、测试、运维,每个技术人员心理多多少少都有一个成为技术大牛的梦,毕竟"梦想总是要有的,万一实现了呢"!正是对技术梦的追求,促使我们不断地努力和提升自己。然而"梦想是美好的,现实却是残酷的",很多..

记我经历的一次公司破产经历,一行代码害死一家公司

前言这是一篇亲身经历的真实记录,事情发生在2010年。狗血剧情一再上演,使我的程序员生涯变得跌宕起伏,也从中学到了很多。在写这篇文章之前,我还专门去查了这家公司的资料。有如下事实:1.官网已经打不开了。2.天..

如何处理前任程序员留下的代码

作为软件工程师不可避免会遇到的一个场景是:我们在改变或添加一个功能到不是我们创建的、我们不熟悉的、与我们负责的系统部分无关的代码中时,会遇到麻烦。虽然这可能会是一个繁琐而艰巨的任务,但是由于使用其他开..

老生常谈!程序员为什么要阅读源代码?

大家好,我是码农先森。阅读源码这是一个老生常谈的话题了,但又是很多人想做又没有付出行动的事情。前段时间我研究了 Swoole 的源代码,并且输出了系列的源码分析文章「感兴趣的朋友可以翻阅以前的文章」。虽然这个..

对码农而言什么样的代码才能叫做好代码?

好的代码,就像是好的笑话——无需解释就能让别人明白。如果你的代码能够做到不解自明,在大多数时候,你根本无需为其配备说明文档。好的代码,就像是一辆配备了优秀音响和杯架的汽车,这辆车在行驶到最高速度的时候..

当一个程序员写不出代码了,该怎么办?

你已经对着电脑n个小时了。不知道该写什么代码,或者一种摔键盘的冲动正在你的胸中酝酿。咖啡一杯接着一杯。不敢再喝了,因为搞不好要有副作用了,心跳加速,身体不由自主地颤抖,出冷汗,但还是无法产出任何代码。..

ASP.NET MVC最常用的设计模式代码示例

ASP.NET MVC 是一个基于分层架构的框架,其核心架构本身已经实现了 MVC 模式(Model-View-Controller)。除了 MVC 模式,开发者在使用 ASP.NET MVC 开发应用时,通常会结合其他设计模式以提高代码的可维护性、可扩展..

10款.NET开发中推荐的代码分析和质量工具

以下是10款.NET开发中常用的代码分析和质量工具列表,以及它们的主要功能和使用场景:1. SonarQube简介:一个流行的开源静态代码分析平台,用于检测代码中的漏洞、错误、技术债务等问题。主要功能:支持代码质量监测..

.NET C# EntityFramework(EF)连接SQLite代码示例

在.NET C#中使用Entity Framework(EF)连接SQLite数据库是一种常见的做法,可以有效地管理和操作数据。以下是一个简单的示例代码,展示了如何使用EF Core连接到SQLite数据库并执行基本的CRUD操作。首先,确保你已经..

Tesseractjs 前端OCR识别提取图像文本字符工具 支持 100+ 种语言

Tesseract.js 简介Tesseract.js 是一个基于 Tesseract OCR 引擎的开源 JavaScript 库,用于在浏览器和 Node.js 环境中执行光学字符识别 (OCR)。它的特点是无需依赖服务器端支持,完全在客户端执行 OCR 操作。Tessera..

设计模式之高质量代码

0,什么是高质量代码我觉得回答这个问题,应该从两个方面考虑。从业务角度考虑。首先,在公司开发一款软件,应该是业务在驱动。所以,从这个角度来说,代码第一个应该满足的是业务需求,如果连最基本的业务需求都满..

DockerUI 中文可视化Docker管理工具使用示例

DockerUI 是由国内开发者打造的一款优秀的 Docker 可视化管理工具。该工具拥有简洁直观的UI界面,可以轻松进行Docker主机管理、集群管理,以及Docker任务的编排等操作。DockerUI不仅展示了资源利用率、系统信息和更..

C#发送邮件代码简洁示例(附源码下载)

C#发送邮件,主要使用的是System.Net.Mail命名空间下的方法实现,方法很简单,短短十几行代码即可完成发送,具体代码如下。 try { MailMessage myMail = new MailMessage(); myMail.From = new MailAddress..

AutoMapper.AutoMapperMappingException”类型的异常在 AutoMapper.dll 中发生,但未在用户代码中进行处理

今天修改别人的代码抛出了这样的异常: AutoMapper.AutoMapperMappingException”类型的异常在 AutoMapper.dll 中发生,但未在用户代码中进行处理。进行了调试,往下走的时候直接报错了,百度之~中文网站上没..

c#无损压缩图片代码,可设置压缩质量

之前写过一篇文章《使用htmlagilitypack+xpath抓取网页内容示例》,提到使用htmlagilitypack抓取网页信息。想做一个网络爬虫,但是想把网页上的图片也下载到本地,于是写了下载图片的功能。但是第三方网站上的图片大..

.Net Core HttpClient读取GB2312网页乱码

.NET Core使用HttpClinet抓取网页,使用Encoding.UTF8.GetString(arr)方法获取网页内容时中文会变成乱码。但是如果改为Encoding.GetEncoding("gb2312").GetString()方法的话会报错:'gb2312' is not a supported enc..

.NET Core MVC页面输出中文被编码了

如果在使用 .NET Core MVC 时发现页面上的中文字符被编码了,可能是由于的网页编码与的实际编码不匹配所致。可以尝试以下解决方法:在Program.cs文件中注册HtmlEncoder.Create服务:using System.Text.Encodings.Web..

使用.NET SDK Betalgo调用OpenAI ChatGPT API 代码示例

首先准备工作是需要有OpenAI的帐号然后获取ApiKey。目前国内IP无法注册和登陆OpenAI。翻墙后注册需要手机验证,可以通过手机验证码平台注册,注册过程非常快,花费大概1元左右。注册方法请看:最新OpenAI ChatGPT注..

发表回复

返回顶部