首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

15 岁的我用 Rust 编写了一个 QR 代码生成器作为我的第一个项目

作者 | Timur Borisov       译者|关天宇‍‍‍‍‍

责编 | 屠敏

出品 | CSDN(ID:CSDNnews)

大家好,我想向大家介绍一下我是如何从零开始学习 Rust 编程的,为此我选择了制作 QR 码编码器这个目标,并得到了 Pionir 免费学校一位导师的帮助。

至于我为什么会选择 Rust?

首先,我想尝试一些新的东西,我以前使用简单的 Python 写作,没有使用更低级、更复杂的语言的经验;其次,Rust 这种语言看起来很有前途,也很有趣。

我选择 QR 码编码器作为项目,是因为这个项目并不复杂,而且我可以熟悉 Rust 的基础知识。

此外,由于我们的未来计划与二维码有关,我想从这个项目中学到:如何使用位、矩阵、Rust 及其库、Reed-Solomon 算法和常见的纠错算法。

什么是 QR 码,以及如何生成?QR 码(Quick Response code,快速反应码)是一种二维条形码,它可以存储一些信息,借助搜索模式,可以用扫描仪轻松读取。

QR 码的优点是读取速度快、存储信息量大,而且使用 Reed-Solomon 算法,QR 码的纠错率高达 30%。

要生成 QR 代码,需要经过以下步骤:

数据编码

添加服务信息并填充

将信息分块

创建校正字节

组合区块

在 QR 码上放置信息

我对架构的设计

我对于生成器的架构设计有不同的想法,从中我选择了我认为最简单易懂的一种。

我想架构分为四个层次:

QrMatrix ⏤ 主矩阵,由模块组成,可以使用坐标进行更改。

DataBitvec ⏤ 使用编码将位转换为位向量(二进制位组成的向量),并添加纠错功能,然后将其发送到 DataEncoder。

DataEncoder ⏤ 该结构将使用 ZigZagIt 迭代器在 QrMatrix 中对数据进行编码。

QrBuilder ⏤ 作为 fieldQrMatrix (矩阵域)接收,它将所有基本元素添加到其中,对信息进行编码并创建掩码。

这些基本结构在 QrBuilder 结构中相互影响,从而得到一个现成的 QR 码矩阵,最后只需显示一个 QR 码即可。

创建主矩阵 QrMatrix

一开始,我想创建自己的 QrMatrix 数据结构,它只需由一个 Vec 组成,我可以使用模块的坐标搜索功能完成所有更改,但我认为不值得自己从零创建,所以我使用了库 generic-matrix。

通过函数中引入size值,我们可以创建自己想要的矩阵大小,并从中填入模块。模块中定义了枚举类,用于将功能块与普通信息分开,因此更加方便,而且无法更改功能块。

在我的结构中没有使用模块中的预留,因为在找到二维码的最佳掩码之前,信息块是不会被填满的。

要更改矩阵中的模块,可以使用以下函数:

插入FinderBuilder 和 TimingBuilder

为了在二维码中生成搜索模式,我创建了一个数组形式的常量:

由于总是有三种搜索模式,而且它们的初始坐标很容易计算,因此我认为直接在 QrMatrix 中生成没有意义,只需将常数 FINDER_BLOCK 旋转并完全插入特定坐标即可。

创建同步车道要容易得多,因为它们的坐标总是已知的,长度也很容易求出:

由于条纹总是以白色模块开始,因此每转一圈将值改为相反的值,就可以得到一个具有同步条纹正确模块的矢量。‍

建立 DataEncoder

要将某些内容编码成 QR 码,必须先将给定的字符串转换成位向量,为此要使用可接受不同字符的编码方式:

数字编码 ⏤ 只对数字进行编码,每 3 个字符使用 10 个 bit。

字母数字编码 ⏤ 对大写拉丁字母、数字、$%*+-./: 等特殊字符和空格进行编码。

