专栏首页用户6811391的专栏QR 数据编码详解(二)

QR 数据编码详解(二)

每种编码模式针对其字符,不断优化以产生最短的编码二进制串。在此过程中它们采用的编码方法是不同的,本篇将主要解释数据编码过程。

第一步:选择纠错级别

在数据编码前,要先选择纠错级别。QR 二维码采用 Reed-Solomon 纠错算法。该过程基于被编码数据产生纠错码(字节)。QR 读码器将会根据这些纠错码来检验其读取数据是否正确,同时纠错码可以被用来纠错。总共有 4 种校错级别:L,M,Q 和 H,其校错能力如下所示:

  • L 等级可以修正 7% 数据
  • M 等级可以修正 15% 数据
  • Q 等级可以修正 25% 数据
  • H 等级可以修正 30% 数据

注意更高级别的纠错能力需要的是更多字节。因此纠错级别越高,QR 二维码尺寸也会越大。

第二步:决定最小版本

QR 二维码不同尺寸被称为不同版本。目前有40种可用版本。最小的版本是版本 1,尺寸是 21 x 21 像素;版本 2 是 25 x 25 像素;最大的版本是 版本 40,尺寸是 177 x 177 像素。每个版本较之前一版每个维度多 4 像素。

每种版本有最大容量,取决于编码模式。此外,纠错级别也会限制容量。下表中列出版本 1-5 针对不同编码和纠错级别的字符容量:

字符容量表链接:
https://www.thonky.com/qr-code-tutorial/character-capacities

如何决定最小版本

此时数一下目标文本的字符数,选取特定编码模式和纠错级别下可以支持该字符数目的最小版本。

例如,"HELLO WORLD" 有 11 个字符。如果选择 Q 纠错级别和字符模式,容量表显示最小版本可以支持 16 个字符,所以版本 1 是包含此字符串的最小版本。

如果该文本长度超过 16 字符,例如 "HELLO THERE WORLD" 有 17 个字符,那么版本 2 将是 Q 纠错级别和字符编码下的最小版本。

容量上限

最大容量的 QR 二维码是 40-L(版本 40,纠错级别L)。下表展示了四种编码模式下 40-L QR 二维码的容量。这里容量指一个独立的 QR 二维码所能携带的最大字符数。版本 40-M,40-Q 以及 40-H 的容量低是因为它们需要更多字符去维持校错码字。

第三步:添加编码模式指示符

每种编码格式都有一个四位模式指示符来标明身份。被编码的信息必须以模式指示符为开始用以标明其编码模式。不同编码模式的四位指示符如下:

  • 数字编码 Numeric Mode:0001
  • 字符编码 Alphanumeric Mode:0010
  • 字节编码 Byte Mode:0100
  • 日文编码 Kanji Mode:1000
  • ECI 编码 ECI Mode:0111
  • *中文编码是 1101

第四步:添加字符计数符

字符计数符是用来代表被编码字符数字的指示符。字符计数符必须被置于编码模式指示符后。此外,该计数符根据不同 QR 二维码版本有不同的长度。

首先计算初始输入文本的字符数目,将该数字转化为二进制。字符计数符的长度取决于 QR 二维码的编码模式和版本。为了让计数符长度符合要求,在其左侧补全 0 即可。

下方列出了每种编码模式和版本下字符计数符的长度:

例如,如果编码 "HELLO WORLD" 采用 版本1 和 字符编码模式,那么字符计数符必须 9 位长。计算文本长度是 11,转化为二进制是 1011,用 0 将其补齐到 9 位长度得到 000001011。将其置于第三步中得到的编码模式指示符后我们得到最终结果:0010 000001011。

第五步:采用选中模式编码

上一篇中介绍了如何选择合适的编码模式。每种模式其编码过程如下:

数字编码

第一步将数字串拆成 3 个一组,如果数字串长度不是 3 的整倍数,最后一组只保留一或两个数字,例如 867 530 9。

第二步将每组转化为二进制。首先将每组三位数字看作一个三位数(如果最后一组长度小于三,那就是一位或二位数),将该数字转化为 10 位的二进制数。如果组成的三位数开头是一位 0,那么它应该被看作二位数,应该将其转化为 7 位的二进制数;如果开头是两位 0,那么它只能被看作一位数,应该被转化为 4 位的二进制数。同理,最后一组如果不满三位,也是两位数转化为 7 位二进制,一位数转化为 4 位二进制。

867 转化为 1101100011

530 转化为 1000010010

9 转化为 1001

字符编码

