论坛首页 移动开发技术论坛

Kuix乱码问题

浏览 1311 次
该帖已经被评为隐藏帖
作者 正文
   发表时间:2009-11-26  

问题的来源还是前面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 辅助平面的字符使用四字节编码。 

 

论坛首页 移动开发技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics