[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fMi5jGgAumqiRyPw_6nnZW7x3DkEVscx9yYoXW48P-_A":3},{"item":4,"related":40},{"id":5,"type":6,"title":7,"slug":8,"summary":9,"body":10,"coverUrl":11,"productScreenshots":12,"productLinks":13,"authorName":14,"authorUrl":15,"authorSubject":16,"category":17,"tags":22,"sourceLabel":31,"sourceName":32,"sourceUrl":32,"status":33,"seoTitle":32,"seoDescription":32,"canonicalUrl":32,"isFeatured":34,"sno":35,"sortOrder":36,"publishedAt":37,"updatedAt":38,"createdAt":39},"9a71c215-da3c-448f-9dca-c231e4e8ac2f","article","二维码破了一块为什么还能扫？——错误纠正的秘密","qr-code-error-correction-reed-solomon-explained","二维码不是把文字简单涂成黑白格，而是加入了可计算的冗余。本文从定位图形、数据码字和掩模讲起，解释 Reed–Solomon 错误纠正、四档容错率、交错排列与 Logo 留白的边界。","二维码中间贴上一个 Logo、边角被挡住、打印机把几个小方块印糊了，为什么手机仍然可能识别？答案不是扫码器“猜得很聪明”，而是二维码在编码时主动加入了冗余数据。ISO\u002FIEC 18004 对 QR Code 的格式、编码、尺寸、错误纠正规则和参考解码算法都有定义。[ISO\u002FIEC 18004:2024](https:\u002F\u002Fwww.iso.org\u002Fstandard\u002F83389.html)\n\n二维码的黑白小方块叫 module。它们不只是装数据，还承担定位、校准、格式描述、掩码和错误纠正等任务。扫码器先要确定“这张图的坐标系在哪里”，然后才能把小方块还原成数据。\n\n## 扫码器第一眼看到的不是文字\n\n一个 QR Code 大致包含几类区域：\n\n- **定位图形**：三个角上的大方块，帮助识别方向和尺寸。\n- **校准图形**：在较大版本中帮助纠正透视和几何变形。\n- **时序图形**：辅助确定模块之间的网格间距。\n- **格式信息**：描述错误纠正等级和掩码模式。\n- **数据与纠错码字**：真正承载内容和恢复信息的区域。\n\n因此，二维码被遮挡时并不是所有区域都同等重要。遮住定位图形，扫码器可能连网格都找不到；遮住一部分数据区域，则可能仍然有机会通过纠错恢复。\n\n## 错误纠正到底加了什么\n\n二维码通常把内容编码成 codeword，再通过 Reed–Solomon 码计算出额外的纠错 codeword。它们不是原文的简单复制，而是根据有限域上的计算产生的校验信息。\n\n可以把它类比成一组考试答案和校验关系：如果其中少量答案被擦掉或改坏，剩余答案与校验关系仍然可能推回原来的值。解码器先把图像转成 0 和 1，再根据数据 codeword 与纠错 codeword 的关系定位错误并修复。\n\n二维码有四个常见错误纠正等级，等级越高，能够承受的损坏比例通常越大，但能放入的原始数据越少。这个比例是整体设计能力的近似，不是“任意位置损坏这么多百分比都一定能读”。损坏是否集中、是否破坏定位图形、打印对比度和透视角度都会影响结果。\n\n```mermaid\nflowchart TD\n    A[原始文本或二进制] --> B[编码成数据码字]\n    B --> C[生成 Reed-Solomon 纠错码字]\n    C --> D[交错排列并加入格式信息]\n    D --> E[放入二维码模块]\n    E --> F[相机拍摄并校正透视]\n    F --> G[定位网格与读取码字]\n    G --> H{是否有可恢复错误}\n    H -->|没有或可修复| I[还原原始数据]\n    H -->|超出纠错能力| J[解码失败]\n```\n\n## 为什么要交错排列\n\n如果纠错码只对应一整段连续数据，那么二维码被划掉一条长条时，可能恰好损坏同一段数据，恢复压力很大。实际编码会把数据和纠错信息分成块并进行交错，让空间上的一片损坏尽量分散到多个逻辑块中。\n\n这解释了一个常见现象：同样面积的遮挡，分散的小污点可能比一条贯穿码图的黑色划痕更容易恢复。错误纠正不仅取决于“坏了多少”，还取决于“坏在哪里、以什么形状坏”。\n\n## 掩码不是加密\n\n二维码在放置数据后，会尝试若干 mask pattern，把部分模块按规则翻转，让最终图案避免大面积同色、长直线或类似定位图形的结构。扫码器通过格式信息知道使用了哪种掩码，再把它反向还原。\n\n掩码的目的主要是提高可扫描性和降低误判，不是为了隐藏内容。任何拿到二维码图像的人，仍然可以按标准解码。把二维码当成“视觉加密”会产生错误的安全感。\n\n## 为什么加 Logo 不能随便加\n\nLogo 通常覆盖的是数据区域，并依赖较高错误纠正等级留下的冗余。但实际能否扫描还受到 Logo 面积、边缘是否清晰、背景对比度、打印质量和扫码器算法影响。高等级并不等于可以无限遮挡，更不能保证遮住定位图形后仍然工作。\n\n如果二维码承载的是支付、登录或门禁信息，二维码的纠错能力只负责“恢复数据”，不负责验证数据是否可信。业务仍然需要签名、过期时间、服务端校验和重放防护。\n\n## 一句话带走\n\n二维码之所以耐损，是因为它把一部分容量换成了冗余校验。扫码器并不是看着残缺图案猜答案，而是在定位、解掩码、读码字和数学纠错之后，尽力恢复一份仍然满足校验关系的数据。\n\n## 延伸阅读\n\n- [ISO\u002FIEC 18004:2024 QR Code 标准](https:\u002F\u002Fwww.iso.org\u002Fstandard\u002F83389.html)\n- [二维码错误纠正示意图](https:\u002F\u002Fcommons.wikimedia.org\u002Fwiki\u002FFile:QR_code_error_correction_diagram.png)\n- [QR Code damaged example](https:\u002F\u002Fcommons.wikimedia.org\u002Fwiki\u002FFile:QR_Code_Damaged.jpg)","\u002Fuploads\u002F2026-08-09\u002F2d6cea40-70a0-49e2-8931-2e5db404d4dc.jpg",[],[],"Foundit","https:\u002F\u002Ffoundit.cn","f39339b1-aaa6-4e86-b0c2-a6e6a21113b5",{"id":18,"name":19,"slug":20,"description":21},"6179d3b6-dc34-4483-9ded-3cd9f1b37a47","科普","abbreviation","介绍各领域新兴概念",[23,27],{"id":24,"name":25,"slug":26},"4c2bbea6-eab7-40a8-8447-1de478ff7749","分析","analyse",{"id":28,"name":29,"slug":30},"7c76bfc2-f80f-4ee0-a95d-27bd8708b434","技术","slug","资料来源",null,"published",false,66,0,"2026-08-09T00:00:00.000Z","2026-08-09T12:41:54.999Z","2026-08-09T12:13:30.055Z",[41,50,58],{"id":42,"type":6,"title":43,"slug":44,"summary":45,"coverUrl":46,"authorName":14,"sno":47,"publishedAt":48,"createdAt":49},"d6ff32b0-f499-453a-b1e3-c31a4a7a5b16","布隆过滤器：系统如何快速判断一个东西肯定不存在","bloom-filter-probabilistic-membership-explained","布隆过滤器用位数组和多个哈希函数，以极少内存快速筛掉肯定不存在的对象。本文讲清误报与漏报、误报率、删除难题，以及它在缓存、数据库和去重系统中的正确用法。","\u002Fuploads\u002F2026-08-06\u002F234915f6-2ef2-4820-8523-1a98544732f9.jpg",61,"2026-08-06T00:00:00.000Z","2026-08-06T05:38:25.381Z",{"id":51,"type":6,"title":52,"slug":53,"summary":54,"coverUrl":55,"authorName":14,"sno":56,"publishedAt":48,"createdAt":57},"23cd6a50-6716-4620-99f3-d5307511c8ee","CRDT：两个人同时改同一段文字，为什么不会互相覆盖","crdt-collaborative-editing-explained","CRDT 允许多个副本先本地修改，再通过可合并的数据结构最终收敛。本文用计数器和协同编辑讲清 CvRDT、CmRDT、并发插入、删除标记、元数据成本与业务语义冲突。","\u002Fuploads\u002F2026-08-06\u002F50641649-08d8-4cac-94f0-647eabe9fc5f.jpg",62,"2026-08-06T05:38:23.928Z",{"id":59,"type":6,"title":60,"slug":61,"summary":62,"coverUrl":63,"authorName":14,"sno":56,"publishedAt":48,"createdAt":64},"a04c10a4-3fe8-4537-81bd-354103c1078f","WebGPU：浏览器为什么能跑 3D、滤镜和部分 AI","webgpu-browser-gpu-computing-explained","WebGPU 让网页能更直接地使用 GPU 做渲染与通用计算。本文从 CPU 与 GPU 的分工讲起，拆解适配器、设备、缓冲区、WGSL 着色器和命令队列，并说明什么时候并行计算真的值得。","\u002Fuploads\u002F2026-08-06\u002F05f26f06-cda2-4ed3-b1f2-5f26c82c2a37.jpg","2026-08-06T05:38:22.834Z"]