转义、编码和加密是开发中很常见也很基础的概念。对于初学开发的开发者,可能有时会无法准确的区分着几个词。我们将通过这篇文章来了解一下“转义、编码和加密”这几个词的关联和区别。

<!-- more -->

转义

第一种转义场景

绝大多数的开发者都曾经在自己学习第一个编程语言时,就遇到了这个概念。以经典的C语言中字符串中的字符转义为例。

如果在一个字符串中存在一个",那么就需要在"前添加\才能够正常的表示,比如下面这样。

char* universal_law = "月老板说:\"世界上本也不存在'银弹'。一套框架解决不了所有问题。\""

之所以需要这样,是因为对于字符串来说,"本身就是表示一个字符串的起止符号。如果不进行转义,那么编译器将无法正确的识别其中的"哪些是分隔符,哪些是字符串内部的"

所以,第一种需要转义的场景就是:如果不进行转义就可能与语法规定的某些内容产生混淆,所以这些内容都被设计为需要转义。

基于这种场景,可以在很多的编程语言和概念中找到这种场景的体现:

第二种转义场景

当然,另外还有一种场景,同样还是以C语言为例,看一下下面这个例子:

char* hammurabi_no1 = "月落大佬:\"业务复杂度不会因为系统设计变化而减少,\r\n它只是从一个地方转移到了另外的地方。\""

其中的\r\n也是一种转义场景的使用。他们分别表示一个回车符和换行符。之所以要转义,是因为正常情况下,这样的字符是不可见的,对于这种字符,不过不采用转义的形式进行表达,那么会比较困难,因为语言设计者设计了这种转义的方式来表达不容易表达的字符。

因此,可以总结出第二种需要转义的场景:转义可以使得表达内容的方式更加容易,更加容易理解,所以设计了这类转义规则。

基于这种场景,也可以在很多编程语言和概念中找到对应的体现:

除了在IT领域,在其他领域其实也存在类似第二场景的应用。例如在中国的航空领域,对于数字的念法有特殊的处理:7读作拐,0读作洞,1读作幺,2读作两。经过这样的“转义”处理,可以避免误听而造成的困扰。

转义的总结

总结来说,转义规则的设计,主要解决了两种场景下对代码的表达问题:

  1. 如果不进行转义就可能与语法规定的某些内容产生混淆,所以这些内容都被设计为需要转义。
  2. 转义可以使得表达内容的方式更加容易,更加容易理解,所以设计了这类转义规则。

值得一提的是,很多名称中包含有escape或者unescape的函数或者方法都表明了它们与转义有关。

编码

编码也是一个非常常见的概念。比如经常会听到UTF8编码、GBK编码、Base64编码、URL编码、HTML编码、摩斯电码等等一些和编码有关的概念。

生活化地理解编码

在了解编码之前,首先通过一个生活化的例子来了解一下“什么是信息,什么是信息的载体”。

全世界,对于“我爱你”这样一句话的表达方式千差万别。口头表达,书面表达,肢体表达,普通话表达,英语表达,音乐表达,绘画表达。甚至有生之年我们可以脑电波表达。但不论表达方式是如何的,其中包含的信息可以是一致的。都是为了传达“我爱你”这样的一个核心价值。

在以上这段表述中,可以将“我爱你”这样的概念理解为“信息”。而各种表达方式理解为这个信息的各种载体。

那么,回到编程的世界中来。计算机中的信息主要的载体是以电磁信号的物理载体存在于计算机世界中。那么如果要将现实世界复杂的内容都依靠这种载体来表达,就需要进行转化,我们可以将这种转化理解为编码。结合前文生活化的例子,使用普通话来表达“我爱你”这个信息,就可以理解为使用普通话来编码这个信息。

因此,编码,其可以理解为,采用一种新的载体来表示前一个载体所表达的信息。

可以套用类似这样一个公式来理解:XX编码,将A编码为B,以实现通过B进行存储或传输传输的目的。

技术相关的编码

那么,采用这样的概念,我们来理解一下以往见到的各种技术概念:

