前言

做项目偶尔会接触到 stream 这个感念,不管是 memory stream 还是 file stream,它们又会提到 bytes。

还有像 Identity – 安全基础知识 中提到的 SHA-256,非对称加密等等,在 ASP.NET Core 操作这些也都是用 bytes。

而提到 bytes 又经常会牵扯出 ASCII,Unicode 这些鬼东西。所以这一篇就让我们来了解它们究竟是什么吧。

参考:

阮一峰 – 字符编码笔记:ASCII,Unicode 和 UTF-8

Bit

bit 又叫比特. 是数码世界中最小的单位。1 个 bit 就代表 1 个二进制 (binary),0 或者 1。

在数码世界里,万物就是用 0 和 1 来表达的,为什么要用二进制来表达呢?看这一篇 计算机为什么采用二进制?

其中一个原因是通信可以靠电压高低来表示 0 或 1,这样实现起来会很容易,看这篇 李永乐老师 – How does the chip internally perform addition operations?

ASCII

想象一下,如果我发一串长长的电压给你,有高有底,然后问你,知道我说什么吗?

你肯定不知道,因为表达除了声音以外,还要有规则丫,就好比摩斯密码那样。

我们先看看英文,英文的结构是首先有 26 个字母、空格、标点符号。

把 a-z 字母拼接起来就变成单字,在通过拼接单字 (空格作为分割),标点符号,最后变成句子。

所以下一步就是制定一个规则,把二进制转换成英文,这样就可以通信了。

ASCII 就是干这件事儿的。它规范了 a-z、A-Z、0-9、空格、各种符号,每一个对应的二进制数。

举例:

0 = 48,1 = 49,A = 65,a = 97... 以此类推。(注:这里写的是十进制,只是为了方便我们看)

最终 ASCII 定义了 128 个表达,里面包括了各种字母和符号,参考: Wikipedia – ASCII

比如 digit 0 对应的代号是 48 (十进制),转换成二进制就是 110000

通常一个字符会有 2 个形式表达,一个是 Code 一个是 Decimal。

Decimal 是十进制数,Code 则是十六进制 (前面再加上 U+)

总之,一个字符对应一个数(这个数不管是用二进制,十进制 或 十六进制来表达都是同一个数来的)

Byte

ASCII 字典里,一个字符对应的是一组 bit,比如 0 = 110000,a = 1100001。

这一组 bit 的长度是不一致的,0 是 6 bit,a 是 7 bit。

当我们收到一串长长的电压时 (e.g. 0011000001100001),具体要怎样去找字典呢?

总不可能说,有短的就马上配对,那长的起不是永远都配不上了。

所以我们还需要一个切分的规则,这就是 byte 的概念。

1 byte = 8 bit,把上面的电压切分成 byte 就成了 [00110000, 01100001]。

一个字符就对应一个 byte,这样就有规则去找 ASCII 字典了。

那为什么是按照 8 个 bit 来切分呢?为什么不是 10 个?感兴趣的朋友可以去考古一下:What is the history of why bytes are eight bits? 和 Quora – Why is one byte = 8 bits, even on a 16 or 32-bit machine?

那为什么 110000 变成了 00110000 (前面多了两个零) ?因为要充数丫,切分规则是每 8 个 bit 切一刀成 1 个 byte,1 byte 等于 1 个字母,所以即使字母不需要 8 个 bit,也需要硬硬凑数到 8 bit。

8 个 bit 可以 cover 从 00000000 到 11111111 一共 2^8 = 256 个不同的排列,也就是说可以表达出 256 个不同的冬冬,ASCII 定义所有字母和符号只需要 128 个而已,最多只用到 7 个 bit,所以 ASCII 的 byte 第一个 bit 一定是 0。

总之有了 byte,就可以把长长的 bits 切断,一个一个 byte 去找字典转换成字母,这样英文词就慢慢拼出来了。

补充知识:

参考:

详解计算机中的字、字节(Byte)、比特(bit)及它们之间的关系

Youtube – How computer memory works

byte 是一个单位,

1 byte = 8 bits.

1 kb = 2^10 = 1024 bytes. 因为是 2^10 所以是 1024 而不是 1000 bytes (为什么1KB等于1024字节而不是1000字节?)

补充:

参考: What is the difference between Kilobits and Kilobytes ? 和 KB to Bytes Conversion

1 kB (kilobyte) = 1000 Bytes (in decimal, k 小写)

1 KB = 1024 Bytes (in binary, K 大写)

1 KiB (kibibytes) = 1.024 KB = 1024 Bytes

