带你探索条形码世界的奥秘

二维码也就是QR码受所谓的移动互联网吵得也比较火,但是同学我奉劝你还是把一维的先搞懂吧。首先要说的就是印在商品上的条形码 就仅仅是一串竖条而已没什么玄机,对印刷面也无特定要求 黑与白只要能达到一定的光学分辨程度即可。说白了就是一个数字ID 和它下面标注的数字对应,弄条码 只是方便“快速录入数据” 仅此而已。再说一下 它仅仅是一个数字ID  既不包含价格信息 也不包含产地 等其鸟信息。但为什么扫一下就能知道他是哪种产品呢 还有价格呢。因为厂商产品的条形码都会添加到ZF的数据库字典里去 就跟那个鸟icp备案一样,这个就是中国物品编码中心。扫描完再到数据字典里一检索自然就出来了。

说到这里你就释然了。不管怎样 反正那个鬼激光器在条码上面晃一下瞬间就读出了那串数字ID 就是这么神奇 高效 还不会出错。远不是OCR文本识别技术对环境 对速度所能达到的。硬件方面咱也不懂 也不去研究那杆枪到底啥结构 里面又包括激光器 又包括信号放大电路啥的 反正就是能读条码就OK了。软件方面咱还是可以搞一搞的。

说到这可能瞬间就有想法了:

kao 这不是二进制么 0101 的。对 如果16个竖条来表示数字 按照我们简单的想法 他能表示的极限是 0~65535。如果表示字母的话呢 2字节? 这也太少了吧 16个竖条啊 。如果弄成横竖二维的方式呢,好了打住  关于这些下次再讨论。
首先就算激光器再精确读取也是有误差的 。我们平常在商品上看到的条码 绝对不是这种编码方式 这是非常不靠谱的。如果你真的发明了这种码 那你就自己搞个东西去读取它吧 整不死你。

看最上面的商品条码图就知道他的“细条” “粗条” “空白” 都有明显的区别 也就是辨识度。这是不是有点像摩斯电码?两长一短 、两短一长 、滴 滴滴 、嘿嘿。事实上超市商品 条码 所遵循的标准叫 EAN-13 、当然还有其他种类型的码 有码的  无码的 不是我们看的爱情动作片里的那种码哈。

先来看下ean-13的简单介绍:

  • 大体上看编码的基本依然是黑色代表1 空白代表0。
  • 可以看到前面 中间 最后 有3各部分的线段较长 称之为护线。分别为起始符 101    中间分隔符01010    终止符101。
  • 左边有6位数字x7=42个单位。
  • 右边也有6位数字x7=42个单位。

ean-13 难道这个13是因为它有13位数字?实际上最左边的数字称之为导入值 是不使用“竖条”进行编码的 ,最后一位称之为检查码 是根据它前面12位进行运算而来的
所以他的有效数据只有11位 每位为0~9。

随便找了点资料  来看下ean-13的编码规则:

数字符 左侧数据 右侧数据
  A B C
0 0001101 0100111 1110010 
1 0011001 0110011 1100110
2 0010011 0011011 1101100
3 011101 0100001 1000010
4 0100011 0011101 1011100
5 0110001 0111001 1001110
6 0101111 000101 1010000
7 0111011 0010001 1000100
8 0110111 0001001 1001000
9 0001011 0010111 1110100
前置字符左侧数据对应规则:
0 A A A A A A
1 A A B A B B
2  A A B B A B
3 A A B B B A
4 A B A A B B
5 A B B A A B
6 A B B B A A
7 A B A B A B
8 A B A B B A
9 A B B A B A 
 

 

 

 

 

 

 

 

 

 

 

 

怎么样 看了半天 也没看出规律吗?肯定还有很多疑问?ABC各代表啥意思?

  • ABC分别代表:A类编码 B类编码 C类编码 对应的数字0~9表现形式
  • AB都为左侧数据 规律是都以0开始1结尾。
  • C为右侧数据 规律是都以1开始0结尾。

所以右边不用考虑了
左边就要看第二个表了,看到 最开始说的那图没有

条形码下面一串数字最左边的 看到没有6 这是称之为导入值 其实是国家代码。中国的都为6。
第二个表的第6行:6A B B B A A 。总共13个数字 左右护线各6个数字 A B B B A A 是不是正好6个,所以左边就在A类编码与B类编码之间按照A B B B A A的规则来回变换来对对应的数字进行编码。而右边始终按照C类编码方式处理。更细心的同学可能发现了 A类跟C类是互补的  B类跟C类是对称的 不知亲爱的你发现没有。

