第七 二维码
二维码源于日本,在中国发扬光大。
二维码相较于条形码,能够存储的信息更多,同时也能快速完成短信息的识别输入。从移动支付到健康码,从商品溯源到电子名片,二维码已经深入到我们生活的方方面面。
7.1 二维码的历史
7.1.1 QR码的诞生
1994年,日本 Denso Wave 公司的工程师原昌宏(Masahiro Hara)发明了 QR Code(Quick Response Code)。当时,丰田汽车旗下的零部件供应商需要一种能够快速扫描并存储大量信息的条码系统来追踪汽车零部件。传统的条形码只能存储少量数字信息,且只能单向扫描,无法满足工业需求。
原昌宏从围棋棋盘的网格结构中获得灵感,设计了这种可以在水平和垂直两个方向同时存储信息的矩阵式二维码。QR Code 的“Quick Response“(快速响应)之名,正是强调了其高速读取的特性。
QR Code 的关键创新:
- 高速读取:通过定位图案实现 360 度任意方向扫描
- 大容量:相比一维条码,信息容量提升数十倍
- 容错能力:即使部分损坏仍可正确读取
- 支持多种数据类型:数字、字母、汉字、二进制数据
7.1.2 在中国的发扬光大
QR Code 在日本发明后,最初主要用于工业领域。然而,真正让二维码“火遍全球“的是中国。
- 2011年:支付宝推出二维码支付,开创了移动支付的先河
- 2014年:微信支付全面接入二维码,“扫码支付“迅速普及
- 2020年:健康码系统使用二维码进行疫情防控,几乎全民使用
- 如今,中国已成为全球二维码应用最广泛的国家,日均扫码量超过百亿次
7.2 QR码原理详解
7.2.1 QR码的结构
一个标准的 QR 码由以下功能区域组成:
┌─────────────────────────────┐
│ ■ ■ ■ ■ ■ ■ ■ ■ │ 定位图案(左上)
│ ■ ■ ■ │
│ ■ ■ ■ │ 定位图案(右上)
│ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■ ■
│ ■ │
│ ■ ■ ■ ■ ■ ■ ■ ■ │ 定位图案(左下)
│ ■ ■ ■ │
│ ■ ■ ■ │
│ ■ ■ ■ ■ ■ ■ ■ ■ │
│ ■ │
│ ■ ■ ■ ■ ■ ■ ■ ← 定时图案(水平和垂直)
│ ■ │
│ [格式信息] [版本信息] │
│ [数据和纠错码字] │
│ [剩余位] │
└─────────────────────────────┘
各区域说明:
| 区域 | 位置 | 功能 |
|---|---|---|
| 定位图案 (Finder Pattern) | 三个角落 | 7x7 的嵌套正方形,帮助扫描器定位 QR 码的位置和方向 |
| 定时图案 (Timing Pattern) | 连接定位图案 | 交替的黑白模块,帮助确定模块坐标 |
| 格式信息 (Format Info) | 定位图案旁 | 纠错等级和掩码图案编号 |
| 版本信息 (Version Info) | 较大版本中 | QR 码版本号(1-40) |
| 数据和纠错码字 | 主体区域 | 编码后的数据和纠错码 |
| 静区 (Quiet Zone) | 四周 | 空白边框,宽度至少 4 个模块 |
7.2.2 编码模式
QR 码支持四种编码模式,根据数据内容自动或手动选择最优模式:
| 模式 | 名称 | 适用字符 | 编码效率 |
|---|---|---|---|
| 0001 | 数字模式 (Numeric) | 0-9 | 最高(3个数字→10bit) |
| 0010 | 字母数字模式 (Alphanumeric) | 0-9, A-Z, 空格, $%*+-./: | 较高(2个字符→11bit) |
| 0100 | 字节模式 (Byte) | ISO 8859-1 字符集(含中文UTF-8) | 中等(1字节→8bit) |
| 1000 | 汉字模式 (Kanji) | Shift-JIS 编码的日文汉字 | 较高(1个汉字→13bit) |
数字模式的编码方式:将数字分为 3 位一组,每组转换为 10 位二进制数。例如数字 123456:
$$123 \rightarrow 0001111011_2$$ $$456 \rightarrow 0111001000_2$$
字母数字模式的字符集包含 45 个字符:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:
将字符分为 2 个一组,每组转换为 11 位二进制数:
$$\text{value} = c_1 \times 45 + c_2$$
7.2.3 纠错等级
QR 码使用 Reed-Solomon 纠错编码,提供四个纠错等级:
| 等级 | 代号 | 纠错容量 | 可恢复数据比例 | 适用场景 |
|---|---|---|---|---|
| L | Low | 约 7% | 数据的 7% 可被恢复 | 数据密度优先,环境良好 |
| M | Medium | 约 15% | 数据的 15% 可被恢复 | 通用场景(默认) |
| Q | Quartile | 约 25% | 数据的 25% 可被恢复 | 需要较高容错 |
| H | High | 约 30% | 数据的 30% 可被恢复 | 带Logo的二维码、恶劣环境 |
纠错等级越高,可用数据容量越小,但容错能力越强。例如,Version 1 的 QR 码在不同纠错等级下的数据容量:
| 纠错等级 | 数字 | 字母数字 | 字节 | 汉字 |
|---|---|---|---|---|
| L | 41 | 25 | 17 | 10 |
| M | 32 | 20 | 14 | 8 |
| Q | 27 | 16 | 11 | 7 |
| H | 17 | 10 | 7 | 4 |
7.2.4 数据编码流程
QR 码的完整编码流程如下:
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 1.数据分析 │ → │ 2.数据编码 │ → │ 3.纠错编码 │ → │ 4.数据排列 │ → │ 5.掩码处理 │
│ │ │ │ │ │ │ │ │ │
│ 确定编码 │ │ 将数据转换 │ │ 生成Reed- │ │ 将码字放入 │ │ 应用掩码 │
│ 模式和版本│ │ 为位流 │ │ Solomon码 │ │ QR码矩阵 │ │ 优化图案 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘
步骤详解:
- 数据分析:确定最优编码模式、版本号和纠错等级
- 数据编码:添加模式指示符和字符计数指示符,将数据转换为位流,添加终止符和填充位
- 纠错编码:将数据分为若干块,对每块生成 Reed-Solomon 纠错码字
- 数据排列:按照蛇形路径将数据码字和纠错码字放入 QR 码矩阵
- 掩码处理:尝试 8 种掩码图案,选择使评分最优的一种
7.2.5 Reed-Solomon 纠错算法
Reed-Solomon(RS)编码是 QR 码容错能力的核心。它属于分组纠错码,基于有限域(Galois Field, GF)上的多项式运算。
基本原理:
设数据码字为 $d_0, d_1, \ldots, d_{n-1}$,构造数据多项式:
$$D(x) = d_0 + d_1 x + d_2 x^2 + \cdots + d_{n-1} x^{n-1}$$
纠错码字通过对 $D(x)$ 在特定点求值得到:
$$c_i = D(\alpha^i) \mod p(x)$$
其中 $\alpha$ 是有限域 $GF(2^8)$ 的本原元,$p(x)$ 是不可约多项式:
$$p(x) = x^8 + x^4 + x^3 + x^2 + 1$$
解码过程:
当 QR 码部分损坏时,接收端通过求解方程组恢复原始数据。设接收到的码字为 $r_0, r_1, \ldots, r_{n+k-1}$(含 $k$ 个纠错码字),定义错误多项式和伴随式(Syndrome),通过 Berlekamp-Massey 算法或 Euclidean 算法求解错误位置和错误值。
7.2.6 版本与容量
QR 码共有 40 个版本(Version 1 到 Version 40),版本号与模块数的关系为:
$$\text{模块数} = 4 \times \text{版本号} + 17$$
| 版本 | 模块数 | 数据容量(字节模式,纠错等级L) | 数据容量(数字模式,纠错等级L) |
|---|---|---|---|
| 1 | 21 x 21 | 17 字节 | 41 位数字 |
| 5 | 37 x 37 | 77 字节 | 185 位数字 |
| 10 | 57 x 57 | 174 字节 | 419 位数字 |
| 20 | 97 x 97 | 589 字节 | 1,423 位数字 |
| 30 | 137 x 137 | 1,256 字节 | 3,041 位数字 |
| 40 | 177 x 177 | 2,953 字节 | 7,089 位数字 |
Version 40 的 QR 码最大可存储 7,089 个数字字符或 2,953 个字节(约 2.9 KB)。
7.3 其他二维码标准
除了 QR Code,还有多种二维码标准,各有特色:
7.3.1 Data Matrix
Data Matrix 由 International Data Matrix 于 1994 年开发,广泛应用于工业标记和电子元器件标识。
| 特性 | 说明 |
|---|---|
| 外观 | 方形或矩形,由黑白模块组成 |
| 最大容量 | 2,335 个数字字符 |
| 容错 | Reed-Solomon 纠错 |
| 特点 | 无需静区(利用 L 形边界定位) |
| 应用 | 电子元器件、医疗器械标记 |
7.3.2 PDF417
PDF417(Portable Data File 417)由 Symbol Technologies 于 1991 年开发,是一种堆叠式二维码。
| 特性 | 说明 |
|---|---|
| 外观 | 多行堆叠的一维条码 |
| 最大容量 | 1,850 个数字字符 |
| 容错 | 两个纠错等级 |
| 特点 | 可打印在证件上,美国驾照标准 |
| 应用 | 身份证件、运输标签、不动产 |
7.3.3 Aztec Code
Aztec Code 由 Andrew Longacre 于 1995 年发明,中心有一个定位图案。
| 特性 | 说明 |
|---|---|
| 外观 | 方形,中心有定位图案(类似“牛眼“) |
| 最大容量 | 3,832 个数字字符 |
| 容错 | Reed-Solomon 纠错,可调比例 |
| 特点 | 不需要静区,尺寸更紧凑 |
| 应用 | 航空 boarding pass、交通票务 |
7.3.4 二维码标准对比
| 特性 | QR Code | Data Matrix | PDF417 | Aztec Code |
|---|---|---|---|---|
| 类型 | 矩阵式 | 矩阵式 | 堆叠式 | 矩阵式 |
| 最大容量 | 7,089 数字 | 2,335 数字 | 1,850 数字 | 3,832 数字 |
| 容错等级 | 4级(L/M/Q/H) | 可调 | 2级 | 可调比例 |
| 需要静区 | 是 | 否 | 是 | 否 |
| 读取速度 | 最快 | 快 | 中等 | 快 |
| 中文支持 | 是 | 是 | 是 | 是 |
| 主要应用 | 移动支付、广告 | 工业标记 | 证件 | 交通票务 |
7.4 QR码安全
7.4.1 恶意二维码
二维码本身不携带恶意代码,但可以被用来引导用户访问恶意网站。常见的攻击方式:
- 钓鱼攻击:将二维码指向伪造的登录页面,窃取用户账号密码
- 恶意软件下载:扫描后自动下载并安装恶意应用
- 中间人攻击:通过二维码劫持通信会话
- 虚假支付:将收款二维码替换为攻击者的收款码
7.4.2 防护建议
- 扫码前预览:使用支持 URL 预览的扫码工具,查看目标地址后再决定是否访问
- 使用安全扫码工具:选择带有安全检测功能的扫码应用
- 注意 URL 来源:确认链接域名是否为官方域名
- 不要随意扫码:来源不明的二维码不要扫描
- 开启支付验证:移动支付应设置密码或生物识别验证
7.5 Rust 二维码实战
7.5.1 环境准备
在 Cargo.toml 中添加依赖:
[dependencies]
qrcode = "0.14"
image = "0.25"
7.5.2 生成基本二维码
use qrcode::QrCode;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 生成二维码
let code = QrCode::new("https://www.rust-lang.org")?;
// 在终端打印二维码(ASCII 形式)
println!("{}", code.render::<char>()
.quiet_zone(false)
.module_dimensions(2, 1)
.build());
// 输出二维码的基本信息
println!("版本: {}", code.version());
println!("宽度: {} 模块", code.width());
Ok(())
}
7.5.3 生成二维码图片
#![allow(unused)]
fn main() {
use image::{GenericImage, ImageBuffer, Rgba};
use qrcode::QrCode;
fn generate_qr_image(data: &str, size: u32) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
// 1. 编码数据生成二维码
let code = QrCode::new(data.as_bytes()).unwrap();
// 2. 设置二维码的模块大小,并添加白色边框
let border = 4;
let module_size = (size as f32 / (code.width() as f32 + 2.0 * border as f32)).ceil() as u32;
// 3. 将二维码渲染为图像
code.render::<Rgba<u8>>()
.min_dimensions(size, size) // 设置最小尺寸,确保清晰
.quiet_zone(true) // 添加静区(白色边框)
.module_dimensions(module_size, module_size)
.build()
}
}
7.5.4 自定义颜色的二维码
use image::{ImageBuffer, Rgba, Rgb};
use qrcode::{QrCode, Color};
fn generate_colored_qr(
data: &str,
fg_color: [u8; 3],
bg_color: [u8; 3],
module_size: u32,
) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
let code = QrCode::new(data).unwrap();
let border = 4;
let width = code.width();
let img_size = (width + border * 2) as u32 * module_size;
let mut img = ImageBuffer::from_pixel(
img_size,
img_size,
Rgba([bg_color[0], bg_color[1], bg_color[2], 255]),
);
for y in 0..width {
for x in 0..width {
let color = if code[(x, y)] == Color::Dark {
Rgba([fg_color[0], fg_color[1], fg_color[2], 255])
} else {
Rgba([bg_color[0], bg_color[1], bg_color[2], 255])
};
// 绘制模块
for dy in 0..module_size {
for dx in 0..module_size {
let px = (x as u32 + border as u32) * module_size + dx;
let py = (y as u32 + border as u32) * module_size + dy;
img.put_pixel(px, py, color);
}
}
}
}
img
}
fn main() {
// 生成蓝色前景、白色背景的二维码
let qr = generate_colored_qr(
"https://www.rust-lang.org",
[0, 100, 200], // 蓝色前景
[255, 255, 255], // 白色背景
10,
);
qr.save("qr_blue.png").unwrap();
// 生成红色前景的二维码
let qr_red = generate_colored_qr(
"Hello, Rust!",
[220, 50, 50], // 红色前景
[255, 255, 255], // 白色背景
10,
);
qr_red.save("qr_red.png").unwrap();
}
7.5.5 带Logo的二维码
利用 QR 码的纠错能力,可以在二维码中心放置 Logo 而不影响识别。需要使用较高的纠错等级(H 级,30% 容错)。
use image::{GenericImage, ImageBuffer, Rgba, imageops};
use qrcode::{QrCode, EcLevel};
fn generate_qr_with_logo(
data: &str,
logo_path: &str,
size: u32,
logo_ratio: f32, // Logo 占二维码的比例(建议 0.2-0.3)
) -> Result<ImageBuffer<Rgba<u8>, Vec<u8>>, Box<dyn std::error::Error>> {
// 1. 使用 H 级纠错生成二维码
let code = QrCode::with_error_correction_level(
data.as_bytes(),
EcLevel::H, // 30% 纠错容量
)?;
// 2. 渲染二维码
let border = 4;
let module_size = (size as f32 / (code.width() as f32 + 2.0 * border as f32)).ceil() as u32;
let mut qr_img = code.render::<Rgba<u8>>()
.min_dimensions(size, size)
.quiet_zone(true)
.module_dimensions(module_size, module_size)
.build();
// 3. 加载 Logo
let logo = image::open(logo_path)?.to_rgba8();
let (qr_w, qr_h) = qr_img.dimensions();
// 4. 计算 Logo 尺寸和位置(居中)
let logo_w = (qr_w as f32 * logo_ratio) as u32;
let logo_h = (qr_h as f32 * logo_ratio) as u32;
let resized_logo = logo.resize(logo_w, logo_h, imageops::FilterType::Lanczos3);
// 5. 添加白色背景区域
let padding = 10;
let bg_w = logo_w + padding * 2;
let bg_h = logo_h + padding * 2;
let mut bg = ImageBuffer::from_pixel(bg_w, bg_h, Rgba([255, 255, 255, 255]));
bg.copy_from(&resized_logo, padding, padding)?;
// 6. 将 Logo 放置在二维码中心
let x = (qr_w - bg_w) / 2;
let y = (qr_h - bg_h) / 2;
qr_img.copy_from(&bg, x, y)?;
Ok(qr_img)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let qr = generate_qr_with_logo(
"https://www.rust-lang.org",
"images/rust-logo-blk.svg",
600,
0.25,
)?;
qr.save("qr_with_logo.png")?;
println!("带 Logo 的二维码已生成: qr_with_logo.png");
Ok(())
}
7.5.6 二维码解码/识别
使用 bardecoder crate 进行二维码解码:
[dependencies]
bardecoder = "0.5"
image = "0.25"
use bardecoder::decode::Decode;
use image::DynamicImage;
fn decode_qr(image_path: &str) -> Result<String, Box<dyn std::error::Error>> {
let img = image::open(image_path)?;
let decoder = bardecoder::default_decoder();
let results = decoder.decode(&img);
for result in results {
match result {
Ok(content) => {
println!("解码结果: {}", content);
return Ok(content);
}
Err(e) => println!("解码失败: {:?}", e),
}
}
Err("无法解码二维码".into())
}
fn main() {
match decode_qr("qr_code.png") {
Ok(content) => println!("二维码内容: {}", content),
Err(e) => println!("错误: {}", e),
}
}
7.5.7 批量生成二维码
use image::{GenericImage, ImageBuffer, Rgba};
use qrcode::QrCode;
use std::fs;
/// 批量生成二维码并保存
fn batch_generate_qr_codes(
data_list: &[(&str, &str)], // (数据, 输出文件名)
size: u32,
) -> Result<(), Box<dyn std::error::Error>> {
for (data, filename) in data_list {
let code = QrCode::new(data.as_bytes())?;
let border = 4;
let module_size = (size as f32 / (code.width() as f32 + 2.0 * border as f32)).ceil() as u32;
let img = code.render::<Rgba<u8>>()
.min_dimensions(size, size)
.quiet_zone(true)
.module_dimensions(module_size, module_size)
.build();
img.save(filename)?;
println!("已生成: {} -> {}", data, filename);
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let data_list = &[
("https://www.rust-lang.org", "qr_rust.png"),
("https://crates.io", "qr_crates.png"),
("https://docs.rs", "qr_docs.png"),
("https://github.com/rust-lang/rust", "qr_github.png"),
];
// 创建输出目录
fs::create_dir_all("output_qr")?;
batch_generate_qr_codes(data_list, 300)?;
Ok(())
}
7.5.8 合并多个二维码
以下代码将多个二维码横向合并为一张图片:
use image::{GenericImage, ImageBuffer, Rgba};
use qrcode::QrCode;
fn generate_qr_image(data: &str, size: u32) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
let code = QrCode::new(data.as_bytes()).unwrap();
let border = 4;
let module_size = (size as f32 / (code.width() as f32 + 2.0 * border as f32)).ceil() as u32;
code.render::<Rgba<u8>>()
.min_dimensions(size, size)
.quiet_zone(true)
.module_dimensions(module_size, module_size)
.build()
}
fn combine_qr_codes_horizontally(
img1: &ImageBuffer<Rgba<u8>, Vec<u8>>,
img2: &ImageBuffer<Rgba<u8>, Vec<u8>>,
gap: u32,
) -> ImageBuffer<Rgba<u8>, Vec<u8>> {
// 1. 计算新图片的尺寸
let (w1, h1) = img1.dimensions();
let (w2, h2) = img2.dimensions();
let final_width = w1 + gap + w2;
let final_height = h1.max(h2);
// 2. 创建新画布,并填充白色背景
let mut combined =
ImageBuffer::from_pixel(final_width, final_height, Rgba([255, 255, 255, 255]));
// 3. 将第一张图复制到左侧 (0, 0) 位置
combined.copy_from(img1, 0, 0).unwrap();
// 4. 将第二张图复制到右侧 (w1 + gap, 0) 位置
combined.copy_from(img2, w1 + gap, 0).unwrap();
combined
}
fn main() {
// 1. 生成两个二维码图像,尺寸设为 200x200
let qr1 = generate_qr_image("https://qr.alipay.com/fkx18203840eqbo648wdo95", 200);
let qr2 = generate_qr_image("wxp://f2f0QuWokq-0iWOJrBOVHz7vVLoOhrChnMI28COpdSmFzRs", 200);
// 2. 横向合并,间隔 20 像素
let combined = combine_qr_codes_horizontally(&qr1, &qr2, 20);
// 3. 保存最终图片
combined.save("/tmp/combined_qr_codes.png").unwrap();
}
7.6 二维码的用途
二维码的应用已经渗透到生活的方方面面:
| 类别 | 应用场景 | 说明 |
|---|---|---|
| 移动支付 | 付款码、收款码 | 支付宝、微信支付 |
| 身份认证 | 健康码、电子证照 | 疫情防控、电子身份证 |
| 商品溯源 | 商品编码、防伪标签 | 食品安全、药品追溯 |
| 广告营销 | 链接分享、优惠券 | 扫码领红包、关注公众号 |
| 交通出行 | 电子车票、登机牌 | 高铁二维码、Aztec Code 登机牌 |
| 社交互动 | 加好友、名片交换 | 微信扫码加好友 |
| 文档管理 | 文件分享、会议签到 | 扫码下载文件、签到打卡 |
| 物联网 | 设备配对、WiFi连接 | 扫码连接WiFi、IoT设备配网 |
7.7 标准与规范
QR Code 的国际标准为 ISO/IEC 18004,由国际标准化组织(ISO)和国际电工委员会(IEC)联合发布。
- ISO/IEC DIS 18004 - QR Code 国际标准
QR Code 的官方信息:
- QR码 官网
- QR码的成功之路
- qrcode.show - 在线制作二维码
技术原理参考:
7.8 相关 Rust 库
| 库名 | 说明 | 链接 |
|---|---|---|
| qrcode | QR 码生成库,支持多种输出格式 | GitHub |
| bardecoder | 多格式条码/二维码解码库 | GitHub |
| rqrr | 高性能 QR 码解码库 | GitHub |
| quircs | quirc QR 解码器的 Rust 绑定 | crates.io |
| image | 图像处理库(配合二维码库使用) | GitHub |
| fast_qr | 高速 QR 码生成库(SIMD 加速) | GitHub |
7.9 总结
QR码核心参数对比
| 参数 | 说明 |
|---|---|
| 版本范围 | Version 1 (21x21) 到 Version 40 (177x177) |
| 模块数公式 | $4 \times \text{version} + 17$ |
| 编码模式 | 数字、字母数字、字节、汉字 |
| 纠错等级 | L(7%), M(15%), Q(25%), H(30%) |
| 最大容量 | 7,089 数字 / 4,296 字母数字 / 2,953 字节 |
| 纠错算法 | Reed-Solomon 编码 |
| 有限域 | $GF(2^8)$,不可约多项式 $x^8 + x^4 + x^3 + x^2 + 1$ |
二维码标准对比
| 特性 | QR Code | Data Matrix | PDF417 | Aztec Code |
|---|---|---|---|---|
| 类型 | 矩阵式 | 矩阵式 | 堆叠式 | 矩阵式 |
| 最大容量 | 7,089 数字 | 2,335 数字 | 1,850 数字 | 3,832 数字 |
| 容错等级 | 4级 | 可调 | 2级 | 可调 |
| 需要静区 | 是 | 否 | 是 | 否 |
| 读取速度 | 最快 | 快 | 中等 | 快 |
| 中国普及度 | 最高 | 低 | 低 | 中等 |
关键公式汇总
| 公式 | 说明 |
|---|---|
| $\text{模块数} = 4v + 17$ | QR 码版本 v 对应的模块数 |
| $p(x) = x^8 + x^4 + x^3 + x^2 + 1$ | Reed-Solomon 有限域不可约多项式 |
| $\text{数字容量} = \lfloor n/3 \rfloor \times 10 + r$ | 数字模式位流计算 |
7.10 练习题
-
基础题:使用
qrcodecrate 生成一个包含你名字的二维码,保存为 PNG 格式。 -
纠错等级对比:分别使用 L、M、Q、H 四种纠错等级生成相同内容的二维码,对比它们的大小差异,并用图像编辑工具遮挡部分区域后测试是否能正确解码。
-
带 Logo 二维码:选择一张你喜欢的 Logo 图片,使用 H 级纠错生成带 Logo 的二维码,并验证其可扫描性。
-
批量生成:编写一个 Rust 程序,读取 CSV 文件中的 URL 列表,为每个 URL 生成一个二维码,文件名为 URL 的哈希值。
-
编码模式分析:对于字符串
"Hello, World! 123",分析 QR 码会选择哪种编码模式?为什么?如果字符串是"1234567890"呢? -
容量计算:Version 10 的 QR 码有多少个模块?在纠错等级 M 下,字节模式最多能存储多少个字节?
-
思考题:为什么二维码在中国如此普及,而在欧美国家的使用率相对较低?从技术、商业模式、社会习惯等角度分析。
-
安全实践:编写一个 Rust 程序,生成二维码前先验证 URL 的合法性(检查协议、域名等),拒绝生成指向已知恶意域名的二维码。