所以要非常清楚的表达 1024 最好使用 KiB, 避免不必要的混乱, 比如 Google Light 就用 KiB 来表达

经常听到 OS, CPU, 软件 有分 32-bit (x86) 和 64-bit (x64), 它们是啥?  参考: 为什么好多软件都区分32位和64位,到底有什么区别?

它们指的是 CPU 一次可以处理多少位字的信息, 32-bit 就是 32 bits 也就是 4 个 bytes. 而 64-bit 就是 8 个 bytes.

那为什么说 32-bit 只能支持 4 GB ram 呢? 看 32位CPU最多支持4G内存是怎么算出来的?(解惑篇)和 知乎 – 32位系统只能寻址4G空间,64位则是128G,这些是怎么算出来的?

内存的最小单位是 bytes (CPU 1 次读 ram 最少就是拿 1 byte 的意思), 32-bit 可以表示 2^32 = 4294967296 状态, 1 个对应 1 个内存地址的话就是可以操控 4294967296 bytes

换算到来就是 4294967296 / 1024 become kb / 1024 become mb / 1024 become gb = 4gb.

Unicode

ASCII 是英文对应二进制的字典,那其它语言呢?汉字、日文、怎么办?简单丫,继续往上加呗。于是就有了 Unicode。

比如:

严 = 20005

截至 2022 年 2 月 19 日,Unicode 定义了 144,697 个字符,用二进制表示的话需要 18 个 bits 才足够。

插播: unicode编码是占用几个字节?

早年 Unicode 只用 16 进制而已,所以一般会说 2 bytes 就足够,但是 2 bytes = 16 bits = 2^16 = 65536 表达,哪里够 144,697。

所以现在是用 32 进制 4 bytes 了。

定义是 ok 了,但 1 - 4 bytes 才能表达 1 个字符,那要怎么知道拿到多少 bytes 的时候去查 Unicode 字典找出对应的字符呢?

总不能强制每个字符都占据 4 bytes 吧,这太浪费磁盘空间,网络带宽,传输速度了吧。

软件工程里有一句话,没有任何一个问题是不能通过多加一层去解决的,如果有那就再加一层。

于是,就有了 UTF-8。

UTF-8

UTF-8 是一种 Unicode 的实现手法, 它依然遵守 1 byte = 8 bits 原则. 刚才我们说 ASCII 用了后面 7 个 bits, 最前面还有一个 bit 是 0.

这就让它有了利用价值. 如果发现开头是 1 就表示它是一个 Unicode 然后需要去拿下一个 byte. 具体规则看这里.

总之就是一种解析规则, 通过识别几个 bytes > 从 bytes 里获取最终的二进制 > 去找 Unicode 字典 > 得出字符串.

UTF-8 还向后兼容了 ASCII, 又通过巧妙的设计, 让只有需要很多 bits 的字符才真的用到那么多 bits. 所以它收到了大家的青睐.

UTF-16, 32

UTF-16, 32 也是对 Unicode 的一种实现, 解析的规则不一样而已.

它们其实各有特色. 可以参考:

为什么 UTF-8 编码比 UTF-16 编码应用更广泛?

UTF-8 UTF-16 UTF-32 比较

Unicode 编码及 UTF-32, UTF-16 和 UTF-8

简单说, UTF-8 的优势是, 按需用量, 节省空间. 然后兼容 ACSII. 所以它既可以保留英文为主的场景, 又可以兼容后续的语言. 这就成为了大家最喜爱的了.

UTF-16 本来是有它的优势的, 但后来随着 Unicode 越来越多, 它根本支持不完. 所以优势也没了.

取而代之的是 UTF-32, 如果 1 个字符需要用到 32 bits 那么用 UTF-8 来处理其实不会比 UTF-32 来的好. 毕竟多一层处理.

所以有时候会看到处理信息时会把它 convert to UTF-32. 尤其是非英文内容时

UTF-8 vs UTF-16

这里拿一个阮一峰老师文章中的例子

"严“ 这个中文字在 Unicode 字典里对应的二进制是 100111000100101

如果用 UTF-8 的规则, 最终需要 3 bytes [11100100, 10111000, 10100101]

而 UTF-16 只需要 2 bytes [100101, 1001110]

总结

Unicode 只规定, 一个字母或符号对应的二进制. 至于这一个字母的二进制你要用多少个 bytes 来保存那它不管的.

UTF 则是负责规定多少个 bytes = 1 个字母.

UTF-8 最少 1 bytes = 1 字母, 当一个 1 bytes 不足够它会用更多 bytes 来表示 1 个字母