总的来说,通过编码,可以转化信息表达的载体。这样就可以利用新载体带来的好处。这里也有一些生活化的例子:

值得一提的是,很多名称中包含有encode或者decode的函数或者方法都表明了它们与编码有关。

什么是乱码

根据上文提到的公式,编码是完成A->B的载体转化过程。那么同样可以定义A->B的逆过程B->A为“解码”。

一般,如果解码之后无法正确还原原来A所表达的信息,我们会说出现了乱码。例如,使用GB2312的方式去解码一个UTF8编码的文件,那么就会出现乱码。

当然,更加常见的情况是,当开发者,特别是初入的新晋工程师,看到自己无法理解的文本,就说:“这是乱码。”

总的来说,乱码通常来说只是因为选用的解码方式和编码方式不同,而导致信息失真的情况。选用正确的编码就能够解读出正确的信息。

加密

加密很好理解,在日常生活中也不乏加密的使用场景。特别是在以前的战争中的无线电技术应用历史中,确保己方军事信息不被敌方破解,采用优秀的加密算法是极为重要的军事内容。

加密,可以这样概括:按照一定的算法,将需要表达的信息进行处理,以达到除了信息的发送者和接收者之外,其他人无法识别信息真实内容的目的。

技术上,有需要使用加密的场景:

这里需要特别说的是编码和加密的区别和联系:

所以要简单区分是编码还是加密,可以简单套用这个理解:在算法完全公开的情况下,如果还需要密钥,那么是加密。如果不需要密钥,只能算是编码。

结合生活例子理解一下加密和编码的区别:存在这样一段字符串Мистер Мун, Навсегда Бог.这并不是加密,因为这是一段正常的俄语。不能因为看不懂就说他是加密,因为如果懂俄语,会用俄语解码这段信息,就能知道他表达的意思是:“月先生,永远的神”。

值得一提的是,很多名称中包含有encrypt或者decrypt的函数或者方法都表明了它们与加密有关。

下饭小测

以下是关于本文章的一些概念的测试题,以便读者更好的理解。

不必担心这些语言你没有学过,因为概念其实和语言关系不大。

所有的问题都只有三个选项:

  1. 转义
  2. 编码
  3. 加密

小测1

在很多编程语言中都存在“字符串内插”的语法,例如:C#、ES6、Powershell。

以C#为例,以下就是一个示例:

var dalao = "月落大佬";
var hammurabi_no1 = $@"{dalao}:
""业务复杂度不会因为系统设计变化而减少,
它只是从一个地方转移到了另外的地方。""
";
Console.WriteLine(hammurabi_no1);

那么,以上代码中""输出时只表示一个",这是(A)处理。

如果需要在$@开头的“多行字符串内插”字符串中,输出一个},那么需要使用}}来进行(B)处理。

A:转义

B:转义

小测2

在Powershell中如果要定义一个多行字符串变量,那么需要采用下面这样的写法:

$template = @"
## [version]

[content]

"@

那么,如果需要在这个字符串中插入一个@或者",可以直接写进去,因为powershell是使用@""@,作为多行字符串的起止符,而且要求起止符需要单行。因此中间出现的@#都不需要进行(A)处理。

A:转义

小测3

在javascript中有一个函数名称为escape。按照MDN的解释,该函数已经被标记为弃用了。建议使用encodeURIencodeURIComponent代替。从相应的函数解释上也可以看出,原来的escape是进行表达的意思是进行(A)处理。或许就是大佬们意识到这个名字其实不对,所以换了函数?新函数看名字直接理解应该是对URI进行(B)处理,似乎更加准确哟。

A:转义

B:编码

小测4

曾经有的网站使用 base64 的方式,处理登录票据,并且保存在 Cookie 中。尽管这似乎比明文保存要高明一点,但这是不安全的,因为 base64 只是一种(A)算法,不能够安全的防止信息被篡改。可以选用例如 DES 这样的(B)算法,来确保信息不被篡改。

A:编码

B:加密

总结

转义、编码和加密都是在开发过程中常常遇到的概念。注意区分学习,进行正确的表达能够更好沟通。