是不是忘了什么东西?忘了最前面说的了么 ean-13 有效位只有11位   分隔符右边6位数有效位只有5位 最后一位是检查码 。也就是计算机里常说的奇偶效验啥的 防止标签污损 或者有人篡改而设置的。
检查码之计算步骤如下:
C1 = N1+ N3+N5+N7+N9+N11 C2 = (N2+N4+N6+N8+N10+N12)× 3
CC = (C1+C2) 取个位数 C (检查码) = 10 - CC  (若值为10,则取0)


EAN标准码的尺寸:
条码部分:宽31.35mm 高23.18mm
全部 :宽37.29mm 高26.26mm
可放大倍数:0.8 ----- 2

我需要怎么做?考验你动手能力的时候到了

好了 能量蓄得差不多了 哥要发大招了

先建个Bar类
第一步 做什么事情都先在脑子里思考下 你需要干什么 你得先确定下几个固定的变量吧,在生成条码之前你不可能不验证下数据的正确性吧?:

 1 int[] code ;//原始数据
 2 Bitmap result;//条形码图案
 3 int x;//第n个竖线单位 竖线单位数=6x7+6x7+3+3+5=95
 4 Brush[] cors = { Brushes.White, Brushes.Black };//背景色和前景色
 5 int zoom = 2;//分辨率 默认为1单位像素
 6 
 7 public Bitmap generate(string _code)
 8 {
 9     //初始化数据
10     code=new int[12];
11     result= new Bitmap(95*zoom+7*zoom, 55*zoom);//条码宽度单位
12     x=0;
13     //验证  必须为12位数字
14     if (RegexValidate("^[0-9]{12}$", _code) == false)
15     {
16         throw new Exception("数据格式错误,必须为12位的数字组合");
17         return result;
18     }
19     else
20     {
21         for (int i = 0; i < code.Length; i++)
22             code[i] = int.Parse(_code[i].ToString());
23     }
24     Graphics g = Graphics.FromImage(result);
25     g.FillRectangle(cors[0], 0, 0, result.Width, result.Height);
26     //条码生成算法部分
27     for (int i = 0; i <= 11; i++)
28     {
29         step(i);
30     }
31     //清空数据
32     code = new int[12];
33     //result = new Bitmap(95, 44);
34     x = 0;
35     return result;
36 }
37 
38 public static bool RegexValidate(string regexString, string validateString)
39 {
40     Regex regex = new Regex(regexString);
41     return regex.IsMatch(validateString.Trim());
42 }

 

第二步 然后你得把上面那两张表格抄下来 至于抄的方式嘛 各有各的C++的 java的 C#的:

 1 enum codeType
 2 {
 3     A, B, C
 4 }
 5 codeType getCp(int left)//left 0~5
 6 {
 7     codeType[][] cps = new codeType[10][];
 8     cps[0] = new codeType[] { codeType.A, codeType.A, codeType.A, codeType.A, codeType.A, codeType.A };
 9     cps[1] = new codeType[] { codeType.A, codeType.A, codeType.B, codeType.A, codeType.B, codeType.B };
10     cps[2] = new codeType[] { codeType.A, codeType.A, codeType.B, codeType.B, codeType.A, codeType.B };
11     cps[3] = new codeType[] { codeType.A, codeType.A, codeType.B, codeType.B, codeType.B, codeType.A };
12     cps[4] = new codeType[] { codeType.A, codeType.B, codeType.A, codeType.A, codeType.B, codeType.B };
13 
14     cps[5] = new codeType[] { codeType.A, codeType.B, codeType.B, codeType.A, codeType.A, codeType.B };
15     cps[6] = new codeType[] { codeType.A, codeType.B, codeType.B, codeType.B, codeType.A, codeType.A };
16     cps[7] = new codeType[] { codeType.A, codeType.B, codeType.A, codeType.B, codeType.A, codeType.B };
17     cps[8] = new codeType[] { codeType.A, codeType.B, codeType.A, codeType.B, codeType.B, codeType.A };
18     cps[9] = new codeType[] { codeType.A, codeType.B, codeType.B, codeType.A, codeType.B, codeType.A };
19 
20     int first = int.Parse(code[0].ToString());
21     return cps[first][left];
22 }
23 
24 byte getCodeVal(int val, codeType cp)
25 {
26     byte[,] values ={
27         {Convert.ToByte("0001101", 2),Convert.ToByte("0100111", 2),Convert.ToByte("1110010", 2)},
28         {Convert.ToByte("0011001", 2),Convert.ToByte("0110011", 2),Convert.ToByte("1100110", 2)},
29         {Convert.ToByte("0010011", 2),Convert.ToByte("0011011", 2),Convert.ToByte("1101100", 2)},
30         {Convert.ToByte("0111101", 2),Convert.ToByte("0100001", 2),Convert.ToByte("1000010", 2)},
31         {Convert.ToByte("0100011", 2),Convert.ToByte("0011101", 2),Convert.ToByte("1011100", 2)},
32 
33         {Convert.ToByte("0110001", 2),Convert.ToByte("0111001", 2),Convert.ToByte("1001110", 2)},
34         {Convert.ToByte("0101111", 2),Convert.ToByte("0000101", 2),Convert.ToByte("1010000", 2)},
35         {Convert.ToByte("0111011", 2),Convert.ToByte("0010001", 2),Convert.ToByte("1000100", 2)},
36         {Convert.ToByte("0110111", 2),Convert.ToByte("0001001", 2),Convert.ToByte("1001000", 2)},
37         {Convert.ToByte("0001011", 2),Convert.ToByte("0010111", 2),Convert.ToByte("1110100", 2)}
38     };
39     switch (cp)
40     {
41         case codeType.A:
42             return values[val, 0];
43         case codeType.B:
44             return values[val, 1];
45         case codeType.C:
46             return values[val, 2];
47         default:
48             return 0;
49     }
50 }               

