商品条码生成器——校验位、十个图案,和它画出的每一格
打进或粘进一个 EAN-13、EAN-8 或 UPC-A 编号,然后一格一格地把这个符号建起来:校验位背后那张每一项都列出来的加权和表、三个码组全都由之而来的十个七格图案、护条和数字逐件的清单、留白与毫米的算术,还有一次回读——它先量画出来的那些条,再量你自己的浏览器光栅化出的那些像素,然后才承认那个数还留在里面。你打的任何东西都不离开这台设备。
符号本体:一根条一个矩形,数字写在下面
最后一位,算出来的
| 第几位 | 数字 | 权重 | 乘积 | 累计 |
|---|
这个符号的每一件,按它被画出的顺序
| 哪一件 | 它载着什么 | 格数 | 扫描枪量出来的宽度 |
|---|
这个符号量出来是多少,按格、像素和毫米
| 哪一段 | 格数 | 按你设的宽度是多少像素 | 按你设的 X 是多少毫米 |
|---|
十个图案,以及由它们做成的另外两组
| 数字 | 左码 | 右码 | 另一种左码 | 各自的 1 的个数 | 左码的宽度 |
|---|
从你的浏览器真正画出的东西里读回来
| 量的是什么 | 量了多少次 | 回来的是什么 | 对着打进去的那个数 |
|---|
读到哪里停下来,以及为什么
| 它停下来不读的那件事 | 它改说了什么 |
|---|
决定每一根条的那套算术
一个符号是一串宽度,不是一个数的图形
EAN 符号是一条被切成等宽格子的地面,每一格要么是墨要么是无墨。它跟排版没有关系:条不是字体里的笔画,条下面那几个数字是给肉眼看的礼貌,不属于码本身。EAN-13 从第一根黑条的左边缘到最后一根黑条的右边缘是 95 格,而这个数不是对任何印出来的东西的测量——它是 3 + 7 x 6 + 5 + 7 x 6 + 3:起始护条、六位数字每占七格、中间护条、再六位数字、结束护条。EAN-8 是同一个形状但每边四位,所以 67。UPC-A 是十二位,画法跟 EAN-13 画十三位时一样,所以又是 95。逐件那张表把这十五件按它们被画出的顺序排开,而"格数"那一列把各行接起来就是整个符号。
读数器真正量的不是格子而是游程:每根黑条持续几格、每个空隙持续几格,从左边缘开始数。5901234123457 的这些游程长 59 项,其中 30 项是条,宽度只可能是 1、2、3 或 4 格——1 格宽的 33 段,2 格的 18 段,3 格的六段,4 格的两段。墨盖住 49 格,空地 46 格。正是这个不对称让 EAN 看起来比别的家族的码更黑,也正是它让一次坏印刷被描述成"丢了一格"而不是"丢了一位":一个分不清 2 和 3 的读数器并不是读错了一个数,它是读错了一条条的一个边。
最后一位就是一次除法,而且把过程印出来
取校验位之前的十二位,从右边数起按 3、1、3、1……乘上去,再加起来。590123412345 得到 83;83 里尽量多地取出十个,剩 3;把 3 补到下一个十的那个数是 7,于是完整的号是 5901234123457。加权表一位一行,带着它的权重、它的乘积和一个累计和,所以这次加法可以用手指头逐行对。那套算术里有两处值得背下来。你打进去的最后一位永远是乘三的,不是乘一——这就是为什么一位打错很少是无辜的。还有,当那个和正好被十整除时,校验位是 0 而不是 10,因为它是一个穿着数字外衣的余数。
把最后一位留空,同一张表就为你拿回来的那个数画出来;把它打错,答案和你那一位会并排显示,条照你打的画。这是有意的。零售系统会拒绝一个校验位对不上的编号,可是按写出来的样子画出的符号,是唯一诚实的做法——它让你看见扫描仪将要遇到的到底是什么。
这一位值多少,以及它唯一看不见的那种错
改一位,加权和就变动那位权重乘上误差的量。权重是 1 和 3,误差是 1 到 9 中的一个,而 1 x 误差 和 3 x 误差 都不可能是十的倍数——所以余数一定动,一位打错每次都被抓到。三种格式各故意打错五百次,校验位在全部一千五百次里都和被损坏的编号不同意,一次也没漏。
交换相邻两位,和变动 (b - a) x 3 + (a - b) x 1,也就是 2 x (b - a)。二乘一个数是十的倍数,当且仅当那个数是五,所以一次交换恰好在两位相差五的时候看不出来:0 与 5、1 与 6、2 与 7、3 与 8、4 与 9,两个方向都算。那是九十对有序相异数字里的十对。把同一个实验换成做交换,1,345 个真的变了化的编号里有 133 个校验位安然无恙——不到十分之一,而那十对本身允许的是百分之十一。这里没有任何"更努力一点"能把校验和改好的余地;这是十进制和 3、1 这个权重排布的性质。相差五的一对调位,正是这一位从来不是为了抓而设计的失败,一个通过了校验位测试的编号照样可以恰好就是这个错。三种格式的每一个位置都对着规则枚举行过——2,430 对数字,规则和实测在全部 2,430 个上一致。
二十个图案,以及它们相隔多远
符号左边的十位数字取自十个七格图案。右边另用十个。还有第三组十个。三组来自同一张表:左边那十个是定死的,右边那十个是同样十个把墨和无墨对调,第三组就是把对调过的那份从另一头读。屏幕上的表把三组都印出来,所以这个推导可以逐行核对;这张表有两处承重。左边每个图案含三条或五条——奇数——而另外两组里每个图案含两条或四条。还有在任一组内部,最近的两个码也相差两格:每组全部四十五对被量过,没有一对是隔一格的邻居。
第二处性质就是阻止一个损坏的数字被读成另一个数字的东西:要把一个 3 变成同组里的 6,得有两格发生改变。可是第一组和第三组之间,距离有时是一。左边 0 的图案是 0001101,第三组里 4 的图案是 0011101:差一格。所以扫描仪完全可能盯着一团糊掉的 0 看出一个 4——它只是做不到不留下证据,而证据就是下一节。
第一位根本不画任何条
EAN-13 有十三位数字,却只画其中十二位。第一位整个花在一次选择上:左边那六位,每一位决定从第一组还是第三组来画。这个选择写成一排六个字母,L 或 G,在用的恰好十排:
LLLLLL LLGLGG LLGGLG LLGGGL LGLLGG LGGLLG LGGGLL LGLGLG LGLGGL LGGLGL
第一排属于数字 0,第二排属于 1,往下依次类推。5901234123457 以 5 开头,5 买的是 LGGLLG,而逐件那张表显示左边六位各被画成了哪一组。读数器反着做:它解出画出来的十二位,把那六个字母读出来,查这排字母属于哪一排,就免费拿回第一位。如果六个字母对不上十排中的任何一排,它根本没有编号可报。
再看那些排一会儿。其中一排一个 G 都没有,另外九排恰好三个。这不是一件奇闻,它是安全性质:只要有一格翻转,把一个槽位从 L 图案变成 G 图案或者变回去,G 的个数就变成一、二、四或五,而任何有效的排列都不含这些个数。所以 EAN-13 左半边的一格损坏永远不可能产出一个被读错号的编号——它只能产出一个什么都拼不出的符号。
损坏,量出来的
把符号一格一格拆开,看一个严格的读数器怎么说。每种格式取 200 个符号,依次翻转每一格——总共 51,400 个被损坏的符号——没有一个被读成它被画出来的那个号,也没有一个被读成任何号。拒绝分成四种说法,而这四种的分法本身有信息。EAN-13 的 19,000 次翻转里:2,200 次落在护条内,破坏了本该恰好是 101、01010、101 的形状;8,400 次落在右半边,那里只有第二组合法,而改一格从来落不到它的成员上;4,818 次落在左半边,落到了那一侧允许的两种图案之外的任何一种;3,582 次落在了真实的图案上,被拒绝的理由就是上一节那条——它们留下的六个字母拼不出一个首位。EAN-8 和 UPC-A 上第三桶又分了一次,因为在它们的左半边,一个 G 本身就是非法的,而不只是罕见。
两格损坏是另一个故事,而这个设计诚实的上限值得一个数字而不是一个耸肩。在 15,841 个随机翻转两格的 EAN-13 符号里,416 个仍然读得出一个号,这 416 个每一个都读成了错的号,其中 17 个的校验位还能加得回去。于是每三十八个被损坏的符号里有一个,会在双重损坏下变成一个看起来可信的错号;大约每四千个里有一个,把每一条结构规则和校验和一起躲过去。这里造的读数器会把这一切大声拒绝;车间里的扫描枪却有权猜,而当它猜的时候,那一万五千分之十七在下游每一个系统眼里都是合法的。
两种格式,一套条
十排排列里的第一排是六个 L,而 UPC-A 把整个左半边都画成 L。所以一个以 0 开头的 EAN-13,画的正是由它后十二位做成的那个 UPC-A 的条——随机比了 500 个,500 个完全一致,条和校验位都一致。校验位之所以一致,是因为那个前导零压在权重 1 下面,对那个和没有贡献。这就是两个名字在同一件包装上共存的原因:前面加一个零的"UPC"就是一个 EAN-13,不需要通知任何打印机。
EAN-8 是紧凑的那个:八位,67 格,每边四位,左半边被限制在第一组。它用在小包上,而更短的符号付出的代价是那套排列技巧没了——EAN-8 的形状里没有任何东西编码第九位,这也正是它左半边出现一个 G 时被当成损坏而不是当成信息的原因。
尺寸在变成墨之前只是算术
一格有多宽由打印机说了算,所以几何表给三种单位,让你自己换算。按每格 3 像素算,5901234123457 从第一根黑条的边缘到最后一根黑条的边缘是 285 像素,总宽 351,含两端各十一格空地。要毫米——热敏打印机上 0.495 是一个常见的模块宽度——同样的跨度变成 47.025 毫米和 57.915 毫米。这里的留白是一个控制而不是规则:它可以设成零,那时显示出来的宽度就恰好是条和条间空隙的宽度。任何被当作规范最小值报出的护条间距或印刷对比度都故意不作断言,因为那些没有一个被量过;屏幕上的是你选的那个模块数的算术。
小数宽度是图形和数字分道扬镳的地方。一根一格的条在每格 0.5 像素下只有半个像素宽,而光栅化按它自己的方式取整;回读那张表从浏览器真正画出的像素里取每格的中点,解出它找到的东西,然后要么印"每一格都还原成了它被画上的那个颜色",要么印有多少格还原错了颜色、读数器把结果当成了什么。那一行量的是浏览器,不是算术,所以它写"你的浏览器画出了",而不是"符号就是"。
这里没有回答的事
画出来的格式是三种,而且只有三种:EAN-13、EAN-8、UPC-A。它们共用一张十个图案的表,所以屏幕上的算术不用第二套规则就覆盖了全部三种。Code 128、Code 39、ITF 和 DataMatrix 不在这里;二维码是另一种东西,带纠错码字,它有它自己的工具。校验位证明的是算术,不是发放——这 95 格里没有任何一句说这个编号属于谁、或者根本被分配过,所以这里没有前缀登记处可查,也没有任何关于包装真假的断言。印刷等级同样不估:一份 ANSI 式的通过需要一次量出来的印刷加一把量出来的扫描器,而屏幕上一块画布两者都不是。
上面这一切就在你正在用的这个浏览器里、就着你打的那几位数字跑完,输入只有编号、格式和那两三个尺寸格子。你打的任何东西都不离开这台设备——表、图形、报告和存下的文件,都是从印在它们旁边的那同一段算术建起来的。