UTF-16 最少 2 个 bytes = 1 字母

UTF-32 最少 4 个 bytes = 1 字母

因为英文占据了多数, 所以 UTF-8 最 popular. 但从上面 "严" 的例子可以看出. 如果字母本身需要超过 1 byte, 用 UTF-8 反而会用更多 space.

因为它需要加入额外的信息让 decoder 知道这个 byte 还不足够, 需要再加上下一个.

Base64

参考:让你彻底理解Base64算法(Base64是什么,Base64解决什么问题,Base64字符串末尾的=是什么)

用 ASCII 来表达任何 Unicode

Base64 的坏处是会让内容变大, 大概变成 135% 左右.

好处就是它可以确保传输完整 (有些旧设备比如路由器只支持 ASCII)

Base64 URL

Base64 同时符合 URL 限制的字符. 这样几乎任何字符都可以通过 URL 传递了.

ASP.NET Core Bytes & String Conversion

var valueByte = System.Text.Encoding.ASCII.GetBytes("test");
var valueString = System.Text.Encoding.ASCII.GetString(valueByte);

System.Text.Encoding.Unicode 就是 UTF-16 来的, 比较常用的应该是 UTF-8 (网页都是跑这个)

参考: Hashing a string with Sha256

比如: ASP.NET Core 要做 SHA256 是这样: HashAlgorithm.ComputeHash Method

图片编码

我曾经有个疑问, 图片也是 Unicode 吗? 当然不是.

一个文件, 储存的就是 bytes, 一堆的二进制而已.

这些二进制表达着什么, 取决于你怎么去看它.

知乎 – 为什么同样是二进制编码的数据,从计算机内部来看都是一堆0/1代码,有的数据就是文字,而有的是图片?

比如, txt 代表文字内容文件. 把它的 bytes 拿出来后, 通过 ASCII Unicode 规范字典, 翻译成了人类看得懂的字符.

.jpeg, .webp 是图片文件, 把它的 bytes 拿出来后, 就要对照图片编码的字典, 翻译成图像让人类看得懂 (其实对显示器来说, 字符, 图像是一样的, 就是亮几颗灯管的不同而已)

所以每当我们用错了解析器, 就无法显示正确的内容了, 比如你用 notepad 打开一张图, 它会去拿 bytes 然后对 Unicode 做翻译, 结果当然是乱七八糟. 因为图片有自己的编码规则.

二进制, 十进制, 十六进制

上面提到的都是讲二进制对应的字母和符号. 和十进制, 十六进制没有什么关系.

十进制, 十六进制只是另一种表达手法而已.

二进制是 0 和 1, 两个号而已, 到号了就进位.

十进制就是我们熟悉的 0123456789 它比 2 进制多了 8 个号 3456789 所以叫 10 进制. 同样的, 到号后就进位.

二进制没有 “2” 这个号, 所以就进位成 10 了. 而十进制则可以用 "2" 来表达.

十六进制又比十进制多了 6 个号. 它们是 abcdef

二进制的 10 = 十进制的 2

十进制的 10 = 十六进制的 a

所以同样的 10 在不同的进制情况下表达的数是不同的. 这是我们要清楚的.

JS binary convert

Number(30).toString(2); // 转成二进制 11110
Number(30).toString(10); // 转成十进制 30
Number(30).toString(16); // 转成十六进制 1e parseInt('1e', 16); // 十六进制 转成 十进制 30

C# binary convert

var value1 = Convert.ToString(30, 2);  // 转成二进制 11110
var value2 = Convert.ToString(30, 10); // 转成十进制 30
var value3 = Convert.ToString(30, 16); // 转成十六进制 1e var value4 = Convert.ToInt16("1e", 16); // 十六进制 转成 十进制 30

各个类型的 online convertor :

ASCII > 十进制

ASCII > 二进制

UTF-8,16,32 > 二进制

十进制 > 二进制