再重复一遍,字符编码只能对大写字母、不能对小写字母编码。这里我们用版本 1 对 "HELLO WORLD" 进行字符编码。首先将其拆分成两两一组:

HE,LL,O空格,WO,RL,D

之前我们提过字符编码有个字符索引表:

例如 HE 中 H 对应 17,E 对应 14,使用第一个索引值乘以 45 然后与第二个索引值相加 45 * 17 + 14 = 779。将结果转化为 11 位二进制,如果位数不足,在左侧用 0 补齐。

HE 对应 779 转化为 01100001011

LL 最终转化为 01111000110

O空格 最终转化为 10001011100

WO 最终转化为 10110111000

RL 最终转化为 10011010100

如果要转化的是奇数位字符,那么最后单独的字符这一组将转化为 6 位二进制位

D 对应 13 转化为 001101

字节编码

字节编码默认的字符集是 ISO 8859-1,如果可以的话,你应该将你的文本转化为该字符集中的字符。QR 二维码规范中提到了 ECI 混合编码,允许我们使用不同的字符集,但不是所有的 QR 读码器支持 ECI 转义序列。

如果要转化的文本中有的不能被 ISO 8859-1 编码,你也可以使用 UTF-8 编码,因为许多 QR 读码器在字节编码模式下可以正确检测 UTF-8 编码

,无需 ECI 转义序列。

为了解决非 ISO 8859-1 编码字符的问题,你可以首先测试下 QR 读码器,或者从 QR 读码器用户获取使用反馈。

在将文本转化为 ISO-88591-1 字符,或者 UTF-8 字符(如果 QR 读码器可以正确识别)后,我们要将字符拆分成 8 位字节。

这里我们用 "Hello, world" 版本 1 下的字节编码当例子,因为文本中有小写字母、逗号和感叹号,字符模式已经无法满足。

将字符串中字符转化为十六进制字节再转化为 8 位二进制字节:

日文编码

日文编码只能用来对双字节字符编码,其字节范围是 0x8140 到 0x9FFC 以及 0xE040 到 0xEBBF(十六进制)。所有字符都可以在 Shift-JIS 日文表中找到。

Shift-JIS 日文表链接:
http://www.rikai.com/library/kanjitables/kanji_codes.sjis.shtml

例如日文编码下,“茗荷”二字符情况如下:

茗 对应 0xE4AA

荷 对应 0x89D7

日文编码模式下,针对不同的双字节字符,有两种编码方法。第一种是针对 0x8140 到 0x9FFC 范围内的字符;第二种是针对 0xE040 到 0xEBBF 范围的字符。

第一种编码方法,以“荷”的 0x89D7 为例,先将此值在 16 进制下减去 0x8140:0x89D7 - 0x8140 = 0x0897,将结果的前两个 16 进制位拿出来乘以 0xC0,然后加上后两个 16 进制位,最后转化成 13 位二进制位:

(0x08 * 0xC0) + 0x97 = (0x600) + 0x97 = 0x697

0x697 = 0 0110 1001 0111

第二种编码方法,以“茗”的 0xE4AA 为例,先将此值在 16 进制下减去 0xC140:0xE4AA - 0xC140 = 0x236A,将结果的前两个 16 进制位拿出来乘以 0xC0,然后加上后两个 16 进制位,最后转化成 13 位二进制位:

(0x23 * 0xC0) + 0x6A = (0x1A40) + 0x6A = 0x1AAA

0x1AAA = 1 1010 1010 1010

最终按照“茗荷”的顺序将两串二进制位拼到一起得到 26 位的二进制:

11010101010100011010010111


以上便是四种编码模式的具体过程,接下来我们继续以 "HELLO WORLD" 为例,经过字符编码,我们可以得到其 4位 编码模式指示符 0010,9位 字符计数符 000001011,以及 61 位文本数据编码:

01100001011 01111000110 10001011100 10110111000 10011010100 001101

共计 74 位。

第六步:拆分成 8 位码字必要时添加填充字符

在得到一串包含编码模式指示符、字符计数符和文本数据编码后,可能会需要用 0 和填充字节来填充,因为 QR 二维码规范要求字符串必须完全填充其容量。下文将解释添加 0 和填充字节的过程。

决定 QR 二维码位数

参考错误校正表,决定二维码需要的位数。

错误校正码链接:
https://www.thonky.com/qr-code-tutorial/error-correction-table

在表中找到 QR 二维码所使用的版本和纠错级别,然后找到码字总数这一列(最后一列),将总数乘以 8 即可得到该版本和纠错级别下需要的总位数。

