我正在为一个类项目编写一些编组/解组例程,在这种情况下我对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[])
构造函数的默认行为相互平行?
答案 0 :(得分:4)
问题是您正在使用基础char[]
编写,其中byte[]
表示字符串的UTF-16表示,请参阅javadoc。
然后使用String(byte[] bytes)
构造函数进行读取,该构造函数用于读取使用系统默认编码编码的数据,在您的情况下可能是UTF-8。
您需要保持一致,事实上DataOutputStream.writeUTF()
和DataInputStream.readUTF()
函数是专门为此而设计的
如果您因某些原因需要使用基础byte[]
,则可以使用String
轻松获取String.getBytes("UTF-8")
的UTF-8表示形式,再次参阅javadoc。
为简化问题,您只需使用ObjectOutputStream
和ObjectInputStream
即可将实际的String
序列化到流而不仅仅是char[]
表示。
答案 1 :(得分:0)
最好认为Java不使用其字符的任何编码。它的字符串只是原始的16位字符值,与UTF16相同。 “其他”方法默认为系统编码的原因是因为不同的平台使用不同的默认编码。例如,将包含部分ascii字符的UTF8写入使用EBDCDIC(sp)的大型机是没有意义的。