Bit, Byte, ASCII, Unicode, UTF, Base64的更多相关文章

  1. ASCII UNICODE UTF "口水文"

    最近接了一个单是需要把非 UTF-8 (No BOM)编码的文件转换成 UTF-8 (No BOM),若此文件是 UTF-8 但带有 BOM ,需要转换成不带 BOM 的.于是开启了一天的阅读.首先花 ...

  2. bit/byte/ascii/unicode

    bit(位).byte(字节).ASCII.Unicode 和 UTF-8位和字节的关系bit 电脑记忆体中最小的单位,在二进位电脑系统中,每一bit 可以代表0 或 1 的数位讯号byte一个byt ...

  3. Unicode(UTF&UCS)深度历险

    Unicode(UTF&UCS)深度历险 计算机网络诞生后,大家慢慢地发现一个问题:一个字节放不下一个字符了!因为需要交流,本地化的文字需要能够被支持. 最初的字符集使用7bit来存储字符,因 ...

  4. 字符编码 ASCII,Unicode和UTF-8的关系

    转自:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/00143166410626 ...

  5. 深入编解码:ASCII,UNICODE,UTF8等

    ASCII ASCII = American Standard Code for Information Interchange(美国信息交换标准码) 美国最先有了计算机技术,计算机里面只有01,也就 ...

  6. 字符编码 ASCII unicode UTF-8

    字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题. 因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特(bit)作为一个字节(b ...

  7. 浅显总结ASCII Unicode UTF-8的区别

    如果觉得此地排版不好,欢迎访问我的博客 浅显总结ASCII Unicode UTF-8的区别 制作表单时,为了追求更好的用户交互体验,常常会有提示性的内容,比如提醒用户字符的限制.由于英文,中文字符的 ...

  8. Unicode, UTF, ASCII, ANSI format differences

    Going down your list: "Unicode" isn't an encoding, although unfortunately, a lot of docume ...

  9. 字符集、字符编码、国际化、本地化简要总结(UNICODE/UTF/ASCII/GB2312/GBK/GB18030)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 环境说明   普通的linux 和 普通的windows.    ...

  10. 【转】【编码】ANSI,ASCII,Unicode,UTF8之一

          不同的国家和地区制定了不同的标准,由此产生了 GB2312.GBK.GB18030.Big5.Shift_JIS 等各自的编码标准.这些使用多个字节来代表一个字符的各种汉字延伸编码方式,称 ...

随机推荐

  1. 内部网关协议RIP

    RIP协议的特点:仅和相邻路由器交换信息:交换自己现在的路由表:按固定的时间周期. 对每一个相邻路由器发送的RIP报文,执行以下步骤: 1.对地址为x的相邻路由器发来的报文,修改此报文中的所有项目,把 ...

  2. tensorflow学习率指数衰减ExponentialDecay的参数介绍与使用方法

      本文介绍在tensorflow库中,用于动态调整神经网络的学习率的一种方法--指数衰减ExponentialDecay()策略的参数含义及其具体用法.   在进行神经网络训练时,我们经常需要用到动 ...

  3. [oeasy]python0023_[趣味拓展]Guido的简历_从ABC到python

    Guido的简历 回忆上次内容 上次 添加了 各种 符号 铭文 各种 颜色 铸造了 自己的宝剑       添加图片注释,不超过 140 字(可选)   这些都是 用python画出来的宝剑   py ...

  4. oeasy教您玩转vim - 42 - # 剪切进入

    ​ 剪切进入 回忆上节课内容 上次我们了解到了各种寄存器 :reg 无名寄存器"" 数字寄存器"0-"9 行内删除专用寄存器"- 指定寄存器" ...

  5. CF452C 题解

    洛谷链接&CF 链接 题目简述 有 \(m \times n\) 张牌,有 \(n\) 个种类,每个种类有 \(m\) 张,现在抽一张放回,再抽一张,求这张牌与第一张抽出的牌种类相同的概率. ...

  6. Java 根据XPATH批量替换XML节点中的值

    根据XPATH批量替换XML节点中的值 by: 授客 QQ:1033553122 测试环境 JDK 1.8.0_25 代码实操 message.xml文件 <Request service=&q ...

  7. AS自制闹钟学习,关于PendingIntent与AlarmManager

    PendingIntent是Intent的封装,不是立刻执行某个行为,而是满足某些条件或触发某些事件后才执行指定的行为实例获取一般为下列5个用法 getActivity() getActivities ...

  8. sftp文件上传下载方法

    随着信息化.数字化的发展,企业对数据安全及应用安全意识普遍加强,在数据文件传输过程中,一般建议使用sftp协议进行文件传输,sftp文件操作脚本如下: sftp操作主要有三种方式,分别是sftp客户端 ...

  9. 【Java】Collections 集合工具类

    Collections 集合工具类 - 操作Set.List.Map等集合的工具 - 提供了排序.查询.修改.操作,提供对对象设置不可变对集合容器对象实现同步控制等方法 排序操作: - static ...

  10. 【Java】Enumeration Class 枚举类

    枚举类 enum 对象是有限的确定的.属于类的(静态的) 适合定义一组常量 例如固定的一些事物: - 季节 - 性别 - 状态 自定义枚举类的使用 public class EnumerationTe ...