例如,根据表格,版本 1-Q 有 13 个码字。因此,该 QR 二维码总共需要 13 * 8 即 104 位。

必要时添加终止符

如果字符串比需要的二维码需要的位数短,一个最多 4 位 0 组成的终止符 0000 必须被加在字符串右边。如果字符串比需要的位数短超过 4 位,在其结尾处添加 4 位 0;如果相差的位数小于 4,只添加相差位数的 0。

例如,使用 版本 1-Q QR 二维码编码 "HELLO WORLD" 时,上文提过其所需的总位数是 104 位。第五步中编码后的数据总长度是 74 位,因此终止符是 4位 0 组成的 0000,尽管添加完终止符还是无法满足 104 位,但 QR 二维码规范所需要的终止符最长只能是 4 位。如果此时字符串是 102 位,那么终止符只要 2 位 0 即 00 便满足规范。

至此,我们得到四部分编码:

继续添加 0 使长度为 8 的倍数

在添加完终止符后,如果字符串长度不是 8 的整倍数,首先继续在字符串右边填充 0 使得字符串长度被 8 整除。

例如,刚加完终止符后的 "HELLO WORLD" 的字符串长度变成了 78 位,仍不是 8 的倍数,需要再加两位 0 补至 80 位长度。这样全字符串变成了 10 个 8 位二进制串:

00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000

长度不够继续添加补齐码

如果字符串还是不够最大容量的长度,在字符串结尾添加以下补齐码,不断重复直到字符串达到最大长度:11101100 00010001

这两个字节分别等于 236 和 17。QR 二维码规范要求如果字符串在此阶段不够长要加这两字节。

继续我们的 "HELLO WORLD" 例子,已经到达 80 位长度,距离 1-Q 二维码需要的 104 位还差 24 位长度,即 3 个补齐码,所以需要填充

11101100 00010001 11101100

最终我们得到 104 位的数据编码结果:

00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000 11101100 00010001 11101100

其中已经包含了 编码模式信息、字符计数信息以及被编码的文本信息。

本文翻译自
https://www.thonky.com/qr-code-tutorial/data-encoding
以及参考
http://blog.sae.sina.com.cn/archives/1139

本文分享自微信公众号 - TTTEED(TEDxPY),作者:TED

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 初识 QR 二维码(零)

    提到二维码,想必大家每天都会接触到,扫码支付、扫码添加微信好友等都会用到。关于二维码的生成原理,网上确实有些介绍,但基本涉及到具体编码就一笔带过没有深入了。目前...

    TTTEED
  • 手绘二维码攻略

    提到二维码想必大家都不陌生,扫码支付、添加好友以及关注公众号等,随处可见二维码身影。通常我们见到的二维码会有三个用于定位的黑白嵌套的方块,这基本上就是 QR 二...

    TTTEED
  • QR 编码模式分析(一)

    一个 QR 二维码其实是一串文本信息的编码。QR 二维码的标准支持以下四种编码模式:数字编码、字符编码、字节编码和日文编码。每种模式都将文本编码为一串由 0 和...

    TTTEED
  • C编程练习010

    首先,在与该源文件相同文件夹下建一个test.txt的文件,里面输入你要统计的内容:

    正念君
  • 5.python中文编码

    python到目前为止,一共有两个版本,分别是2.x和3.x版本,根据官方正式通知2020年停止对python更新和维护,距离今天还有110天左右,所以正在学习...

    猿说编程[Python和C]
  • 5.python中文编码

    python到目前为止,一共有两个版本,分别是2.x和3.x版本,根据官方正式通知2020年停止对python更新和维护,距离今天还有110天左右,所以正在学习...

    猿说编程[Python和C]
  • Java基础-基本数据类型

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    cwl_java
  • 密码发展史以及常用编码算法介绍

    描述:密码是一种用来混淆的技术,它希望将正常的(可识别的)信息转变为无法识别的信息。当然,对一小部分人来说,这种无法识别的信息是可以再加工并恢复的。密码在中文里...

    WeiyiGeek
  • IBC+Palette 实现屏幕内容编码优化

    https://www2.tutormeetplus.com/v2/render/playback?mode=playback&token=e632113e78...

    LiveVideoStack
  • Linux shell 程序设计1——安装及入门

    1、什么是shell? shell是linux内核的“壳”,是用户和内核的桥梁。它类似于windows下的命令提示符,将用户输入的命令解释给内核执行,并返回给用...

    用户1214695

扫码关注云+社区

领取腾讯云代金券