zxing解析中文符号错误

二维码扫描器app(Android)小结:

进入正题之前,先对之前做的这个手机二维码扫描器app做个总结吧,看官可以忽略这部分。

该扫描器的工作流程如下:

1. 解析扫到的二维码 ;

2. 分析扫到的信息,并向服务器发送相应的数据请求或修改命令;

3. 接收服务器返回数据并显示。


问题背景:

解析二维码的部分是用的zxing这个开源库;

二维码编码格式为按字节编码;

二维码生成工具为phpqrcode;

源数据为GB2312格式的字符串。

遇到的问题如下:当源数据中含有中文字符时,手机端解码就会出现错误。如中文乘号“×”,在源数据中由0xai0xc1两个字节表示,但是解码之后却变成了别的数据,这就导致手机端解码完毕后,虽然解码成功,显示也正常(“×”虽然二进制数据不对,但是在手机端却能正常显示,这点很奇怪 PS:手机端的文本编码格式都是GB2312),但是发到数据库的信息却是错的。

比如,有一张二维码的信息是“T2×2”,扫描器到这个信息后,会向服务器询问该零件是否存在与数据库中。数据库中的“×”是以0xa10xc1表示的,但是手机端发过来的却是另外的值,因此查询的结果是数据库中不存在该零件。

问题原因:

zxing在解qr码时,即使qr码的编码格式是按字节编码,decode的返回结果里的字符串也不是原始的字节串,因为zxing在解析qr码时,解析出原始的字节串之后,会调用一个名为StringUtils.guessEncoding(readBytes, hints);的函数,来猜测原始字符串的编码格式。猜测的具体过程我没有看,但是猜测的结果就是——把原始字节串破坏了。

解决方法:

我想既然编码方式里有按字节编码,解码结果里应该也有按字节输出的功能吧,结果并没有。于是,只能自己再写一个函数,在解码的最后,不进行编码猜测,而是直接输出解析出来的原始字节串并输出。

注意:下面代码是直接写在自己的工程里的,而不是添加到zxing里。代码中调用的一些方法在从gid上直接下载到的zxing版本中是不可见的,需要修改zxing的源代码并重新生成一个jar才可以。当然,也可以直接在zxing里添加一个函数来实现这个功能,这样的效率会更高。(其实也有可能是zxing中本身就有实现这个功能函数,但是我没找到。。)

代码如下:

private byte[] QRDetectAndDecode(PlanarYUVLuminanceSource img)
			{
				BinaryBitmap img_bitmap = new BinaryBitmap(new HybridBinarizer(img));
				try {
					Detector qrDetector = new Detector(img_bitmap.getBlackMatrix());
					try {
						DetectorResult detectRs = qrDetector.detect();
						BitMatrix codeBits = detectRs.getBits();
						BitMatrixParser paser = getQRPaser(codeBits);
						try {
							Decoder qrDecoder = new Decoder();
							DecoderResult rs = qrDecoder.decode(codeBits);
							byte[] rawByte = rs.getRawBytes();
							BitSource bits = new BitSource(rawByte);// 获取原始字节(带标识码和校验码)
							Mode mode = Mode.forBits(bits.readBits(4));// 获取qr类型(8bit或者kanji等等)
							int count = bits.readBits(mode.getCharacterCountBits(paser.readVersion()));
							byte[] readBytes = new byte[count];
							for (int i = 0; i < count; i++) {
								readBytes[i] = (byte) bits.readBits(8);
							}
							int isee = 0;
							return readBytes;
						} catch (ChecksumException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
							Log.e(TAG, "decode ChecksumException");
						} catch (FormatException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
							Log.e(TAG, "decode FormatException");
						}
						
					} catch (FormatException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				} catch (NotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return null;
			}
private BitMatrixParser getQRPaser(BitMatrix tBitMatrix) {
				BitMatrixParser rs = null;
				Detector qrDetector = new Detector(tBitMatrix);
				DetectorResult tDetectorResult = null;
				try {
					tDetectorResult = qrDetector.detect();
				} catch (NotFoundException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					// Log.e(TAG, "detect NotFoundException");
					return null;
				} catch (FormatException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					Log.e(TAG, "detect FormatException");
					return null;
				}
				try {
					rs = new BitMatrixParser(tDetectorResult.getBits());
				} catch (FormatException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					return null;
				}

				return rs;
			}


补充:

按照上述改法,又出现了另一个问题——不是以完全以字节形式编码的信息,会解析不完全。

例如,源信息为“123”,如果在编码时,把123当做了数字进行处理的话,按照上述方法解析,将会解析不到“123”。

原因分析:

上述方法的实质是,自己直接从返回结果中的RawBytes取出了数据,而没有让zxing去帮你猜测这串字节的编码格式并自动转换为String,因此避免了自动转换环节出现的错误。但是,在源码中,RawBytes中存放的只是用Byte形式编码的数据,并没有存储以字母或者数字形式编码的数据。

解决方法:

二维码的编码的实质是,将整段信息拆分成若干端,存于一连串的有序的单元(segment)中,之后再加入适当校验信息。于是,解码的实质其实就是解析出每个segment中的值,之后再讲其拼接起来,最终得到原始信息。需要注意的是,这些单元是有类型之分的。一个单元的“类型”,标明了该单元需要用什么策略去解析。在我的项目中,遇到单元的类型有——字母,数字,字节这三种。

zxing在解析QR码时,只在解析字节型segment时,将解析出来的原始字节保存在了RawBytes中。因此,我将解析字母和数字segment得到的数据都也存入了segmentList(RawBytes的来源就是它)中。在原版本的源码中,只有字节模式的segment解析后才将解析出来的字节保存到segmentList里,其它类型的都不保存。


你可能感兴趣的:(订单系统)