Java-二维码生成与识别(二)

本文介绍了一种处理包含ASCII码和汉字的字符串生成二维码的方法,并提供了两种生成方案及二维码识别过程。通过补码和添加特殊标识区分不同字符类型,确保了二维码的正确生成与解析。

给定一个既含有ASCII码又含有汉字的字符串,如“abcd今天天气不错”,如何生成其二维码并将获得的二维码识别成该字符串呢?要解决这个问题,首先我们需明白ASCII码和汉字的区别。ASCII码字节数为1,且其对应的二进码字符串首字符都是0,而汉字字节数为2,其对应的二进码字符串首字符可为0也可为1。所以为了区别汉字和ASCII码,我们可以在汉字第一个字节前添加一个由二进码1开头的字节。

方法一:

(一)二维码生成:

思路:获取给定字符串中的每个字符,并将其转为二进制码字符串。根据二进制码字符串的长度来判断字符是汉字还是ASCII码,并对其进行相应的补码操作。若是ASCII码,则补完码后需在其后加两个字节,以保持二维码图形的美观。若是汉字,则补完码后需在第一个字节前加一个由二进制码1开头的字节,以区分汉字和ASCII码。添加相应的字节后,将获得的二进制码字符串保存到数组中。之后对数组中的每个二进制码字符串进行字符遍历,若是‘0’,则设置画笔颜色为白色,若是‘1’,则设置画笔颜色为黑色。最后绘制图片。

1.获取给定字符串中的每个字符,并将其转为二进制码字符串。

String str="abcd今天天气不错";
for(int i=0;i<str.length();i++) {
			char c=str.charAt(i);//遍历字符串中的每个字符  
			binarystr=Integer.toBinaryString(c);//将获得的字符转为二进制码字符串
}

2.判断字符是汉字还是ASCII码,并对其进行相应的补码操作和添加字节操作。

if(binarystr.length()<=8) {//ASCII码
	while(binarystr.length()<8) {//不足8位的在前补0
		binarystr="0"+binarystr;
	}
	binarystr=binarystr+"1001010011010011";//补完码后 凑齐3个字节 保持图形的美观
}
else{//汉字
	while(binarystr.length()<16) {//不足16位在前补0
		binarystr="0"+binarystr;
	}
	binarystr="10010100"+binarystr;//在前补一个字节 该字节首个二进制码为1  用来区分是汉字还是ASCII码
}

3.对数组中的每个二进制码字符串进行字符遍历,设置画笔颜色,绘制图形。

for(int i=0;i<tobinarystr.length;i++) {
	String str=tobinarystr[i];//获取二进制码字符串数组中的每个二进码字符串
	for(int j=0;j<str.length();j++) {
		char c=str.charAt(j);//获取二进制码字符串中的每个字符
        //根据获得的字符进行判断 设置画笔颜色
		if(c=='0') {
			g.setColor(Color.WHITE);
		}else {
			g.setColor(Color.BLACK);
		}
		//绘制图片
		g.fillRect(100+i*10, 100+j*10, 10, 10);
	}
}

4.完整代码

QRcodeunion1类:将给定字符串中的每个字符转换为二进制码字符串,并创建保存二进制码字符串的数组。

public class QRcodeunion1 {
	String str="abcd今天天气不错";
	//声明一个字符串数组,用来保存将字符转为二进制码的字符串
	String[] binarystring=new String[str.length()];
	//创建方法 将字符串中每个字符(ASCII码或汉字)转为二进制码字符串 返回字符串数组
	public String[] tobinarystring() {
		String binarystr;
		for(int i=0;i<str.length();i++) {
			char c=str.charAt(i);//遍历字符串中的每个字符  
			binarystr=Integer.toBinaryString(c);//将获得的字符转为二进制码字符串
			//判断是汉字还是ASCII码
			if(binarystr.length()<=8) {//ASCII码
				while(binarystr.length()<8) {//不足8位的在前补0
					binarystr="0"+binarystr;
				}
				binarystr=binarystr+"1001010011010011";//凑齐3个字节 保持图形的美观
			}
			else{//汉字
				while(binarystr.length()<16) {//不足16位在前补0
					binarystr="0"+binarystr;
				}
				binarystr="10010100"+binarystr;//在前补一个字节 该字节前一位为1  用来区分是汉字还是ASCII码
			}
			System.out.println(binarystr);
			binarystring[i]=binarystr;
			
		}	
	return binarystring;
	}
}

