问题的来源还是前面Kuix国际化的乱码问题,之前没有深究,今天刚好看到了,发现了其中的缘由,先看一篇网上的帖子,讲的就是怎么读取UTF-8:
一、读取Unicode文件
/**
* 读取Unicode编码文本文件
* @param resource String - 文件名
* @return String - Unicode文本
*/
public static String read_Uni(String resource) {
byte word_uni[] = new byte[1024];
String strReturn = null;
InputStream is;
try {
is = instance.getClass().getResourceAsStream(resource);
is.skip(2); // 跳过两个字节的文件头
is.read(word_uni);
is.close();
StringBuffer stringbuffer = new StringBuffer("");
for (int j = 0; j < word_uni.length; ) {
int l = word_uni[j++];
int h = word_uni[j++];
char c = (char) ((l & 0xff) | ((h << 8) & 0xff00));
stringbuffer.append(c);
}
strReturn = stringbuffer.toString();
} catch (IOException ex) {
System.out.println(ex);
} finally {
is = null;
}
return strReturn;
}
二、读取UTF-8文件
/**
* 读取UTF-8编码文本文件
* @param resource String - 文件名
* @return String - UTF-8文本
*/
public static String read_UTF(String resource) {
byte word_utf[] = new byte[1024];
String strReturn = null;
InputStream is;
try {
is = instance.getClass().getResourceAsStream(resource);
is.read(word_utf);
is.close();
strReturn = new String(word_utf, "UTF-8");
} catch (IOException ex) {
System.out.println(ex);
}
return strReturn;
}
再看看property文件的内容
YES=\u662F
这是GBK编码保存的,这里有点混乱,实机上myeclipse的property编辑器有个bug,编辑器里面保存的属性值全部用系统编码保存,也就是GBK码,如果你自己在源文件中写中文,而且文件属性是UTF-8,保存后是这样的:
YES=\u00CA\u00C7
变成了4个字节,这种情况下Kuix读到的会是乱码,看Kuix.loadMessages
private static synchronized void loadMessages(InputStream inStream) throws Exception {
InputStreamReader inputStream = new InputStreamReader(inStream, "UTF-8");
......
key = convertString(key);
value = convertString(value);
messageTable.put(key, value);
可以看到试图用UTF-8编码读取文件,而实际上property是用十六进制保存的,所以还需要用convertString做一次转换
private static String convertString(String theString) {
char aChar;
int len = theString.length();
StringBuffer outBuffer = new StringBuffer(len);
for (int x = 0; x < len;) {
aChar = theString.charAt(x++);
if (aChar == '\\') {
aChar = theString.charAt(x++);
if (aChar == 'u') {
// Read the xxxx
int value = 0;
for (int i = 0; i < 4; i++) {
aChar = theString.charAt(x++);
switch (aChar) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + aChar - '0';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
// return KuixConstants.DEFAULT_UNKNOWN_I18N_MESSAGE STRING if there is any problem
return "???";
}
}
outBuffer.append((char) value);
} else {
if (aChar == 't') {
aChar = '\t';
} else if (aChar == 'r') {
aChar = '\r';
} else if (aChar == 'n') {
aChar = '\n';
} else if (aChar == 'f') {
aChar = '\f';
}
outBuffer.append(aChar);
}
} else {
outBuffer.append(aChar);
}
}
return outBuffer.toString();
}
关键在这一句outBuffer.append((char) value);它用两个字节做一个汉字了,所以出现乱码,实际上应该参考前面的方法:
int l = word_uni[j++];
int h = word_uni[j++];
char c = (char) ((l & 0xff) | ((h << 8) & 0xff00));
最后摘一段文章说明UTF-8编码的大小.(http://www.iteye.com/topic/113572)
1、UTF-8用几个字节表示一个汉字?
这各答案你可能了解,但也可能不了解,我敢打保票一半人会不清楚(包括特意查资料之前的我)。
了解这个对编程有什么影响?
以下我把对yoolywu的回答,转为帖子发表,以表重视。
yollywu的问:
引用
系统有两个子系统,一个是BS的,一个是delphi做的CS,中间的数据传输是通过XML进行传输的。在XML传输的功能实现后,要求对XML进行加密解密.加密解密算法是CS端用delphi写的,然后这边用JAVA写个同样的算法。现在碰到的一个问题是:
用该算法的时候,CS和BS各自都能够加解密,我这边的过程是这样的。。。。[但最后]中文始终是乱码
Java代码
StringBuffer strbuf = new StringBuffer();
try {
FileInputStream in = new FileInputStream(file);
int size = 0;
byte [] buf = new byte[1024];
while ((size=in.read(buf)) != -1) {
strbuf.append(new String(buf,0,size));
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return strbuf;
Qieqie的答:
以下的代码是错误的:
Java代码
StringBuffer strbuf = new StringBuffer();
...
strbuf.append(new String(buf,0,size));
第一、
你应该使用ByteArrayOutputStream,将InputStream的字节全部读出来,然后转成byte[]数组,最后在根据你和对方协议规定的字符集合(假设你们规定的是UTF-8,如果没有规定,那么就补充上吧),将byte[]变成String: String theString = new String(bytes, "UTF-8")。
不加"UTF-8"的new String,将使用Java环境设置的字符集,没有特别设置的情况下也就是操作系统的字符集。这是不可靠的。
第二、
不能使用byte[]+StringBuffer:StringBuffer是针对char操作的(String也是)。读取byte时可能刚好把一个多字节的char分成前后两批加入StringBuffer。这样就破坏了char的完整性了。而如果你使用UTF-8编码的中文,你就会中招,导致乱码(其实是因为你的读取是由于byte失去原有顺序导致的,跟一般的乱码还不一样)
--
在UTF-8编码集中,每个汉字使用 3个字符表示! 实践证明:
1、创建一个UTF-8编码的文件:weare.txt
2、写入三个字:“我们是”
3、运行以下代码:
Java代码
public class UTF8 {
public static void main(String[] args) throws IOException {
String p = "weare.txt";
InputStream in = new FileInputStream(p);
int read = in.read(new byte[1204]);
System.out.println(read);
}
}
4、你会发现打印出来的是 9 !
所以,byte[]+StringBuffer的使用方式是错误的!
不过可以使用StringBuffer + bufferedReader.readLine(),读出一行行后再加入StringBuffer。
或者第2楼说的stringbuffer+reader.read(char[])的形式(毕竟错误是由于byte[]导致的,而非StringBuffer)
参考资料:
zh.wikipedia.org 写道
UTF-8 使用一至四个字节为每个字符编码。128 个 ASCII 字符(Unicode 范围由 U+0000 至 U+007F)只需一个字节,带有变音符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及马尔代夫语(Unicode 范围由 U+0080 至 U+07FF)需要二个字节,其他基本多文种平面(BMP)中的字符(CJK属于此类-Qieqie注)使用三个字节,其他 Unicode 辅助平面的字符使用四字节编码。