使用ZXing对byte[]进行编码和解码

9
我正在开发一款Android应用程序,需要对使用ZXing应用程序生成的QRCode中的字节数组进行编码和解码。我的问题是,我解码后的消息与生成的字节数组并不完全匹配。 我尝试基于包含递增索引的字节数组创建QRCode,即:
input = [0, 1, 2, ..., 124, 125, 126, 127, -128, -127,... -3, -2, -1, 0, 1, 2, ...]

在将消息编码到QRCode中并在响应方解码后,我获得了以下字节数组输出:
output = [0, 1, 2, ..., 124, 125, 126, 127, 63, 63,... 63, 63, 63, 0, 1, 2, ...]

所有“负”字节值都会被转换为ASCII字符63:“?”问号字符。我认为编码字符集出了问题,但由于我正在使用ISO-8859-1,而且每个人都声称这是此类问题的解决方案(其他处理相同问题的主题这里),我不知道我的错误在哪里,或者我在编码或解码实例化期间是否跳过了某个步骤。以下是我执行的用于编码给定字节数组的代码:
String text = "";
byte[] res = new byte[272];
for (int i = 0; i < res.length; i++) {
    res[i] = (byte) (i%256);
}
try {
    text = new String(res, "ISO8859_1");
} catch (UnsupportedEncodingException e) {
    // TODO
}
Intent intent = new Intent(Intents.Encode.ACTION);
Intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
intent.putExtra(Intents.Encode.TYPE, Contents.Type.TEXT);
intent.putExtra(Intents.Encode.FORMAT, "ISO8859_1");
intent.putExtra(Intents.Encode.DATA, text);
intent.putExtra(Intents.Encode.FORMAT, BarcodeFormat.QR_CODE.toString());

boolean useVCard = intent.getBooleanExtra(USE_VCARD_KEY, false);
QRCodeEncoder qrCodeEncoder = new QRCodeEncoder(activity, intent, dimension, useVCard);
Bitmap bitmap = qrCodeEncoder.encodeAsBitmap();

为了解码QRCode,我发送以下意图:
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.qrcodeDecoding);

    Intent intent = new Intent(Intents.Scan.ACTION);
    intent.putExtra(Intents.Scan.MODE, Intents.Scan.QR_CODE_MODE);
    startActivityForResult(intent, 0);
}

等待结果:

@Override
protected void onActivityResult(int request, int result, Intent data)
{
    if(request == 0)
    {
        //action
        if(result == RESULT_OK)
        {
            String res = data.getStringExtra(Intents.Scan.RESULT);
            byte[] dat = null;

            try{
                    dat = res.getBytes("ISO8859_1");
            } catch(UnsopportedEncodingException e) {
                    //TODO
            }
        }
        else if(result == RESULT_CANCELED)
        {
            //TODO
        }
    }

}

请问您能否告诉我我的错误在哪里,或者应该看哪里?

非常感谢,

Franck


只是为了“玩一下”(我对此一无所知),例如,如果使用UTF-8作为编码会发生什么? - User
UTF-8 肯定行不通。首先,并非每个字节序列都是有效的 UTF-8 序列。因此,大多数输入甚至无法通过这种方式得到一个字符串。 - Sean Owen
3个回答

5
在我的一个应用程序中,我需要对使用ZXing应用生成的QR码中的字节数组进行编码和解码。由于字节数组包含压缩的文本数据,我想避免使用base64编码。虽然这是可能的,但由于我迄今为止没有看到完整的代码片段集合,因此我将在此发布它们。
编码:
public void showQRCode(Activity activity, byte[] data){
  Intent intent = new Intent("com.google.zxing.client.android.ENCODE");
  intent.putExtra("ENCODE_TYPE", "TEXT_TYPE");
  intent.putExtra("ENCODE_SHOW_CONTENTS", false);
  intent.putExtra("ENCODE_DATA", new String(data, "ISO-8859-1"));
  activity.startActivity(intent);
}

