QR码生成原理(一)

一、什么是QR码

QR码属于矩阵式二维码中的一个种类,由DENSO(日本电装)公司开发,由JIS和ISO将其标准化。QR码的样子其实在很多场合已经能够被看到了,我这还是贴个图展示一下:

这个图如果被正确解码,应该看到我的名字和邮箱。

二、QR码的特点

说到QR码的特点,一是高速读取(QR就是取自“Quick Response”的首字母),对读取速度的体验源自于我手机上的一个软件,象上面贴出的码图,通过摄像头从拍摄到解码到显示内容也就三秒左右,对摄像的角度也没有什么要求;

二是高容量、高密度;理论上内容经过压缩处理后可以存7089个数字,4296个字母和数字混合字符,2953个8位字节数据,1817个汉字;

三是支持纠错处理;纠错处理相对复杂,目前我还没有深入了解,按照QR码的标准文档说明,QR码的纠错分为4个级别,分别是:

·         level L : 最大 7%的错误能够被纠正;

·         level M : 最大 15%的错误能够被纠正;

·         level Q : 最大 25%的错误能够被纠正;

·         level H : 最大 30%的错误能够被纠正;

四是结构化;看似无规则的图形,其实对区域有严格的定义,下图就是一个模式2、版本1的QR图结构(关于QR码的"模式"、"版本"将在后面进行介绍):

在上图21*21的矩阵中,黑白的区域在QR码规范中被指定为固定的位置,称为寻像图形(finder pattern)和定位图形(timing pattern)。寻像图形和定位图形用来帮助解码程序确定图形中具体符号的坐标。

黄色的区域用来保存被编码的数据内容以及纠错信息码。

蓝色的区域,用来标识纠错的级别(也就是Level L到Level H)和所谓的"Mask pattern",这个区域被称为“格式化信息”(format information)。

五是扩展能力。QR码的Structure Append特点,使一个QR码可以分解成多个QR码,反之,也可以将多个QR码的数据组合到一个QR码中来。

三、QR码的模式和版本

前面提到过QR码的模式(Model)和版本(Version)。QR码分为Model1和Model2两种模式,Model1是对QR的初始定义,Model2是对Model1的扩展,目前使用较为普遍的是Model2,本文的所有说明也仅用于Model2。

QR图的大小(size)被定义为版本(Version),版本号从1到40。版本1就是一个21*21的矩阵,每增加一个版本号,矩阵的大小就增加4个模块(Module),因此,版本40就是一个177*177的矩阵。(版本越高,意味着存储的内容越多,纠错能力也越强)。

三、QR码支持的编码内容

QR码支持编码的内容包括纯数字、数字和字符混合编码、8位字节码和包含汉字在内的多字节字符。其中:

数字:每三个为一组压缩成10bit。

字母数字混合:每两个为一组,压缩成11bit。

8bit字节数据:无压缩直接保存。

多字节字符:每一个字符被压缩成13bit。

QR码编码原理二(编码)

编码就是把常见的数字、字符等转换成QR码的方法。说具体的编码之前,先说一下QR码的最大容量问题。

一、最大容量

QR码的最大容量取决于选择的版本、纠错级别和编码模式(Mode:数字、字符、多字节字符等)。以版本1、纠错级别为Level Q的QR码为例,可以存储27个纯数字,或17个字母数字混合字符或11个8bit字节数据。如果要存储同样多的内容同时提高纠错级别,则需要采用更高的版本。版本1~9数据容量、纠错码容量对照如下表:

(version)

(error correcting level)

(count of data code words)

count of EC code words

(numeric)

(alphanumeric)

8bit

1

L

19

7

41

25

17

M

16

10

34

20

14

Q

13

13

27

16

11

H

9

17

17

10

7

2

L

34

10

77

47

32

M

28

16

63

38

26

Q

22

22

48

29

20

H

16

28

34

20

14

3

L

55

15

127

77

53

M

44

26

101

61

42

Q

34

36

77

47

32

H

26

44

58

35

24

4

L

80

20

187

114

78

M

64

36

149

90

62

Q

48

52

111

67

46

H

36

64

82

50

34

5

L

108

26

255

154

106

M

86

48

202

122

84

Q

62

72

144

87

60

H

46

88

106

64

44

6

L

136

36

322

195

134

M

108

64

255

154

106

Q

76

96

175

108

74

H

60

112

139

84

58

7

L

156

40

370

224

154

M

124

72

293

178

122

Q

