為什么超市售貨員掃一下條形碼就能結(jié)賬?為什么別人掃一下你的二維碼就能加上你的微信?小小的條形碼、二維碼中究竟如何蘊(yùn)藏如此多不同的信息?今天,就讓我們和孩子們一起走進(jìn)條形碼、二維碼的世界吧!

撰文 | 吳進(jìn)遠(yuǎn)
我們前一篇文章討論了摩爾斯電碼,談到它對當(dāng)代的計算機(jī)技術(shù)有著深刻的影響。這種影響并不是在具體的編碼方法上,而主要是體現(xiàn)在思維方法上。現(xiàn)在我們購買到的大多數(shù)商品,包裝上面都有條形碼。條形碼初期存在多個發(fā)明,現(xiàn)在也存在非常多的變種。條形碼可以算是和摩爾斯電碼沾點邊,因為早期的一位發(fā)明家曾經(jīng)受到過摩爾斯電碼的啟示。確實,從某種意義上說,我們可以想象把摩爾斯電碼的點和劃,垂直畫成線,變成窄和寬的條紋。當(dāng)然現(xiàn)在主流應(yīng)用上的條形碼,并不是簡單地從摩爾斯電碼轉(zhuǎn)換來的。條形碼世界
今天,計算機(jī)越來越多地參與著我們生活的方方面面。為了把信息迅速方便地送入計算機(jī),人們設(shè)計了各種各樣的編碼,條形碼就是其中的一種。其他的編碼方法,比如RFID,盡管成本越來越低,但相比之下,條形碼仍然是最便宜的,其成本幾乎為零,因為條形碼只需要用油墨印在包裝盒上即可。條形碼是怎樣將信息編碼的呢?對于不同的應(yīng)用存在許多標(biāo)準(zhǔn)。我們這里介紹一下其中一個標(biāo)準(zhǔn),即GTIN-13以及它所對應(yīng)的條形碼編碼標(biāo)準(zhǔn)EAN-13。GTIN是Global TradeItem Number的意思,其中的GTIN-13標(biāo)準(zhǔn)是用13位數(shù)字代表全球貿(mào)易中的萬物。其中最高的三位代表生產(chǎn)物品的國家,比如中國生產(chǎn)的產(chǎn)品前三位是690-695。其余一些位數(shù)表示地域,行業(yè)等信息,靠后的一些位數(shù)是物品的代碼。如果13位數(shù)全部用滿,可以表述10萬億種不同的物品,足夠全球所有人每人研發(fā)1000種不同的商品。我們現(xiàn)在在市場上買到的商品,大部分都有這樣一個13位數(shù)的代碼。GTIN兼容了國際標(biāo)準(zhǔn)書號(ISBN),國際標(biāo)準(zhǔn)期刊序號(ISSN)等等。當(dāng)GTIN-13需要印刷成條形碼的時候,使用的是EAN-13條形碼標(biāo)準(zhǔn)。比如作者參與寫作的一本科學(xué)普及書的條形碼,如下圖所示。對于圖書,不管是哪個國家出版的,其前三位都被指定為978或979。而雜志期刊等,前三位總是977。
大家可以仔細(xì)觀察上面的條形碼,我們可以看到數(shù)字5出現(xiàn)了幾次,不過它們似乎長的不完全一樣,在左邊的兩個是一個樣,而在右邊的兩個顯然不同。如果認(rèn)真看看,左邊與右邊條紋是“黑白顛倒”的關(guān)系??墒牵倏纯醋筮叧霈F(xiàn)的兩個7,它們似乎也不一樣。此外,在條形碼左邊與右邊各有6位數(shù)字,這并不難理解,但整個碼符號的最左邊那個9是怎么來的?難道可以無中生有嗎?
為此,我們需要了解一下EAN-13(包括ISBN,ISSN,UPC等)條形碼的生成規(guī)則。條形碼的編碼規(guī)則
EAN-13條形碼是用兩個條紋來表示一個數(shù)字的,整個碼包括13個數(shù)字,其中直接編碼12個,左邊6個右邊6個,第13個用后面介紹的方法隱含編碼。條形碼兩邊與中間安排了起始符(S)中間符(M)以及結(jié)束符(E),各為兩個窄條。因此,所有碼,不管具體數(shù)字是什么,都包含有30個條紋。那么,每個數(shù)字又是怎樣用兩個條紋表示的呢?這就需要我們在更細(xì)尺度上討論。每個數(shù)字的兩個條紋,包括了兩黑兩白四個區(qū)域,它們的總寬度為7個單位寬度。這個單位寬度可以根據(jù)印刷精度自由選擇,比如當(dāng)我們把單位寬度選定為0.5毫米時,每個數(shù)字所占寬度為3.5毫米。對于左邊6個數(shù)字,每個數(shù)字的左邊一定是白的,而右邊一定是黑的。每個數(shù)字從左到右都是“白黑白黑”地構(gòu)造。這樣,當(dāng)幾個數(shù)字一個個緊挨在一起的時候,它們之間就可以存在一個清晰的邊界。了解了這些,我們就可以想象自己是當(dāng)年制定這種條形碼的設(shè)計師,很容易地數(shù)一數(shù)可用的編碼有哪些。我們可以把7個單位寬度組合在一起,看成7個比特的二進(jìn)制數(shù)。如果這7個比特可以任意安排黑白,則可以表達(dá)128個字符。不過我們已經(jīng)限定最左邊一定是白色,最右邊一定是黑色,這樣就只有中間5個比特可以改變,或者說最多可以表達(dá)32個字符。把這32個可能的字符全部畫出來,就得到下面這個圖。
現(xiàn)在,我們把不符合“白黑白黑”要求的字符去掉,還剩下20個可用的字符。這20個字符又可以分成兩類,一類包含有奇數(shù)個(3個或5個)單位寬度為黑,共10個,另一類包含有偶數(shù)個(2個或4個)單位寬度為黑,也是共有10個。我們把這兩類字符分別用來作為0-9數(shù)字的代碼。其中奇偶性為奇的10個稱為EAN-L碼,在上面圖中用淺棕色標(biāo)注。奇偶性為偶地10個稱為EAN-G碼,在上面圖中用淺綠色標(biāo)注。這兩種左邊的條形碼如下圖所示。
那么,右邊的條形碼又是什么樣的呢?我們希望最終的條形碼具有一定的對稱性,比如希望右邊的碼左黑右白,這樣可以與結(jié)束符(E)有一個清晰的邊界。因此最簡單的一個做法,是把EAN-L碼黑白顛倒,這樣我們就有了EAN-R碼,如下圖所示。顯然,由于EAN-L的奇偶性為奇,因此很容易看出EAN-R的奇偶性為偶,里面黑條的總寬度為偶數(shù)個單位寬度,與EAN-L正好反過來。

