QRcode
众所周知,大家使用的微信付款收款都在用二维码扫描去完成。近十来年,二维码技术已不再是“高科技”,各种教你制作二维码的教程和代码一篇又一篇。今天!小二把原理,代码一次性写出来,也希望各位能够理解。
- 我们微信一般使用的就是QRcode,这是一个矩阵式的二维码。是由小日…日本——DENSO开发出来的可以通过高速读取(高速读取就是quick response ,也就是QR的简称)并能够存7089个数字或4296个字母和数字的混合字符或2953个8位字节数据或1817个汉字。
- 支持纠错处理:L级:最大7%;M级:15%;Q级:25%;H级:30%。(即使部分编码变脏或破损 也可以恢复数据 最多可纠错越30%)
- 结构化:寻像图形和定位图形帮助解码程序定位具体符号的坐标。
- 扩展能力:可组合多个QR码亦可拆分。(可将数据分割成多个编码,最多支持16个QR码)
模式
QR图的大小(size)定义为版本(Version),版本号从1-40;→ 版本1——2121矩阵;版本40——177177矩阵。(规格一定,纠错等级越高,容量越小)(每个版本比前一个版本增加4个码元,计算公式为( n-1)*4+21,每个码元存储一个二进制0或者1)
编码内容
- 数字:每三个为一组压缩成10bit.
- 字母数字混合:每两个为一组,压缩成11bit.
- 8bit字节数据:无压缩直接保存.
- 多字节字符:每个字符被压缩成13bit.
先来一张图看看
比较经典的图,我们一个二维码中包含这些内容,而位置探测图形、位置探测图形分隔符、定位图形,校正图形是用于对二维码的定位。
位置探测图形用于标记矩形大小,3个图形确定一个矩形。
定位符是因为二维码有40个版本尺寸,当尺寸过大后需要有根标准线,不然扫描的时候可能会扫歪。
QRcode计算:
模式标识符
- 数字模式:0001
- 混合字符模式:0010
- 8bit:0100
- 日本汉字:1000
- 中国汉字:1101
- ECI:0111
- 结构链接:0011
- FNCI:0101(第一位置)1001(第二位置)
- 终止符(信息结尾):0000
对于日本汉字与中文编码
1.日本汉字(KANJI)是两个字节表示的字符码,编码方式——将其转换为13字节的二进制码。
步骤:
1.对于JIS值为8140(hex)到9FFC(hex)之间字符:
(1)将待转换的JIS值减去8140(hex);
(2)将高位字节*C0(hex);
(3)将(2)生成的数据加上低位字节;
(4)将结果转换为13位二进制串;
2.对于JIS值位E040(hex)到EBBF(hex)之间的字符:
(1)将待转换的JIS值减去C140(hex);
(2)将高位字节*C0(hex);
(3)将(2)生成的数据加上低位字节;
(4)将结果转换为13位二进制串;
2.中文汉字:
1.对于第一字节为0xA1~0xAA之间,第二字节在0xA1~0xFE之间字符:
(1)第一字节减去0xA1
(2)上一步结果*0x60
(3)第二字节减去0xA1
(4)将(2)结果加上(3)的结果
(5)将结果转换为13位二进制串
2.对于第一字节为0xB0~0xFA之间,第二字节在0xA1~0xFE之间字符:
(1)第一字节减去0xA6
(2)上一步结果*0x60
(3)第二字节减去0xA1
(4)将(2)步骤的结果加上(3)步骤的结果
(5)将结果转换为13位二进制串
废话不多说,直接看二维码生成步骤
-
信息按照一定的编码规则后变成二进制,通过黑白色形成矩形。
-
根据version和纠错级别(纠错码的数量)编码生成一个二进制序列,序列包含
- 编码类型的二进制(数字类型,字符类型有特定的编码)
- 编码内容的长度的二进制
- 编码内容的二进制
- 结束符(4个0)
-
将序列按8bits为一组重排,如果所有的编码加起来不是8个倍数,还要在后面加上足够的0。
-
补码。如果序列还没有达到最大的bits数的限制,还要加一些补齐码(Padding Bytes),Padding Bytes就是重复下面的两个bytes:11101100 00010001。每种版本的bits的位数是不同的。
-
生成纠错码。可以查看文档的第30页到44页的Table-13到Table-22的定义表,可以知道生成纠错码的过程。
-
穿插放置。把数据码和纠错码的各个8位一组的十进制数(codewords)交替放在一起。交替规则如下:
对于数据码:把每个块的第一个codewords先拿出来按顺度排列好,然后再取第一块的第二个,如此类推。
对于纠错码:规则也是一样。
然后按数据码在前纠错码在后合并起来。 -
加上Reminder Bits,对于某些Version的QR,上面的还不够长度,还要加上Remainder Bits,比如:5Q版的二维码,还要加上7个bits,Remainder Bits加零就好了。关于哪些Version需要多少个Remainder bit,可以参看文档的第15页的Table-1的定义表。
-
按照一定规则进行掩码,就是进行异或,分散数据,进行画图。
代码:
大家可以试试网站链接和字符串啥的,改一改纠错级别和模式,会让你生成的二维码不一样喔!
生成二维码:
package qrcode;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import com.swetake.util.Qrcode;
/**
* 二维码生成器
*/
public class Encode { //总对象
/**
* 生成二维码(QRCode)图片
* @param content
* @param imgPath
*/
public void encoderQRCode(String content,String imgPath) { //编码qrcode的content值和图片imgPath路径值
try {
Qrcode qrcodeHandler = new Qrcode(); //创建一个新Qrcode类
qrcodeHandler.setQrcodeErrorCorrect('M');
//设置纠错级别
qrcodeHandler.setQrcodeEncodeMode('B');
//设置模式
qrcodeHandler.setQrcodeVersion(3);
//设置版本信息
System.out.println(content);
//输出content信息
byte[] contentBytes = content.getBytes("gb2312");
//设置字节版本信息
BufferedImage bufImg = new BufferedImage(140, 140,
BufferedImage.TYPE_INT_RGB);
//创建图片buffer;设置图片大小及使用颜色方法;
Graphics2D gs = bufImg.createGraphics(); //制作二维平面图像
gs.setBackground(Color.WHITE);
gs.clearRect(0, 0, 140, 140);
// 设定图像颜色> BLACK
gs.setColor(Color.BLACK);
// 设置偏移量 不设置可能导致解析出错
int pixoff = 2;
// 输出内容> 二维码
if (contentBytes.length > 0 && contentBytes.length < 120) {
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 {
System.err.println("QRCode content bytes length = "
+ contentBytes.length + " not in [ 0,120 ]. ");
}
gs.dispose(); //画出gs
bufImg.flush(); //做成bufImg
File imgFile = new File(imgPath);
// 生成二维码QRCode图片
ImageIO.write(bufImg, "png", imgFile);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String imgPath = "E:/qrcode_pics/Michael_QRCode.png";
String content = "http://www.baidu.com";
Encode handler = new Encode();
handler.encoderQRCode(content, imgPath);
System.out.println("encoder QRcode success");
}
}
解码二维码:
package qrcode;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import jp.sourceforge.qrcode.QRCodeDecoder;
import jp.sourceforge.qrcode.data.QRCodeImage;
import jp.sourceforge.qrcode.exception.DecodingFailedException;
public class Decode {
/**
* 解码二维码
* @param imgPath
* @return String
*/
public String decoderQRCode(String imgPath) {
// QRCode 二维码图片的文件
File imageFile = new File(imgPath);
BufferedImage bufImg = null;
String decodedData = null;
try {
bufImg = ImageIO.read(imageFile);
QRCodeDecoder decoder = new QRCodeDecoder();
decodedData = new String(decoder.decode(new J2SEImage(bufImg)));
} catch (IOException e) {
System.out.println("Error: " + e.getMessage());
e.printStackTrace();
} catch (DecodingFailedException dfe) {
System.out.println("Error: " + dfe.getMessage());
dfe.printStackTrace();
}
return decodedData;
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
Decode handler = new Decode();
String imgPath = "E:/qrcode_pics/Michael_QRCode.png";
String decoderContent = handler.decoderQRCode(imgPath);
System.out.println("解析结果如下:");
System.out.println(decoderContent);
System.out.println("========decoder success!!!");
}
class J2SEImage implements QRCodeImage {
BufferedImage bufImg;
public J2SEImage(BufferedImage bufImg) {
this.bufImg = bufImg;
}
public int getWidth() {
return bufImg.getWidth();
}
public int getHeight() {
return bufImg.getHeight();
}
public int getPixel(int x, int y) {
return bufImg.getRGB(x, y);
}
}
}
记得,一定要先下载一个QRCode.jar
我会在我的资源文件中分享一个完整的代码demo可恨这csdn不能直接在页面保存文件!!
来点QRcode的赞美:
超高速
从QR Code码的英文名称Quick Response Code可以看出,超高速识读特点是QR Code码区别于四一七条码、Data Matrix等二维码的主要特性。由于在用CCD识读QR Code码时,整个QR Code码符号中信息的读取是通过QR Code码符号的位置探测图形,用硬件来实现,因此,信息识读过程所需时间很短,它具有超高速识读特点。用CCD二维条码识读设备,每秒可识读30个含有100个字符的QR Code码符号;对于含有相同数据信息的四一七条码符号,每秒仅能识读3个符号;对于Data Martix矩阵码,每秒仅能识读2~3个符号。QR Code码的超高速识读特性使它能够广泛应用于工业自动化生产线管理等领域。
全方位
QR Code码具有全方位(360°)识读特点,这是QR Code码优于行排式二维条码如四一七条码的另一主要特点,由于四一七条码是将一维条码符号在行排高度上的截短来实现的,因此,,它很难实现全方位识读,其识读方位角仅为±10°。
能够有效地表示汉字
由于QR Code码用特定的数据压缩模式表示汉字,它仅用13bit可表示一个汉字,而四一七条码、Data Martix等二维码没有特定的汉字表示模式,因此仅用字节表示模式来表示汉字,在用字节模式表示汉字时,需用16bit(二个字节)表示一个汉字,因此QR Code码比其它的二维条码表示汉字的效率提高了20%。