1

我正在为一个类项目编写一些编组/解组例程,并且在这种情况下对 Java 的默认行为有点困惑。这是我的“天真”子程序,用于在字节流中写入和读取字符串:

protected static void write(DataOutputStream dout, String str)
        throws IOException{
    dout.writeInt(str.length());
    dout.writeChars(str);
}

protected static String readString(DataInputStream din)
        throws IOException{
    int strLength = 2*din.readInt(); // b/c there are two bytes per char
    byte[] stringHolder = new byte[strLength];
    din.read(stringHolder);
    return new String(stringHolder);
}

不幸的是,这根本行不通。字符默认以 UTF-16 格式编写,但String(byte[])似乎假设每个字节都包含一个字符,并且由于 ASCII 字符在 UTF-16 中都以 0 字节开头,因此构造函数似乎只是放弃并返回一个空细绳。解决方案是更改readString为指定必须使用 UTF-16 编码:

protected static String readString(DataInputStream din)
        throws IOException{
    int strLength = 2*din.readInt();
    byte[] stringHolder = new byte[strLength];
    din.read(stringHolder);
    return new String(stringHolder, "UTF-16");
}

我的问题是,为什么这是必要的?由于Java默认使用UTF-16作为字符串,为什么在从字节读取字符时不假设使用UTF-16?或者,为什么不默认将字符编码为字节?简而言之,为什么writeChars()方法和String(byte[])构造函数的默认行为不相互平行?

4

2 回答 2

4

问题是您正在使用底层编写char[],它本质上是 abyte[]表示字符串的 UTF-16 表示,请参阅javadoc
然后,您正在使用String(byte[] bytes)构造函数进行读取,该构造函数旨在读取使用系统默认编码编码的数据,在您的情况下,大概是 UTF-8。
您需要保持一致,实际上DataOutputStream.writeUTF()DataInputStream.readUTF()功能是专门为此设计的。
如果您出于某种原因想要使用底层byte[],您可以获得 UTF-8 表示的Stringeasy using String.getBytes("UTF-8"),再次,请参阅javadoc
为了简化问题,您可以使用 anObjectOutputStream和 anObjectInputStream来序列化实际的String流,而不仅仅是它的char[]表示。

于 2013-02-18T00:11:29.523 回答
0

最好认为 Java 不使用其字符的任何编码。它的字符串只是原始的 16 位字符值,与 UTF16 相同。“其他”方法默认使用系统编码的原因是因为不同的平台使用不同的默认编码。例如,将包含部分 ascii 字符的 UTF8 写入使用 EBDCDIC (sp) 的大型机是没有意义的。

于 2013-02-18T02:25:05.933 回答