我是風箏,公眾號「古時的風箏」,專注于 Java技術 及周邊生態,
文章會收錄在 JavaNewBee 中,更有 Java 后端知識圖譜,從小白到大牛要走的路都在里面,
這里所說的二維碼默認指的就是我們經常用到的微信二維碼這樣方形格式的,二維碼的英文全稱是 Quick Response Code,快速回應矩陣圖碼,
我們簡直不要太熟悉了,平均每天都要掃碼好幾次,做核酸掃碼、吃飯付錢掃碼、進出辦公樓掃碼,

就像上面這樣,二維碼就是用黑白或者明暗的格子來分別代表1和0,用黑色代表1,白色代表0,將0和1連接成二進制串,就能幻化出各種各樣的內容,當然其中要經過各種演算法、編碼、解碼等操作,
這是公眾號的二維碼,里面存盤的內容其實就是一個鏈接地址,也就是一段文本內容,然后用微信掃碼或者直接長按識別后,經過微信二維碼識別程式的處理,將其中的內容讀取出來,并且經過一系列的檢測,判斷這是微信公眾號的二維碼,于是跳轉到公眾號主頁,
那二維碼存盤的不都是文本嗎,不管是鏈接還是什么其他的東西,都是將二維碼決議之后再做處理,是鏈接就跳轉、是純文本就顯示,是這樣沒錯,但是畢竟二維碼最后是以圖片的形式展示,還是要越小越好,
所以,二維碼在最初設計的時候就制定了幾種模式,每種模式都用最簡便的編碼方式處理,保證最后出來的二維碼最簡單,
二維碼的幾種模式
二維碼說白了其實就是一種協議,和我們熟知的 IP、HTTP 協議類似,都是規定好一個標準,比如 IP 的頭部資訊,用4bit表示版本號,4bit表示首部長度等,
用4個bit標記當前二維碼所采用的的模式,反應到二維碼上就是4個格子,
數字編碼模式
此模式對應的場景是內容全部都是數字的情況,標記為0001,

字符編碼模式
內容包含數字和大寫的A-Z(不包含小寫)、以及$ % * + – . / : 和空格,標記為 0010 ,
位元組編碼模式
支持0x00~0xFF內所有的字符,標記為 0100,
Kanji mode
日文模式,因為二維碼是日本的工程師發明的,雙位元組編碼,這個模式下也可以支持中文,標記為 1000 ,
中文
支持中文,一個中文占3個位元組,標記為 1101 ,
混合模式
同時支持多種編碼格式,可以包含中文、應為、數字等內容,標記為 0011 ,
ECI
用于特殊字符,標記為 0111 ,
FNC1
主要是給一些特殊的工業或行業用的,比如GS1條形碼之類的,
二維碼版本的概念
二維碼版本是從0到40,每增加一個版本,在現有基礎上橫向增加4行、縱向增加4列,計算公式為(V - 1) * 4 + 21,其中V是版本號,比如版本是1,就是橫豎各21個格子,版本號是2,就是橫豎各25個格子,
例如下圖,其中內圈白色的版本號為1,格子數為 21 x 21,外圈加上了黃色的框,版本號為2,利用公式計算,其格子數為 25 x 25,橫豎各增加了 4 個,

版本越大,格子數越多,包含的資訊量就越大,但同時生成和識別所需的時間也就越長,而且如果二維碼的面積不變,那生成的格子也就越密集,
同樣都是一個英文字符 A的二維碼,左邊版本號為1,右邊版本號為40,清晰程度一目了然,

容錯率
我們都知道,二維碼在遮住其中的某些部分,或者不太清楚的情況下, 大多數時候仍然能識別出來的,這其實是和二維碼本身設計機制中的容錯率有關的,
下面兩個二維碼,左邊遮擋了一小部分,右邊遮擋了比較大的部分,左邊就可以正常的識別出來,而右邊的無論用什么客戶端也識別不出來,