有了左邊與右邊數(shù)字的條形編碼圖形,我們就很容易拼接出一個完整的條形碼。當(dāng)我們只需要編碼12個數(shù)字時,也就是說當(dāng)13位數(shù)中最高位為0時,左邊6個數(shù)字都用EAN-L碼,右邊6個數(shù)字則用EAN-R碼。把數(shù)字與S,M,E符拼接在一起后,我們就可以得到如下圖所示的條形碼。

我們前面問過一個問題,在13位編碼的整個條形碼碼符號的最左邊那個數(shù)字(我們前面圖書的條形碼中的9)是怎么來的。此外,大家還會問,我們前面談到的EAN-G圖形能不能用在條形碼的左邊,代替EAN-L碼?實際上,這兩個問題是聯(lián)系在一起的,EAN-13中,最左邊那個數(shù)字,就是利用EAN-G圖形,代替左邊6個數(shù)字中一部分EAN-L圖形來表述的。
在條碼左邊6個數(shù)字中,每個數(shù)字可以選用L或者G兩種碼。因此通過選擇每一位的L或G,一共可以得到64(2的6次方)種組合。人們從這64種組合中,挑出了10個組合,用來表述13位編碼中的最左邊那個數(shù)。這10個組合以及它們代表的數(shù)字為:0=LLLLLL;1=LLGLGG;2=LLGGLG;3=LLGGGL;4=LGLLGG;5=LGGLLG;6=LGGGLL;7=LGLGLG;8=LGLGGL;9=LGGLGL。這樣一來,最左邊這個數(shù)不需要直接用一個單獨的圖形表述,而只需要通過選配左邊6個數(shù)字編碼圖形的L或G組來“隱喻”。現(xiàn)在我們再回過頭看我們前面那本書的條形碼,原來的一些疑問也就豁然開朗了。
首先,右邊的兩個5與左邊的兩個5自然不會是相同的,我們知道 EAN-L 與 EAN-R 對應(yīng)的10個編碼,是黑白反轉(zhuǎn)的關(guān)系。那么,同在左邊的相同數(shù)字編碼一樣嗎?不一定。比如上面條碼中左邊的兩個7,它們一個是 L 碼另一個是 G 碼。因為這個條碼編碼了第13位數(shù)字9,因此左邊6個數(shù)字的L或G的選擇為9=LGGLGL,因此它的第一個7是L而第二個7是G,所以這兩個7長得不一樣。此外,我們還可以看出左邊的兩個5恰好都是L,否則它們也未必相同。
條形碼帶給我們的啟示
在很多編碼工作的實踐中,我們的著眼點是提高編碼的效率。也就是說,利用盡量少的資源來存儲或者傳輸比較多的信息。但是在設(shè)計條形碼的時候,更需要考慮的,是可靠性和準(zhǔn)確性。為此,我們可能會“浪費”一些編碼資源,來提高編碼的冗余度。在前面談到的條形碼中,每個數(shù)字的編碼空間有7個單位寬度,如果充分利用可以編制128個字符。但是我們對編碼空間作了限制:(1)左白右黑,(2)包含兩個條紋。這樣一來,這個編碼空間中就只剩下20個可以用的組合了。但是這樣做帶來的好處非常多。首先是兩個數(shù)字挨在一起,它們之間存在一個黑白清晰的邊界。同時在每個數(shù)字的編碼空間中,也不會出現(xiàn)一大片黑,或者一大片白的狀況,而是存在足夠的黑白變化,便于掃描器辨別。更重要的是,這樣的編碼方法提供了很多簡便的查錯方法。比如一個完整的條形碼,不論是什么內(nèi)容,總是包含30個條紋。這樣,當(dāng)掃碼器掃過之后如果發(fā)現(xiàn)多于30或少于30個條紋,立即就能知道是出錯了。條形碼應(yīng)用中,還會出現(xiàn)一個常見的復(fù)雜性,就是掃碼器既可能從左向右正著掃,也可能反過來掃。這就要求條形碼自身攜帶左右標(biāo)識。當(dāng)我們在條形碼的左邊使用EAN-L碼,右邊使用EAN-R碼的時候,條形碼的左右就非常分明。左邊所有數(shù)字的奇偶性為奇,右邊所有數(shù)字的奇偶性為偶。當(dāng)我們需要編制13位數(shù)編碼,因此在左邊6個數(shù)中有些會使用EAN-G碼的時候,左邊有些數(shù)字的奇偶性也可能呈現(xiàn)偶。不過,我們前面看到,左邊6個數(shù)中最左邊那一位總是使用L碼,這就足以作為條形碼的左標(biāo)識了。從條形碼到二維碼
條形碼毫無疑問是非常成功的,但由于條形碼是編碼空間是一維的,因此可以攜帶的信息非常有限。很自然,人們想到要向平面上兩個維度發(fā)展。多年來,二維的條形碼出現(xiàn)過許多標(biāo)準(zhǔn)及變化。我們今天經(jīng)??吹降囊环N是QR碼。下圖所示QR碼是作者創(chuàng)作的一個科學(xué)普及音樂視頻文件在微云上的鏈接。
這里特意提醒一下讀者,一個二維碼當(dāng)中,直接編碼的內(nèi)容是幾十乃至上百個字母或數(shù)字,它們通常構(gòu)成一個鏈接,但它們不是視頻文件本身。視頻文件往往會需要幾十 MB 乃至幾十 GB 存儲空間,二維碼存儲不了那么多的數(shù)據(jù)。
當(dāng)然,二維碼要容納幾十乃至上百個字母和數(shù)字也并不容易。對比條形碼,人們對二維碼在可靠性和準(zhǔn)確性上的要求是一樣的。但是,二維碼要比條形碼容納更多的信息,因此還必須兼顧編碼的效率。這兩項要求在有的情況下是矛盾的,但是在很多時候二者是相輔相成的。二維碼掃碼使用時通常是用手機(jī)來拍照,在手機(jī)內(nèi)得到一個由像素構(gòu)成的二維點陣。手機(jī)中的軟件只有可靠準(zhǔn)確地獲得了二維碼的高度寬度等外形參數(shù),才能正確地讀取編制在二維碼中的數(shù)據(jù)。那么,怎樣才能方便地獲取二維碼的外形參數(shù)呢?這就需要我們在設(shè)計時作出仔細(xì)的考慮。從QR碼上,可以看到在左上角,右上角和左下角各有一個口字形或回字形的方塊。我們應(yīng)該可以猜測出,這三個方塊就是為了提供二維碼外形參數(shù)的。問題是,為什么需要三個方塊呢?只在左上角上留一個方塊行不行?我們知道手機(jī)的照相機(jī)照出來的照片是會發(fā)生形變的,如果只有一個方塊,就很不容易獲取二維碼的高度和寬度這兩個參數(shù)。如果只留兩個方塊,比如,保留右上角和左下角這兩個方塊。這好像也不行,因為在手機(jī)的照片中,軟件無法去辨認(rèn)哪邊是上哪邊是下。如果只保留左上角和右上角兩個方塊,則上下倒是可以區(qū)分出來了。但是由于手機(jī)照片的形變,我們只能得到二維碼寬度這個參數(shù),而很難得到高度這個參數(shù)。因此,我們現(xiàn)在看到的二維碼里面有三個大方塊。我們這里談到的僅僅是一些最基本的考慮,實際上二維碼里的學(xué)問還是不少的,這里有一個問題提供給大家思考。下面這個二維碼中,只包含了一個字母“a”。既然只有一個字母,那么這個字母占據(jù)的面積應(yīng)該是很小的吧?因此在整個碼所占的面積中,應(yīng)該大部分是空白。但是,我們并沒有在上面這個二維碼中看到大塊的白色或黑色區(qū)域,而是到處都是錯落有致,黑白相間。大家不妨想想為什么要這樣設(shè)計?用什么辦法可以做到這點?
孩子們在世間遇到的問題從來不會按照教科書的次序,先易后難。他們可能還沒有學(xué)過二進(jìn)制,但卻可能在任何時候找到一個條形碼或者二維碼來問家長。怎樣才能不被孩子問住呢?很簡單,和孩子一塊找一堆條形碼或二維碼一塊研究研究,找找規(guī)律,再上網(wǎng)查查,定會有所收獲。· 滴!刷卡的時候發(fā)生了什么?| 親子科學(xué)系列(8)
· 肥皂泡,光盤上的彩色和彩虹是一回事嗎?| 親子科學(xué)系列(7)
· 學(xué)文還是學(xué)理?所有的知識都不會白學(xué) | 親子科學(xué)系列(6)
· 爬門框、拆車輪,玩也是一種學(xué)習(xí)方式 | 親子科學(xué)系列(5)
· 被海淀家長碾壓?多和孩子一起讀無字之書 | 親子科學(xué)系列(4)
· 切薯格、拆門鎖,生活中的冷知識也有講究 | 親子科學(xué)系列(3)
· 玩手機(jī),學(xué)會考試需要的硬核知識 | 親子科學(xué)系列(2)
· 帶娃漫步荷塘邊,有月色,還可談科學(xué) | 親子科學(xué)系列(1)