第三步 即将大功告成了 看到上面你也了解了 只要调用getCodeVal函数 就可以逐一获取12个编码的竖条的原始数据 ,虽然每个编码只占7跟竖线 但是为了方便我们依然用一个字节来表示。你要问怎么确定12个编码每个的竖线显示与不显示 这个是位运算 别说俺没告诉你哦。0x40二进制是1000000,和原始字节码数据进行“与”运算即可得到结果 其他的以此类推。
大招祭出:

 1 //按步骤写入单个码,全局变量code 索引 0~11
 2         //索引为0时 是模式匹配,所以只会写入起始符
 3         //索引为6时 左数据结束 会写入中间的分隔符
 4         //索引为11时为最后一个 会写入效验码和结束符
 5         public void step(int codeIndx)
 6         {
 7             Graphics g = Graphics.FromImage(result);
 8             g.TranslateTransform(7 * zoom, 0);
 9             if (codeIndx == 0)
10             {
11                 //导入值字符
12                 g.DrawString(code[codeIndx].ToString(), new Font(FontFamily.GenericSerif, 7f * zoom), cors[1], new PointF(x - 7 * zoom, result.Height * 0.76f));
13                 //起始符
14                 g.FillRectangle(cors[1], x, 0, zoom, result.Height); x += zoom;
15                 g.FillRectangle(cors[0], x, 0, zoom, result.Height); x += zoom;
16                 g.FillRectangle(cors[1], x, 0, zoom, result.Height); x += zoom;
17             }
18             else
19             {
20                 //底端字符
21                 g.DrawString(code[codeIndx].ToString(), new Font(FontFamily.GenericSerif, 7f * zoom), cors[1], new PointF(x, result.Height * 0.76f));
22 
23                 //提取码
24                 byte csh_1 = 0x40, csh_2 = 0x20, csh_3 = 0x10, csh_4 = 0x8, csh_5 = 0x4, csh_6 = 0x2, csh_7 = 0x1;
25 
26                 //数据位
27                 byte coded = getCodeVal(code[codeIndx], codeIndx < 7 ? getCp(codeIndx - 1) : codeType.C);
28                 g.FillRectangle((coded & csh_1) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
29                 g.FillRectangle((coded & csh_2) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
30                 g.FillRectangle((coded & csh_3) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
31                 g.FillRectangle((coded & csh_4) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
32                 g.FillRectangle((coded & csh_5) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
33                 g.FillRectangle((coded & csh_6) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
34                 g.FillRectangle((coded & csh_7) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
35 
36                 if (codeIndx == 11)
37                 {
38                     //效验码
39                     int C1 = code[0] + code[2] + code[4] + code[6] + code[8] + code[10];
40                     int C2 = (code[1] + code[3] + code[5] + code[7] + code[9] + code[11]) * 3;
41                     int CC = C1 + C2;
42                     int C = CC % 10;
43                     C = 10 - C;
44                     if (C == 10)
45                         C = 0;
46                     coded = getCodeVal(C, codeType.C);
47                     //效验字符
48                     g.DrawString(C.ToString(), new Font(FontFamily.GenericSerif, 7f * zoom), cors[1], new PointF(x, result.Height * 0.76f));
49 
50                     g.FillRectangle((coded & csh_1) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
51                     g.FillRectangle((coded & csh_2) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
52                     g.FillRectangle((coded & csh_3) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
53                     g.FillRectangle((coded & csh_4) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
54                     g.FillRectangle((coded & csh_5) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
55                     g.FillRectangle((coded & csh_6) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
56                     g.FillRectangle((coded & csh_7) == 0 ? cors[0] : cors[1], x, 0, zoom, result.Height * 0.76f); x += zoom;
57                 }
58             }
59 
60             if (codeIndx == 6)
61             {
62                 //分隔符
63                 g.FillRectangle(cors[0], x, 0, zoom, result.Height); x += zoom;
64                 g.FillRectangle(cors[1], x, 0, zoom, result.Height); x += zoom;
65                 g.FillRectangle(cors[0], x, 0, zoom, result.Height); x += zoom;
66                 g.FillRectangle(cors[1], x, 0, zoom, result.Height); x += zoom;
67                 g.FillRectangle(cors[0], x, 0, zoom, result.Height); x += zoom;
68             }
69             else if (codeIndx == 11)
70             {
71                 //结束符
72                 g.FillRectangle(cors[1], x, 0, zoom, result.Height); x += zoom;
73                 g.FillRectangle(cors[0], x, 0, zoom, result.Height); x += zoom;
74                 g.FillRectangle(cors[1], x, 0, zoom, result.Height); x += zoom;
75             }
76 
77         }
1 static void Main(string[] args)
2 {
3     Bar b = new Bar();
4     //694492600001
5     //692645690038
6     //692173496230
7     //669859324875
8     b.generate("692173496230").Save("a.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
9 }

找个代表性的 :


kao 又是这 - -! 就不能换点别的。
运行代码把它打印出来 打印机无要求 办公室普通的激光打印机即可,只要不是太破 。像那种碳粉质量又差 感光鼓又快不行了 打印出来的图片一片片的白道道 你还是趁早换掉吧 用着都伤心。见证奇鸡的时刻到了 ,是不是很鸡冻 。哥们儿掏出你的手机 来“扫一扫”吧。俺也忍不住来试一下,牛皮不是吹的 火车不是推的 看下哥这码能读不?
拍照的手机像素有点低哈 见谅,上图:

话说本帖上面的条码是啥东东 ?你知道了么?

后记

ean-13虽然是一种商品条形码 你也完全可以把它当成文档编号 或者应用到其他领域,如果是其他领域建议还是不要用ean-13 因为有专门用于 档案ID 或者物流ID 领域的codabar码 异曲同工 我就不多讲了。可以看到条形码 是一种完全开放的编码方式。也完全不是一种防伪码 更不带加密功能  因为数据容量少所以功能也有限 所以必须得借助数据库。想要那些的话切搞带芯片存储和算法加密的CPU卡吧。传统的银行卡称之为磁卡,说白了就是以“磁”的方式记录数字ID,并没有任何保护措施。所以要复制银行卡也不是什么太高科技的。你要想取出来钱儿 还是得要密码的 嘿嘿。
虽然功能有限 但是 但是丝毫不影响他。条码是一种最方便 最廉价的解决 方案 ,尤其在物流 医疗等各种信息化系统领域 的应用无处不在,随便看下你身边你就会发现条形码。我身边就有一个: 网站信息备案申请单 左上角就有条形码。作为文档的电子ID印在左上角 方便调阅 管理。
这就是科技的力量 ,世界上每天都会进行条码扫描达10亿次,他为我们生活提供了诸多方便 。

闲扯点别的
虽然啥都没学过从来没写半句代码 ,对我来说要弄android平台的程序也不是啥难事 只是懒得去整。
现如今这个所谓的“移动平台”铺天盖地 大有势不可挡,当然也不乏催生了很多优秀的应用 优秀的开发者。
当然也有好多乱七八糟的广告 垃圾。有时候呢也没有必要太跟风 你说呢。顺其自然吧。
这次就不给源码文件了 哈 都贴在页面上了,如果你真的需要 ctr+c ctr+v都懒得按一下的话 也太伤我自尊了吧

posted @ 2013-11-29 11:44  assassinx  阅读(4072)  评论(4编辑  收藏  举报