88

108

207

125

86

H

66

130

154

93

64

8

L

194

48

461

279

192

M

154

88

365

221

152

Q

110

132

259

157

108

H

86

156

202

122

84

9

L

232

60

552

335

230

M

182

110

432

262

180

Q

132

160

312

189

130

H

100

192

235

143

98

如果要了解更详细的QR码容量信息,可以到电装的网站去看看http://www.denso-wave.com/qrcode/vertable1-e.html

下面,就举例说明将“ABCDE123”转换成为版本1、Level H的QR码转换方法。

二、模式标识符(Mode Indicator)

QR码的模式(Mode)就是前文提到的数字、字符、8bit字节码、多字节码等。对于不同的模式,都有对应的模式标识符(Mode Indicator)来帮助解码程序进行匹配,模式标识符是4bit的二进制数:

1、数字模式(numeric mode): 0001
2、混合字符模式(alphanumeric mode) : 0010
3、8bit byte mode: 0100
4、日本汉字(KANJI mode) : 1000

5、中国汉字(GB2312):1101

由于示例文本串是混合字符,因此将选择alphanumeric mode,其标识码为:0010

三、文本串计数标识符(Character count indicator)

文本串计数标识符用来存储源内容字符串的长度,在版本1-9的QR码中,文本串长度标识符自身的长度被定义为:
数字 : 10bit
混合字符 : 9bit
8bit 字节码 : 8bit
多字节码 : 8bit

在本例中,源文本串的长度为8个字符,混合字符的长度为9bit,因此将字符个数8编码为9位二进制表示:000001000

加上混合字符模式标识码,总的编码为0010 000001000

四、数据内容编码

1、数字模式下的编码

在数字模式下,数据被限制为3个数字一段,分成若干段。如:"123456"将分成"123"和 "456",分别被编码成10bit的二进制数。“123”的10bit二进制表示法为:0001111011,实际上就是二进制的123。

当数据的长度不足3个数字时,如果只有1个数字则用4bit,如果有2个数字就用7个bit来表示。
如:"9876"被分成"987"和"6"两段,因此被表示为"1111011011 0110"。

2、混合字符模式下的编码

混合字符模式编码,其字符对照表如下:

0

0

A

10

K

20

U

30

+

40

1

1

B

11

L

21

V

31

-

41

2

2

C

12

M

22

W

32

.

42

3

3

D

13

N

23

X

33

/

43

4

4

E

14

O

24

Y

34

:

44

5

5

F

15

P

25

Z

35

6

6

G

16

Q

26

[sp]

36

7

7

H

17

R

27

$

37

8

8

I

18

S

28

%

38

9

9

J

19

T

29

*

3

编码方式为:

源码被分成两个字符一段,如下所示,每段的第一个字符乘上45,再用第二个数字相加。因此每段变成了11bit的2进制码,如果字符个数只有1个,则用6bit表示。

示例:

"AB"

"CD"

"E1"

"23"

45*10+11

45*12+13

45*14+1

45*2+3

461

553

631

93

0010

000001000

00111001101

01000101001

01001110111

00001011101

3、8bit字节数据不经编码转换直接保存。

五、编码终止符(Terminator)

如果编码后的字符长度不足当前版本和纠错级别所存储的容量,则在后续补"0000",如果容量已满则无需添加终止符。此时得到的编码串为:

0010 000001000 00111001101 01000101001 01001110111 000010111010000
六、编成8bit码字(Code words)

将以上的编码再按8bit一组,形成码字(code words):

00100000 01000001 11001101 01000101 00101001 11011100 00101110 10000
如果尾部数据不足8bit,则在尾部充0:

00100000 01000001 11001101 01000101 00101001 11011100 00101110 10000000
如果编码后的数据不足版本及纠错级别的最大容量,则在尾部补充 "11101100"和 "00010001",直到全部填满。最后,版本1、Level H下的"ABCDE123"的QR码是:

00100000 01000001 11001101 01000101 00101001 11011100 00101110 1000000011101100

十进制表示法为:

32 65 205 69 41 220 46 128 236

QR码编码原理三(日本汉字和中文编码)

一、日本汉字(KANJI)是两个字节表示的字符码,编码的方式是将其转换为13字节的二进制码制。

转换步骤为:

1、对于JIS值为8140(hex)到9FFC(hex)之间字符:

a)将待转换的JIS值减去8140(hex);

b)将高位字节乘以C0(hex);

c)将b)步骤生成的数据加上低位字节;

