【问题标题】:Dealing with unicode �, how to get rid of? Android/java处理unicode�,如何摆脱?安卓/java
【发布时间】:2013-01-09 15:17:16
【问题描述】:

我正在使用终端仿真器库来创建终端,然后使用它将通过串行输入的数据发送到串行设备。图书馆可以看到here

当我将数据输入终端时,正在发送/接收一系列奇怪的字符。我认为 unicode 替换字符是通过串行发送的,串行设备不知道它是什么并返回 ~0。

当我写“测试”时出现在终端中的屏幕截图:

还有显示发送的字符串和接收的数据的日志。

我创建了一个 EmulatorView,它是终端视图。它提到了钻石here

private void sendText(CharSequence text) {
                int n = text.length();
                char c;
                try {
                    for(int i = 0; i < n; i++) {
                        c = text.charAt(i);
                        if (Character.isHighSurrogate(c)) {
                            int codePoint;
                            if (++i < n) {
                                codePoint = Character.toCodePoint(c, text.charAt(i));
                            } else {
                                // Unicode Replacement Glyph, aka white question mark in black diamond.
                                codePoint = '\ufffd';
                            }
                            mapAndSend(codePoint);
                        } else {
                            mapAndSend(c);
                        }
                    }
                } catch (IOException e) {
                    Log.e(TAG, "error writing ", e);
                }
            }