QRcodeunionUI1类:创建二维码窗体界面,绘制二维码

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class QRcodeunionUI1 extends JFrame {
	private static final long serialVersionUID = 1L;
	String[] tobinarystr= {};
	//创建窗体界面
	public void initUI() {
		setTitle("二维码");
		setSize(800,800);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setVisible(true);
		Graphics g=getGraphics();
		paint(g);//调用绘制方法
	}
	public void paint(Graphics g) {
		super.paint(g);
		//遍历二进制码字符串数组
		for(int i=0;i<tobinarystr.length;i++) {
			String str=tobinarystr[i];
			for(int j=0;j<str.length();j++) {
				char c=str.charAt(j);
				if(c=='0') {
					g.setColor(Color.WHITE);
				}else {
					g.setColor(Color.BLACK);
				}
				//按行绘制二进制码字符串 按列绘制二进制码字符串中的每个字符
				g.fillRect(100+i*10, 100+j*10, 10, 10);
			}
		}
	}
	public static void main(String[]args) {
		QRcodeunionUI1 qrcodeui=new QRcodeunionUI1();
		QRcodeunion1  qrcode=new QRcodeunion1();
		qrcodeui.tobinarystr=qrcode.tobinarystring();//调用QRcodeunion中的方法
		qrcodeui.initUI();
	}
}

5.效果图

 (二)二维码识别

思路:将获得的二维码图片转为int类型的二维数组,保存该图片的像素点。根据图片的宽度和方向进行取值,并设置灰度值,从而对灰度值进行判断以获得二进制码字符串。然后根据获得的二进制码字符串中的首个字符来判断是汉字还是ASCII码。若是ASCII码,则只取前8位二进制码字符合成一个二进制码字符串,若是汉字,则只取后16位二进制码字符合成一个二进制码字符串。之后将获得的二进码字符串先转为整数,再转为字符,并保存到字符数组中。最后遍历输出字符数组中的字符。

1.将获得的二维码图片转为int类型的二维数组,保存该图片的像素点。