d)将结果转换为13位二进制串。

2、对于JIS值为E040(hex)到EBBF(hex)之间的字符:

a)将待转换的JIS值减去C140(hex);

b)将高位字节乘以C0(hex);

c)将b)步骤生成的数据加上低位字节;

d)将结果转换为13位二进制串。

二、中文汉字的与日文汉字转换步骤相似:

1、对于第一字节为0xA1~0xAA之间,第二字节在0xA1~0xFE之间字符:

a)第一字节减去0xA1;

b)上一步结果乘以0x60;

c)第二字节减去0xA1;

d)将b)步骤的结果加上c步骤的结果;

e)将结果转换为13位二进制串。

1、对于第一字节为0xB0~0xFA之间,第二字节在0xA1~0xFE之间字符:

a)第一字节减去0xA6;

b)上一步结果乘以0x60;

c)第二字节减去0xA1;

d)将b)步骤的结果加上c步骤的结果;

e)将结果转换为13位二进制串。

二维码,是一种采用黑白相间的平面几何图形通过相应的编码算法来记录文字、图片、网址等信息的条码图片。如下图

二维码的特点:

1.  高密度编码,信息容量大

可容纳多达1850个大写字母或2710个数字或1108个字节,或500多个汉字,比普通条码信息容量约高几十倍。

2.  编码范围广

该条码可以把图片、声音、文字、签字、指纹等可以数字化的信息进行编码,用条码表示出来;可以表示多种语言文字;可表示图像数据。

3.  容错能力强,具有纠错功能

这使得二维条码因穿孔、污损等引起局部损坏时,照样可以正确得到识读,损毁面积达50%仍可恢复信息。

4.  译码可靠性高

它比普通条码译码错误率百万分之二要低得多,误码率不超过千万分之一。

5.  可引入加密措施

保密性、防伪性好。

6.  成本低,易制作,持久耐用

正因为以上这些特点,二维码现在越来越流行,应用也是越来越广(详细了解请见百度百科,介绍不是本篇重点),所以掌握如何开发二维码是非常不错的知识储备,因此本篇博文将为大家讲解如何生成、解析二维码。

一、Java

所需jar包:QRCode.jar

http://download.csdn.net/detail/songylwq/5115107

TwoDimensionCode类:二维码操作核心类