我們平時生活中也能碰到這種情況,比如之前騎共享單車的時候,有的二維碼被刮了一點,但是還是可以掃出來的,但是有的二維碼被刮花了很大不一部分,無論掃多長時間也于事無補,
容錯率有4個等級,容錯等級越高可修復的面積就越大,最高的修復率達到30%,也就是二維碼有將近 1/3的面積被覆寫了,仍然能識別出來,當然不包括一些關鍵的區域,接下來會提到,
L 級
約 7 %的字碼可被修復
M 級
約 15%的字碼可以被修復
Q 級
約 25%的字碼可以被修復
H 級
約 30%的字碼可以被修復
一般場景下,容錯率都在15%左右,因為容錯等級越高,代價就越大,為了修復被遮蓋的部分,只能加入更多的冗余資訊,
字碼是二維碼中定義的一個概念,其實就是 8個bit,一個位元組的單位,
最大容量
決定二維碼的最大容量的因素有兩個,一個是版本,一個是容錯率,
版本越大,其格子數越多,所以可以編碼的二進制位就越多,最終所能存盤的資料量也就越多,
容錯率越低,其中所冗余的用于糾錯的資訊就越少,所以最終真正的資料內容也就越大,因為總容量是固定的,
下面表格是版本號40(最大版本號)、容錯率為L(最低容錯)的情況下,不同的內容型別所能承載的最大容量,數字最多是7089個,而采用 UTF-8 編碼的中文最多就984個,
| 內容型別 | (對于版本40) |
|---|---|
| 數字 | 最多7,089字符 |
| 字母 | 最多4,296字符 |
| 二進制數(8 bit) | 最多2,953 位元組 |
| 日文漢字 | 最多1,817字符(采用Shift JIS) |
| 中文漢字 | 最多984字符(采用UTF-8) |
現實情況中,一般也不會用二維碼做大量的存盤,我們可以看一下包含大量內容的二維碼最后的生成效果,已經給出提示了,手機和掃碼槍不易掃碼,

生成步驟
用最簡單的版本為1的二維碼舉例,
三個回型定位區域
這三個回型的區域可以幫助掃碼器(例如微信或掃碼槍)定位二維碼的,與三個回型定位區域緊挨的格子要留白,也就是下圖藍色線所在的格子,
并且最后在二維碼的周圍要有一定的留白空間,這樣可以幫助掃碼器快速的定位二維碼的整個區域,

在版本大于1的時候,還有一個小的定位塊,在靠右下的藍色框位置,

定位線
兩條用來定位的定位線,一般用在版本比較高的二維碼上,

填充格式資料
有一些固定的格子是用來存盤當前二維碼的格式資訊的,比如版本號、容錯級別、編碼模式,下圖中的藍色格子,分別有兩組15bit的區域,這兩部分就用來存盤當前二維碼的格式資訊,以二進制串的形式出現,不同的容錯級別以及其他設定不同,從而計算出來的二進制串也不同,這部分計算有專門的演算法,也可以通過查表的方式獲取,因為總共的組合方式是固定的,也就是幾十種,
這部分的資料因為有容錯率的設計,所以其實也是有冗余的,不光只有內容資料容錯,格式資料如果有部分遮擋的情況,也應該能識別出來,

如果版本大于6的話,會另外在右上和左下的回型區域的旁邊開辟兩塊3 x 6 的區域,用來存盤更多的格式資訊,如下圖的紅色格子,

填充內容資料
填充完格式資料后,剩下的就是真正的內容資料了,不同的模式有不同的計算方式,
例如字符模式,
1、拆成字符對:HE, LL, O (空格), WO, RL, D
2、參考字母數字表,轉換成對應的數字,然后用第一個字符數字*45+第二個字符數字,再轉換成二進制,一個字符補滿6位,兩個字符補滿11位,例如:HE=(45*17)+14=779,再轉換成二進制 779 → 01100001011,為什么要乘以45呢,就是這么約定的,
確定了模式和容錯級別,就能確定二維碼所能容納的最大值,如果資料量較少,沒有達到最大值,就要在內容二進制串后面加上四個0bit位 0000,用來標示真正內容的結束,
如果仍然不夠最大長度,則在后面連續加入 236和17的二進制串 11101100 00010001,至于為什么是這兩個數字,沒有為什么,就這么約定的,
最后將計算出來的二進制串從右下角開始依次填充,每次跨兩列,跳過回型定位區、定位線,以及個數資料區域,遇到1就填充成黑色或者深色,遇到0就填充上白色或者淺色,

掩碼美化
其實上一步生成好的二維碼就已經可以用了,但是資料都非常集中,所以最后的出來的二維碼黑色塊和白色塊分配嚴重不均,最后的效果就不太好,比如下面這樣的

為了顏值,所以基本上在生成最終二維碼之前都會加上掩模這一步,其實就是和8種固定的圖案(被稱作掩碼圖案)中的一種做異或計算,最終就可以得到一個分布均勻且比較美觀的最終形態,下面是這8種掩碼圖案,

最后的效果就是我們經常看到的那些二維碼,
還可以到一些在線的平臺對生成的二維碼進行美化,最后的呈現的效果可能像下面這樣,不過美化的太過,識別起來就比較慢了,不信你可以識別下面的二維碼感受一下,

參考檔案:https://www.nayuki.io/page/creating-a-qr-code-step-by-step
公眾號「古時的風箏」,Java 開發者,全堆疊工程師,bug 殺手,擅長解決問題,
一個兼具深度與廣度的程式員鼓勵師,本打算寫詩卻寫起了代碼的田園碼農!堅持原創干貨輸出,你可選擇現在就關注我,或者看看歷史文章再關注也不遲,長按二維碼關注,跟我一起變優秀!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/536024.html
標籤:其他