有没有办法解决这个问题?任何人都可以在库类中看到为什么会发生这种情况吗?,如果我愿意,我如何在 java 中引用 � 来解析它?我不能说如果 (!str.contains("�") 我接受它。

当我在终端中输入时,它会运行:

public void write(byte[] bytes, int offset, int count) {


 String str;
try {
    str = new String(bytes, "UTF-8");
      Log.d(TAG, "data received in write: " +str );

      GraphicsTerminalActivity.sendOverSerial(str.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
    Log.d(TAG, "exception" );
    e.printStackTrace();
}

        // appendToEmulator(bytes, 0, bytes.length);

 return;
}

这就是我所说的发送数据。 sendData(Byte[] data) 是一个库方法。

public static void sendOverSerial(byte[] data) {
        String str;
        try {
            str = new String(data,"UTF-8");
             if(mSelectedAdapter !=null && data !=null){
                 Log.d(TAG, "send over serial string==== " + str);

                mSelectedAdapter.sendData(str.getBytes("UTF-8"));
                 }
        } catch (UnsupportedEncodingException e) {
            Log.d(TAG, "exception");
            e.printStackTrace();
        }

    }

一旦发送数据,就会在此处收到回复:

public void onDataReceived(int id, byte[] data) {

        try {
            dataReceived = new String(data, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            Log.d(TAG, "exception");
            e.printStackTrace();
        }

        try {
            dataReceivedByte = dataReceived.getBytes("UTF-8");
        } catch (UnsupportedEncodingException e) {
            Log.d(TAG, "exception");
            e.printStackTrace();
        }
        statusBool = true;
        Log.d(TAG, "in data received " + dataReceived);
        ((MyBAIsWrapper) bis).renew(data);


        runOnUiThread(new Runnable(){

            @Override
            public void run() {

                mSession.appendToEmulator(dataReceivedByte, 0, dataReceivedByte.length);

            }});

    viewHandler.post(updateView);

}

写入字符的库类的相关部分:

类的相关部分:

private void sendText(CharSequence text) {
                int n = text.length();
                char c;
                try {
                    for(int i = 0; i < n; i++) {
                        c = text.charAt(i);
                        if (Character.isHighSurrogate(c)) {
                            int codePoint;
                            if (++i < n) {
                                codePoint = Character.toCodePoint(c, text.charAt(i));
                            } else {
                                // Unicode Replacement Glyph, aka white question mark in black diamond.
                                codePoint = '\ufffd';
                            }
                            mapAndSend(codePoint);
                        } else {
                            mapAndSend(c);
                        }
                    }
                } catch (IOException e) {
                    Log.e(TAG, "error writing ", e);
                }
            }

            private void mapAndSend(int c) throws IOException {
                int result = mKeyListener.mapControlChar(c);
                if (result < TermKeyListener.KEYCODE_OFFSET) {
                    mTermSession.write(result);
                } else {
                    mKeyListener.handleKeyCode(result - TermKeyListener.KEYCODE_OFFSET, getKeypadApplicationMode());
                }
                clearSpecialKeyStatus();
            }

【问题讨论】:

  • 只是出于好奇,如果你输入 hello 这个词,你会得到 4 shevrons
  • 在我输入每个字符后我得到 ~0~0~0 所以你好是:h~0~0~0e~0~0~0l~0~0~0l~0~0~0o ~0~0~0
  • 我可能会抓住稻草,但我问的原因是因为查看您的 sendText 方法,我只是想考虑一下您说您使用了其他编码,这是否可能是一个简单的编码错误。如果您删除循环中的 If 语句并将 c 传递给 mapAndSend,会发生什么?
  • 我会试试看,代码来自库,所以我没有碰过它。谢谢!
  • @Paul 打印出您需要执行以下操作的字节:String log = ""; for( byte i : byteArray ) { log += String.format("0x%02X, ", i); } Log.d(TAG, log);

标签: java android unicode character-encoding


【解决方案1】:

Java 在内部将文本存储为未编码的 Unicode。以前是 16 位,现在我猜是 32 位,因为您在终端上为您尝试输出的每个 unicode 字符输出四个字符。

您可能想要做的是使用类似 string.getBytes("ASCII") 的东西将您的 unicode 字符串转换为直接的单字节 ascii。如果您的终端模拟器处理其他字符集(如 Latin-1),请使用它而不是“ASCII”。

然后,将字节而不是字符串传输到终端仿真器。

注意:我不肯定“ASCII”是字符集的确切名称;你会想自己研究。另外,我不知道 getBytes() 会对无法转换为 ascii 的 unicode 字符做什么,所以你也想研究一下。

ETA:我无法从您发布的剪贴簿留言中了解您的代码逻辑。谁调用了 write(),数据从哪里来,又去了哪里?同样的问题也适用于 sendOverSerial() 和 onDataReceived()。

无论如何,我几乎可以肯定某处,原始 32 位 Unicode 数据在没有被编码的​​情况下被转换为字节。从那时起,无论是按原样发送还是将其重新编码为 UTF-8 都会产生您所看到的效果。我看不出这在您发布的任何代码中是如何发生的,所以我猜它在您向我们展示的任何函数被调用之前发生在其他地方。

【讨论】:

  • 谢谢。我已经尝试在上面提到 UTF-8 的所有地方添加 ascii 和其他字符集,但我得到了相同的结果。总是一个正确的字节和其他 3 个。我会尝试拉丁语。
  • 当我按下一个键时会调用Write,库在某处这样设置它。 � 在本课程中提到:github.com/jackpal/Android-Terminal-Emulator/wiki/… 这是我正在寻找答案的内容,我认为它是负责 32 位 unicode 的类。当我发送要写的东西时,所有写操作都是调用 sendOverSerial,这会通过串行字节发送数据,我可以从日志中看到它已经是 32 位。
  • 然后应答回显返回到 onDataReceived 并将数据写入屏幕。如果我在通过串行发送数据之前将数据写入屏幕,则会写入正确的字符,因为�被忽略,但是因为我通过串行发送数据,串行控制台似乎能够将�识别为 ~0,并且可能为 null返回为 ~0,可以写入屏幕,而 � 不是。
  • 在库类的相关部分编辑,这是发送字符写入的地方。
【解决方案2】:

我已经通过编辑我正在使用的库解决了这个问题。他们正在使用一种将字节转换为 int 的方法,它接受一个 codePoint 并对其进行转换。因此,每次按键都会使用 4 个字节。我改变了这一点,以便使用一个字节而不是一个 int。没有更多的额外字节。与编码格式无关。

【讨论】:

    【解决方案3】:

    您使用的库似乎将代码点作为 int(32 位)发送,并且您的代码假定其编码为 utf-8,无法正确处理 4 字节。这与 java 如何在内部存储文本无关。顺便说一句,Java 在内部将文本存储为编码的 UTF-16,而不是未编码的 unicode。同样,这不是导致此问题的原因。这是您与正在使用的库进行交互的方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-11-16
      • 1970-01-01
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-18
      相关资源
      最近更新 更多