package qrcode;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.imageio.ImageIO;
import jp.sourceforge.qrcode.QRCodeDecoder;
import jp.sourceforge.qrcode.exception.DecodingFailedException;
import com.swetake.util.Qrcode;
public class TwoDimensionCode {
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param imgPath 图片路径
*/
public void encoderQRCode(String content, String imgPath) {
this.encoderQRCode(content, imgPath, "png", 7);
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param output 输出流
*/
public void encoderQRCode(String content, OutputStream output) {
this.encoderQRCode(content, output, "png", 7);
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param imgPath 图片路径
* @param imgType 图片类型
*/
public void encoderQRCode(String content, String imgPath, String imgType) {
this.encoderQRCode(content, imgPath, imgType, 7);
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param output 输出流
* @param imgType 图片类型
*/
public void encoderQRCode(String content, OutputStream output, String imgType) {
this.encoderQRCode(content, output, imgType, 7);
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param imgPath 图片路径
* @param imgType 图片类型
* @param size 二维码尺寸
*/
public void encoderQRCode(String content, String imgPath, String imgType, int size) {
try {
BufferedImage bufImg = this.qRCodeCommon(content, imgType, size);
File imgFile = new File(imgPath);
// 生成二维码QRCode图片
ImageIO.write(bufImg, imgType, imgFile);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成二维码(QRCode)图片
* @param content 存储内容
* @param output 输出流
* @param imgType 图片类型
* @param size 二维码尺寸
*/
public void encoderQRCode(String content, OutputStream output, String imgType, int size) {
try {
BufferedImage bufImg = this.qRCodeCommon(content, imgType, size);
// 生成二维码QRCode图片
ImageIO.write(bufImg, imgType, output);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 生成二维码(QRCode)图片的公共方法
* @param content 存储内容
* @param imgType 图片类型
* @param size 二维码尺寸
* @return
*/
private BufferedImage qRCodeCommon(String content, String imgType, int size) {
BufferedImage bufImg = null;
try {
Qrcode qrcodeHandler = new Qrcode();
// 设置二维码排错率,可选L(7%)、M(15%)、Q(25%)、H(30%),排错率越高可存储的信息越少,但对二维码清晰度的要求越小
qrcodeHandler.setQrcodeErrorCorrect('M');
qrcodeHandler.setQrcodeEncodeMode('B');
// 设置设置二维码尺寸,取值范围1-40,值越大尺寸越大,可存储的信息越大
qrcodeHandler.setQrcodeVersion(size);
// 获得内容的字节数组,设置编码格式
byte[] contentBytes = content.getBytes("utf-8");
// 图片尺寸
int imgSize = 67 + 12 * (size - 1);
bufImg = new BufferedImage(imgSize, imgSize, BufferedImage.TYPE_INT_RGB);
Graphics2D gs = bufImg.createGraphics();
// 设置背景颜色
gs.setBackground(Color.WHITE);
gs.clearRect(0, 0, imgSize, imgSize);
// 设定图像颜色> BLACK
gs.setColor(Color.BLACK);
// 设置偏移量,不设置可能导致解析出错
int pixoff = 2;
// 输出内容> 二维码
if (contentBytes.length > 0 && contentBytes.length < 800) {
boolean[][] codeOut = qrcodeHandler.calQrcode(contentBytes);
for (int i = 0; i < codeOut.length; i++) {
for (int j = 0; j < codeOut.length; j++) {
if (codeOut[j][i]) {
gs.fillRect(j * 3 + pixoff, i * 3 + pixoff, 3, 3);
}
}
}
} else {
throw new Exception("QRCode content bytes length = " + contentBytes.length + " not in [0, 800].");
}
gs.dispose();
bufImg.flush();
} catch (Exception e) {
e.printStackTrace();
}
return bufImg;
}
/**
* 解析二维码(QRCode)
* @param imgPath 图片路径
* @return
*/
public String decoderQRCode(String imgPath) {
// QRCode 二维码图片的文件
File imageFile = new File(imgPath);
BufferedImage bufImg = null;
String content = null;
try {
bufImg = ImageIO.read(imageFile);
QRCodeDecoder decoder = new QRCodeDecoder();
content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
} catch (DecodingFailedException dfe) {
System.out.println("Error: " + dfe.getMessage());
dfe.printStackTrace();
}
return content;
}
/**
* 解析二维码(QRCode)
* @param input 输入流
* @return
*/
public String decoderQRCode(InputStream input) {
BufferedImage bufImg = null;
String content = null;
try {
bufImg = ImageIO.read(input);
QRCodeDecoder decoder = new QRCodeDecoder();
content = new String(decoder.decode(new TwoDimensionCodeImage(bufImg)), "utf-8");
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
} catch (DecodingFailedException dfe) {
System.out.println("Error: " + dfe.getMessage());
dfe.printStackTrace();
}
return content;
}
public static void main(String[] args) {
String imgPath = "G:/TDDOWNLOAD/Michael_QRCode.png";
String encoderContent = "Hello 大大、小小,welcome to QRCode!" + "\nMyblog [ http://sjsky.iteye.com ]" + "\nEMail [ sjsky007@gmail.com ]";
TwoDimensionCode handler = new TwoDimensionCode();
handler.encoderQRCode(encoderContent, imgPath, "png");
//      try {
//          OutputStream output = new FileOutputStream(imgPath);
//          handler.encoderQRCode(content, output);
//      } catch (Exception e) {
//          e.printStackTrace();
//      }
System.out.println("========encoder success");
String decoderContent = handler.decoderQRCode(imgPath);
System.out.println("解析结果如下:");
System.out.println(decoderContent);
System.out.println("========decoder success!!!");
}
}

TwoDimensionCodeImage 类:二维码图片对象

package qrcode;
import java.awt.image.BufferedImage;
import jp.sourceforge.qrcode.data.QRCodeImage;
public class TwoDimensionCodeImage implements QRCodeImage {
BufferedImage bufImg;
public TwoDimensionCodeImage(BufferedImage bufImg) {
this.bufImg = bufImg;
}
@Override
public int getHeight() {
return bufImg.getHeight();
}
@Override
public int getPixel(int x, int y) {
return bufImg.getRGB(x, y);
}
@Override
public int getWidth() {
return bufImg.getWidth();
}
}

二、.NET

所需dll:ThoughtWorks.QRCode

http://download.csdn.net/detail/songylwq/5115596

这里代码思路跟上述java大同小异,这里就不给出源码了,可参见http://download.csdn.net/detail/songylwq/5115107

上面的java代码,笔者已经进行了简单的封装,方便大家适用不同场合,希望对大家能有所帮助。

java 二维码原理以及用java实现的二维码的生成、解码相关推荐

  1. Java反射的底层原理,以及Java反射的性能分析及优化

    java的反射技术,号称是编程界的九阳神功,也可以说是框架的灵魂.也正是这种反射机制使静态语言的java具备了动态语言的某些特质.就是有了反射,才让java动态,编程的时候更加灵活,能够动态获取信息以 ...

  2. java二维码原理以及用java实现的二维码的生成、解码(转)

    http://blog.csdn.net/songylwq/article/details/8643948 http://sjsky.iteye.com/blog/1136934 http://bbs ...

  3. java 字节码增强原理_深入浅出Java探针技术1--基于java agent的字节码增强案例

    Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...

  4. java中注解动态传参_Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性)...

    Java自定义注解源码+原理解释(使用Java自定义注解校验bean传入参数合法性)java 前言:因为前段时间忙于写接口,在接口中须要作不少的参数校验,本着简洁.高效的原则,便写了这个小工具供本身使 ...

  5. java探针 字节码增强_深入浅出Java探针技术1--基于java agent的字节码增强案例

    Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...

  6. java原理教程,java基础之运行原理(一),java基础运行原理

    java基础之运行原理(一),java基础运行原理 java的核心配置:JDK JDK主要包括三个部分 1.Jre:java的运行环境 2.Java的工具:java的编译器(java.c.exe). ...

  7. 学习:二维码、QR码、J4L-QRCode、java

    转自:http://baijinshan.iteye.com/blog/1004554 学习:二维码.QR码.J4L-QRCode.java Java企业应用网络应用Rails应用服务器  开源码 J ...

  8. 0基础也能看懂的二维码生成器 API 的技术原理(附Java 接入代码)

    写在前面 二维码生成器 API 是一种将文本或 URL 转换为二维码图像的技术.在现代生活中,二维码已经成为了很多人生活中不可或缺的一部分,因为它们可以快速识别.易于存储,并具有可靠性.但是,大多数人 ...

  9. java生成二维码到文件,java生成二维码转成BASE64

    java生成二维码到文件,java生成二维码转成BASE64 如题,利用java和第三方库,把指定的字符串生成二维码,并且把二维码保存成图片,转换成BASE64格式. 需要的jar文件: packag ...

最新文章

  1. steam自建服务器游戏_虽有差评销量却还是直步青云,《Atlas》力登Steam榜单前茅...
  2. CSDN2008最有价值博客获奖感言--放飞梦想,让我们扬帆远航
  3. java bitmap获取图片大小_android 通过uri获取bitmap图片并压缩
  4. Java黑皮书课后题第6章:6.12(显示字符)使用下面的方法头,编写一个打印字符的方法。编写一个测试程序、打印从‘1‘到‘Z‘的字符,每行打印10个,字符之间使用一个空格字符隔开
  5. vue+sentry 前端异常日志监控
  6. VC6和VS2005(及2010)的一些区别总结
  7. 架构之每天数十亿次请求的web应用经验分享
  8. RMAN深入解析之--内存中的RMAN
  9. MySql 中文乱码问题
  10. JMeter Ramp-up 说明
  11. 零基础学习软件测试必看的python之基础语法
  12. Flutter笔记(9)flutter中baseline基准线布局
  13. 简单三步下载网页微博中的视频
  14. springboot微信点餐系统项目设计
  15. Mac下搭建Texstudio+Textlive(Mactex)
  16. 04. Redis 环境搭建-单实例
  17. 超外差,固定码,破解
  18. SRPG游戏开发(二十二)第六章 基本框架 - 七 视图(ViewUI)
  19. java虚拟机线程调优与底层原理分析_啃碎并发(七):深入分析Synchronized原理...
  20. SpringBoot(学习笔记)

热门文章

  1. 极简生活牙膏,让你的口腔清新一整天!
  2. 【Web技术】1159- 浅析 Web 录屏技术方案与实现
  3. pywifi连接中文wifi名称(乱码)连接不上问题解决方案
  4. iTween_itween可视化编辑器(1)
  5. kettle carte服务配置
  6. 微信小程序之本地网络服务器配置
  7. 学日语小技巧 让Office Word效劳
  8. MailConnectException: Couldn‘t connect to host
  9. 【无标题】灵遁者沉思:每个人都有“第三只眼睛”
  10. 人工智能工程师一般需要学什么?