字节编码 ⏤ 虽然信息密度较低,但可以编码任何字符(例如,在 UTF-8 中)。

其实还有中日韩和其他字符的汉字编码,但我没有使用这种编码。

为了存储 bit 并方便使用,我使用了 bit_thing 库。此外,为了方便处理信息,我创建了一个 QrCodeBitvec 结构,它包含三个字段:一个用于存储服务信息(编码类型和字符数)的字段、一个用于存储数据的字段和一个用于存储纠错字节的字段。

举个例子,为了说明这个系统是如何工作的,我将数字 1234 编码成一个二维码。

首先,你需要确定使用哪种类型的编码,以及每行有多少个字符。在下面的示例中,我们使用的是数字编码类型,字符长度为 4,然后将其转换为 BitVec:

数字编码的字符长度为 10 位。

现在,我们需要将数字 1234 转换为数字编码。方法很简单,将 3 个数字分成一组,那这样可以分为 123 和 4,然后把这些数字转换为 BitVec,大于 99 的数字使用 10 个 bit(二进制位),大于 9 的数字使用 7 bit,小于 9 的数字使用 4 个 bit。

现在我们得到了这个:

基于以上,我们有了长度为 28 位的 BitVec,但按照 QR 码的规范要求,我们必须完整填写数据。所以在这个示例中,我们需要将 BitVec 填充至 152 位。为此,我们需要做三件事:

添加终止符 ⏤ 用零填充 4 个位。

用零填充数据,使其可以被 8 整除。

然后循环交替添加 2 个字节 ⏤ 11101100 和 00010001。

这些操作必须重复进行,直到数据量等于 152。

最后的数据就是这个样子:

这些数据就可以发送给 Reed-Solomon 算法了。

Reed-Solomon 算法

为了实现这一算法,我使用了 Reed-Solomon 库。

我并不是十分清楚这种算法在数学上是如何工作的,但我至少尝试理解这种算法的逻辑,并将其正确地用于这个生成器。也有其他创建 QR 码生成器的开发者分享过使用 Reed-Solomon 库的经历,但我不喜欢那个人写的代码,因为其中命名很糟糕,而且有很多奇怪的代码结构,让人很难理解其算法逻辑,也没有什么意义,所以我放弃了这个想法,直接采用了一个现成的库。

我们的数据共有 152 个 bit,我们将这个库的结构发送给编码器。该结构接受一个值⏤校正字节数,对于校正级别为 L 的 QR 码第一版,使用了 7 个校正字节:

然后,我们将 BitVec 的字节转换为十进制数,并将这些代码字发送到 encoder 结构:

我们得到了 7 个纠错字节,对于第一个版本,我们只需在 BitVec 的末尾插入这些纠错字节:

我们用二维码对这些现成的数据进行编码。

ZIG_ZAG_IT 和 DATA_ENCODER

为了将信息编码到二维码中,我创建了一个迭代器,它将给出从右下方开始的坐标。通过 "之 "字形移动和跳过功能块以避免改变它们,我们就能得到插入信息的正确坐标:

现在,沿着 BitVec 并将“位”插入坐标,我们就得到了一个包含正确数据的 QR 码,现在我们只需屏蔽数据,就能得到一个现成可用 QR 码。

这就是数字 1234 在第一版中的样子,校正级别为 L,并带有第一个掩码。

结论

在这个项目的帮助下,我学到了很多有用的东西,至少我开始更好地理解编程,学会了用 Rust 编程,但我还有成长的空间,我真的希望在编程方面有进一步的发展。

未来,我们计划创建其他有趣的项目,例如,创建一个看起来像 ShotCode,但效率更高的极坐标 2D代码矩阵。

本次项目代码已上传到 GitHub ,详见:https://github.com/TimurBora/QrCodeRust/tree/main

  • 发表于:
  • 原文链接https://page.om.qq.com/page/O7VV9RlJkHngJTrTxQ9Bi1OA0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券