开始扫描:

public static void startQRCodeScan(Activity activity){
  Intent intent = new Intent(com.google.zxing.client.android.SCAN);
  intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
  intent.putExtra("CHARACTER_SET", "ISO-8859-1");
  activity.startActivityForResult(intent, 0);
}

扫描结果处理程序:

public void onActivityResult(int requestCode, int resultCode, Intent intent) {
  byte[] result = intent.getStringExtra("SCAN_RESULT").getBytes("ISO-8859-1");
  ...
}

我认为在启动扫描的意图数据中未将CHARACTER_SET设置为ISO-8859-1是原始问题代码失败的关键点。我花了很长时间来挖掘这一点,因为我没有看到这个问题在任何地方被清楚地发布,而Latin 1编码是Xzing中QR代码的标准编码。特别棘手的是,Xzing在线解码器http://zxing.org/w/decode.jspx也没有设置CHARACTER_SET,因此在该站点上解码生成的QR代码时,它看起来有缺陷。

2
你犯了一个错误,认为可以在不使用任何装甲的情况下将任意二进制数据转换为有效字符串。但实际上这是行不通的。在任何标准字符集/编码中,二进制->文本->二进制的过程都是有损的。(提示:即使使用UTF-8也不行。)
为了确保二进制数据不会被破坏,你应该使用像base64编码或十六进制编码这样的东西。

1
我同意总体观点;虽然在Java中ISO-8559-1确实可以用于此目的。 - Sean Owen
1
@SeanOwen 我同意你的观点,将byte[]编码和解码为字符串ISO-8859-1以及反之是可行的。问题可能出在ZXing层面上。然而,base64解决了这个问题。 - franckysnow
1
据我了解,当将任意缓冲区转换为字符串时,ISO-8859-1 的效率达到了100%。而 base64 的效率只有75%。请问我的理解是否正确? - Luca Carlon
@LucaCarlon - 不是的。但效率并不是唯一的问题。 - Stephen C
@SeanOwen 我也遇到了同样的问题,已经浪费了两天时间。我有一个随机字节数组,将其转换为ISO-8859-1字符串并传递给QR码编写器并没有解决我的问题。转换为base64可以解决问题,但会增加总数据大小。接下来我该怎么办? - arvind.mohan
显示剩余3条评论

1
从概念上讲,QR码编码的是文本而不是字节。当然,在内部它们会将输入转换为一系列字节,但对调用者来说这是不透明的。你是正确的,选择正确的编码方式可以让你通过偷梁换柱的方式传递字节,而ISO-8859-1在这里是正确的选择。实际上它确实可行。
ASCII不可能,因为它没有为>=128定义字符,UTF-8肯定行不通。
问题可能出在你的代码上。我不确定你在尝试什么……看起来你正在设置发送到某个地方(到条形码扫描器?)的Intent,但是你并没有这样做,你只是创建了一个Intent并将其发送到从项目中复制的某些代码?我想你在如何设置Intent的额外信息方面出了问题。
如果你在应用程序内部进行操作,这应该会简单得多。只需直接重用QRCodeEncoder.encodeAsBitmap()并删除其余部分即可。

QRCodeEncoder 的唯一构造函数需要四个参数:QRCodeEncoder(Activity activity, Intent intent, int dimension, boolean useVCard)。由于没有设置器方法,我认为这是唯一的方法。如果您能更具体地说明额外信息可能出现的问题,那就更好了。似乎使用 base64 编码并将其传递给 QRCodeEncoder 的 ISO8859-1 字符串可以解决问题。谢谢! - franckysnow
我的意思是你根本不需要这个类——只需复制我指示的方法。android/中的代码并不是一个库;它是我们的应用程序。主要问题在于你在这里做了一些多余的事情——当你没有要回复的Intent时监听它。阅读关键字的javadoc ——FORMAT与字符编码无关,而你却设置了两次。 - Sean Owen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接