public int[][] getpixelarry(String imgpath){
		BufferedImage img = null;//先声明图片对象为空
		try {
			img=ImageIO.read(new File(imgpath));//根据图片路径生成图片
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//获取图片的宽和高
		int w=img.getWidth();
		int h=img.getHeight();
		
		int[][] imgarr=new int[w][h];//定义一个与图片宽高一样的用来保存图片像素点的二维数组
		
		for(int i=0;i<w;i++) {
			for(int j=0;j<h;j++) {
				imgarr[i][j]=img.getRGB(i, j);
			}
		}
		return imgarr;
}

2.根据图片的宽度和方向进行取值,并设置灰度值,从而对灰度值进行判断以获得二进码字符串。

//考虑到截图边界的问题  每次从格子矩形的中点取起
		for(int i=width/2;i<w;i+=width) {
			String str1="";
			String str2="";
			for(int j=width/2;j<width*24;j+=width) {//每隔一个格子取
				int rgb=imgarr[i][j];
				Color color=new Color(rgb);
				//设置灰度值
				int red=color.getRed();
				int blue=color.getBlue();
				int green=color.getGreen();
				int gray=(red+blue+green)/3;
				//根据灰度值进行判断 获取字符串
				if(gray<150) {
					str1+="1";//黑色取1
				}else {
					str1+="0";
				}
		    }
        }

3.根据所获二进制码字符串中的首个字符来判断是汉字还是ASCII码,从而进行相应的取值操作,获得新的二进码字符串。

if(str1.charAt(0)=='0') {//ASCII码
	for(int k=0;k<8;k++) {//只取前8位为一个ASCII码字符的字节
		str2+=""+str1.charAt(k);//二进制码字符转为二进制码字符串
	}
}else {//汉字
	for(int t=8;t<24;t++) {//后16位为汉字的两个字节
		str2+=""+str1.charAt(t);	
	}
}

4.将新的二进码字符串先转为整数,再转为字符,并保存到字符数组中。最后遍历输出该数组中的字符。

int in=Integer.parseInt(str2, 2);//将获取的二进制码字符串转换为整数
char c=(char)in;//整数转为字符
ch[m]=c;//将获取的字符保存在字符数组中
m++;
for(int k=0;k<ch.length;k++) {
	System.out.print(ch[k]);//打印输出字符数组
}

5.完整代码

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
//中英混合二维码识别
public class QRcodecla {
	//构建一个方法  返回将二维码图片转为int类型的二维数组
	public int[][] getpixelarry(String imgpath){
		BufferedImage img = null;//先声明图片对象为空
		try {
			img=ImageIO.read(new File(imgpath));//根据图片路径生成图片
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//获取图片的宽和高
		int w=img.getWidth();
		int h=img.getHeight();
		
		int[][] imgarr=new int[w][h];//定义一个与图片宽高一样的用来保存图片像素点的二维数组
		
		for(int i=0;i<w;i++) {
			for(int j=0;j<h;j++) {
				imgarr[i][j]=img.getRGB(i, j);
			}
		}
		return imgarr;
	}
	//创建方法 根据获得的二进制码 生成字符
	public void tostring() {
		//获取存有二维码图片像素点数组
		int[][] imgarr=getpixelarry("C:\\Users\\011\\Pictures\\Saved Pictures\\中英混合QRcode.png");
		int w=imgarr.length;
		int width=w/10;//格子宽度
		char[] ch=new char[10];
		int m=0;//待输出字符数组的下标
		//考虑到截图边界的问题  每次从格子矩形的中点取起
		for(int i=width/2;i<w;i+=width) {
			String str1="";
			String str2="";
			for(int j=width/2;j<width*24;j+=width) {//每隔一个格子取
				int rgb=imgarr[i][j];
				Color color=new Color(rgb);
				//设置灰度值
				int red=color.getRed();
				int blue=color.getBlue();
				int green=color.getGreen();
				int gray=(red+blue+green)/3;
				//根据灰度值进行判断
				if(gray<150) {
					str1+="1";//黑色取1
				}else {
					str1+="0";
				}
			}
			//根据获得二进制码字符串中的首个字符判断是汉字还ASCII码
			if(str1.charAt(0)=='0') {//ASCII码
				for(int k=0;k<8;k++) {//只取前8位为一个ASCII码字符的字节
					str2+=""+str1.charAt(k);//二进制码字符转为二进制码字符串
				}
			}else {//汉字
				for(int t=8;t<24;t++) {//后16位为汉字的两个字节
					str2+=""+str1.charAt(t);	
				}
			}
			int in=Integer.parseInt(str2, 2);//将获取的二进制码字符串转换为整数
			char c=(char)in;//整数转为字符
			System.out.println(str2+"---"+c);
			ch[m]=c;//将获取的字符保存到字符数组中
			m++;
		}
		for(int k=0;k<ch.length;k++) {
			System.out.print(ch[k]);//打印输出字符数组
		}
	}
	public static void main(String[]args) {
		QRcodecla qrcode=new QRcodecla();
		qrcode.tostring();
	}

}

6.效果图

 

 方法二:

(一)二维码生成

思路:获取给定字符串中的每个字符,并将其转为二进制码字符串。根据二进制码字符串的长度来判断字符是汉字还是ASCII码,并对其进行相应的补码操作。若是汉字,补完码后还需在第一个字节前添加一个由二进制码1开头的字节,以区分ASCII码和汉字。并将所有获得的二进制码字符串合成一个新的二进制码字符串。由于新获得的二进制码字符串共有176位二进制码,所以在对新获得的二进码字符串进行字符遍历时,需借助一个计数器,每隔16个字符将计数器设置为0。这样就可以巧妙地设置坐标,使绘制出的图形美观。

1.获取给定字符串中的每个字符,并将其转为二进制码字符串。

String str="abcd今天天气不错";
String binarystr;
for(int i=0;i<str.length();i++) {
	char c=str.charAt(i);//遍历字符串中的每个字符  
	binarystr=Integer.toBinaryString(c);//将获得的字符转为二进制码字符串

2.判断字符是汉字还是ASCII码,并对其进行相应的补码和添加字节操作。之后将所有获得的二进制码字符串合成一个新的二进制码字符串。

//声明一个字符串,用来保存将字符转为二进制码的字符串
String binarystring="";
if(binarystr.length()<=8) {//ASCII码
	while(binarystr.length()<8) {//不足8位的在前补0
		binarystr="0"+binarystr;
	}
}
else{//汉字
	while(binarystr.length()<16) {//不足16位在前补0
		binarystr="0"+binarystr;
	}
	binarystr="10010100"+binarystr;//在前补一个字节 该字节第一位为1  用来区分是汉字还是ASCII码
}
binarystring+=binarystr;

3.对新获得的二进码字符串进行字符遍历,设置画笔颜色,并巧妙地设置坐标。

for(int i=0;i<tobinarystr.length();i++) {
	//每列绘制指定二进制码字符串中的一个字符  设置一个计数器count  
	//将所获得的新二进制码字符串每取16个字符 即每隔16个字符将计数器设置为0  
	if(count==16) {
		count=0;
	}
	char c=tobinarystr.charAt(i);
	if(c=='0') {
	    g.setColor(Color.WHITE);
	}else {
		g.setColor(Color.BLACK);
	}
	//按行绘制二进制码字符串 按列绘制二进制码字符串中的每个字符 这样每行有11个格子 每列有16个格子
	g.fillRect(100+i/16*10, 100+count*10, 10, 10);//格子的宽度和高度都为10
	count++;
}

4.完整代码

QRcodeunion2类:将给定字符串中的每个字符转换为二进制码字符串,并将这些二进制码字符串合成一个新的二进制码字符串。

public class QRcodeunion2 {
	String str="abcd今天天气不错";
	//声明一个字符串,用来保存将字符转为二进制码的字符串
	String binarystring="";
	//创建方法 将字符串中每个字符(ASCII码或汉字)转为二进制码字符串 返回字符串数组
	public String tobinarystring() {
		String binarystr;
		for(int i=0;i<str.length();i++) {
			char c=str.charAt(i);//遍历字符串中的每个字符  
			binarystr=Integer.toBinaryString(c);//将获得的字符转为二进制码字符串
			//判断是汉字还是ASCII码
			if(binarystr.length()<=8) {//ASCII码
				while(binarystr.length()<8) {//不足8位的在前补0
					binarystr="0"+binarystr;
				}
			}
			else{//汉字
				while(binarystr.length()<16) {//不足16位在前补0
					binarystr="0"+binarystr;
				}
				binarystr="10010100"+binarystr;//在前补一个字节 该字节第一位为1  用来区分是汉字还是ASCII码
			}
			System.out.println(binarystr);
			binarystring+=binarystr;	
		}	
		return binarystring;
	}
}

QRcodeunionUI2类:创建窗体界面,绘制二维码

import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class QRcodeunionUI2 extends JFrame {
	private static final long serialVersionUID = 1L;
	QRcodeunion2  qrcode2=new QRcodeunion2();//调用QRcodeunion2中的方法
	String tobinarystr=qrcode2.tobinarystring();
	//创建窗体界面
	public void initUI() {
		setTitle("二维码");
		setSize(800,800);
		setDefaultCloseOperation(EXIT_ON_CLOSE);
		setVisible(true);
		Graphics g=getGraphics();
		paint(g);//调用绘制方法
	}
	public void paint(Graphics g) {
		super.paint(g);
		//遍历字符串
		int count=0;
		for(int i=0;i<tobinarystr.length();i++) {
			//每列绘制指定字符串中的一个字符  设置一个计数器count  
			//将所获得的二进制码字符串每取16个字符 即每隔16个字符将计数器设置为0  
			if(count==16) {
				count=0;
			}
			char c=tobinarystr.charAt(i);
			if(c=='0') {
				g.setColor(Color.WHITE);
			}else {
				g.setColor(Color.BLACK);
			}
			//按行绘制二进制码字符串 按列绘制二进制码字符串中的每个字符 这样每行有11个格子 每列有16个格子
			g.fillRect(100+i/16*10, 100+count*10, 10, 10);//格子的宽度和高度都为10
			count++;
		}
	}
	public static void main(String[]args) {
		QRcodeunionUI2 qrcodeui=new QRcodeunionUI2();
		qrcodeui.initUI();
	}
}

5.效果图

 (二)二维码识别

思路:将获得的二维码图片转为int类型的二维数组,保存该图片的像素点。根据图片的宽度和方向进行取值,并设置灰度值,从而对灰度值进行判断以获得二进制码字符串。从获得的二进制码字符串中截取需转为ASCII码字符或中文字符的二进制码字符串子串,将其保存到字符串数组中。遍历数组中二进制码字符串,将其转为对应的字符,并输出该字符。

1.将获得的二维码图片转为int类型的二维数组,保存该图片的像素点。

public int[][] getpixelarry(String imgpath){
	BufferedImage img = null;//先声明图片对象为空
	try {
	    img=ImageIO.read(new File(imgpath));//根据图片路径生成图片
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	//获取图片的宽和高
	int w=img.getWidth();
	int h=img.getHeight();	
	int[][] imgarr=new int[w][h];//定义一个与图片宽高一样的用来保存图片像素点的二维数组		
	for(int i=0;i<w;i++) {
		for(int j=0;j<h;j++) {
			imgarr[i][j]=img.getRGB(i, j);
		}
	}
	return imgarr;
}

2.根据图片的宽度和方向进行取值,并设置灰度值,从而对灰度值进行判断以获得二进制码字符串。

for(int i=width/2;i<w;i+=width) {//每隔一个格子进行取值
	for(int j=width/2;j<16*width;j+=width) {//每列有16个格子
	    int rgb=imgarr[i][j];
		Color color=new Color(rgb);
        //取灰度值进行判断
		int red=color.getRed();
		int green=color.getGreen();
		int blue=color.getBlue();
		int gray=(red+green+blue)/3;
		if(gray<150) {
			str1+="1";
		}else {
			str1+="0";
		}
	}
}

3.截取需转为ASCII码字符或中文字符的二进制码字符串子串,将其保存到字符串数组中。

int k=0;
while(k<4) {//从二维码图可知,前四个字符为ASCII码
	str2[k]="";//数组中的每个字符串需初始化
	for(int m=k*8;m<8*(1+k);m++) {//每隔8个二进制码取值
		str2[k]+=str1.charAt(m);
	}
	k++;
}
while(4<=k&&k<10) {//后六个字符为中文
	str2[k]="";
	for(int n=40+24*(k-4);n<32+24*(k-3);n++) {//每隔16个二进制码取值 
		str2[k]+=str1.charAt(n);
	}
	k++;
}

4.遍历数组中二进制码字符串,将其转为对应的字符,并输出该字符。

for(int t=0;t<str2.length;t++) {
	int in=Integer.parseInt(str2[t], 2);
	char c=(char)in;
	System.out.print(c);
}

5.完整代码

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class QRcodecla2 {
	//构建一个方法  返回将二维码图片转为int类型的二维数组
		public int[][] getpixelarry(String imgpath){
			BufferedImage img = null;//先声明图片对象为空
			try {
				img=ImageIO.read(new File(imgpath));//根据图片路径生成图片
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//获取图片的宽和高
			int w=img.getWidth();
			int h=img.getHeight();
			int[][] imgarr=new int[w][h];//定义一个与图片宽高一样的用来保存图片像素点的二维数组
			for(int i=0;i<w;i++) {
				for(int j=0;j<h;j++) {
					imgarr[i][j]=img.getRGB(i, j);
				}
			}
			return imgarr;
		}
		public void tostring() {
			int[][] imgarr=getpixelarry("C:\\Users\\011\\Pictures\\Saved Pictures\\中英混合2.png");
			int w=imgarr.length;
			int width=w/11;//格子宽度(一行由11个黑白格子组成)
			String str1="";//定义保存由灰度值判断的二进制码
			String[] str2=new String[10];//定义保存待转换字符的二进制码字符串
			//考虑到截图边界的问题  每次从格子矩形的中点取起
			for(int i=width/2;i<w;i+=width) {
				for(int j=width/2;j<16*width;j+=width) {
					int rgb=imgarr[i][j];
					Color color=new Color(rgb);
					//取灰度值进行判断
					int red=color.getRed();
					int green=color.getGreen();
					int blue=color.getBlue();
					int gray=(red+green+blue)/3;
					if(gray<150) {
						str1+="1";
					}else {
						str1+="0";
					}
				}
			}
			//从str1中截取需转为ASCII码字符(8bit)或中文字符(16bit)的二进制码字符串
			//保存到数组中
			int k=0;
			while(k<4) {//从中英混合二维码图可知,前四个字符为ASCII码
				str2[k]="";//数组中的每个字符串需初始化
				for(int m=k*8;m<8*(1+k);m++) {//每隔8个二进制码取值
					str2[k]+=str1.charAt(m);
				}
				k++;
			}
			while(4<=k&&k<10) {//后六个字符为中文
				str2[k]="";
				for(int n=40+24*(k-4);n<32+24*(k-3);n++) {//每隔16个二进制码取值
					str2[k]+=str1.charAt(n);
				}
				k++;
			}
			//打印输出二进制码字符串及其所代表的字符
			for(int t=0;t<str2.length;t++) {
				int in=Integer.parseInt(str2[t], 2);
				char c=(char)in;
				System.out.println(str2[t]+"---"+c);
			}
			//打印输出识别后的二维码字符串
			for(int t=0;t<str2.length;t++) {
				int in=Integer.parseInt(str2[t], 2);
				char c=(char)in;
				System.out.print(c);
			}
		}
		//程序入口
		public static void main(String[]args) {
			QRcodecla2 qrcodecla2=new QRcodecla2();
			qrcodecla2.tostring();
		}
}

6.效果图

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值