分享

【Java编程高级进阶】java 获取 string 字符串的编码详解

 好闺女瑶瑶 2017-09-08

刚刚研究的一个问题“Java同样的汉字在服务器和本地的电脑上URLencode 出来的结果不一致”也涉及了字符串的编码格式。

最简单的方法就是:Charset.defaultCharset();

Servlet中可以使用:request.getCharacterEncoding();

也可以使用上文提到的,不过也不那么简单:

  1. dfltEncName = (String)AccessController.doPrivileged(new GetPropertyAction("file.encoding"));   

也有一些其它方式可以参考,如下面这个就比较麻烦了!

  1. public static String getEncoding(String str) {        
  2.        String encode = "GB2312";        
  3.       try {        
  4.           if (str.equals(new String(str.getBytes(encode), encode))) {      //判断是不是GB2312  
  5.                String s = encode;        
  6.               return s;      //是的话,返回“GB2312“,以下代码同理  
  7.            }        
  8.        } catch (Exception exception) {        
  9.        }        
  10.        encode = "ISO-8859-1";        
  11.       try {        
  12.           if (str.equals(new String(str.getBytes(encode), encode))) {      //判断是不是ISO-8859-1  
  13.                String s1 = encode;        
  14.               return s1;        
  15.            }        
  16.        } catch (Exception exception1) {        
  17.        }        
  18.        encode = "UTF-8";        
  19.       try {        
  20.           if (str.equals(new String(str.getBytes(encode), encode))) {   //判断是不是UTF-8  
  21.                String s2 = encode;        
  22.               return s2;        
  23.            }        
  24.        } catch (Exception exception2) {        
  25.        }        
  26.        encode = "GBK";        
  27.       try {        
  28.           if (str.equals(new String(str.getBytes(encode), encode))) {      //判断是不是GBK  
  29.                String s3 = encode;        
  30.               return s3;        
  31.            }        
  32.        } catch (Exception exception3) {        
  33.        }        
  34.       return "";        //如果都不是,说明输入的内容不属于常见的编码格式。  

2017-02-10更新

纠正对”Charset.defaultCharset()“的错误理解,Charset.defaultCharset()“方法的注释内容是返回此Java虚拟机的默认字符集。默认字符集在虚拟机启动期间确定,并且通常取决于底层操作系统的区域设置和字符集。而字符串的编码是受来源影响的!

但是如”Java同样的汉字在服务器和本地的电脑上URLencode 出来的结果不一致“中提到的,Charset.defaultCharset()“实际上是返回的”file.encoding“,也就是文件采用的编码。

使用”System.getProperties()“ 可以得到如下几种编码相关的属性:

file.encoding.pkg=sun.io

sun.jnu.encoding=GBK

file.encoding=UTF-8

sun.io.unicode.encoding=UnicodeLittle

sun.cpu.endian=little

sun.jnu.encoding 猜测是系统的默认编码,参考:

[plain] view plain copy
  1. 关于 Java 的系统属性 sun.jnu.encoding 和 file.encoding 的区别  
  2. sun.jnu.encoding 影响文件名的创建,而 file.encoding 则影响到文件内容。  
  3. 所以说,在我们使用 Java 处理中文文件的时候,如果发现文件的中文内容没有乱码,而文件的中文名发生乱码,我们就应当多考虑一下 sun.jnu.encoding 和 file.encoding 的区别了。  

file.encoding 处理文件内容默认使用的编码;

使用Eclipse main 方法测试时,会自动根据文件内容的编码确定启动JVM使用的编码,如下图:


如果在这里配置了和文件编码不同的编码方式,文件中的字符串输出可能就会产生乱码,如下面的代码输出为:

ISO-8859-1
?????????? ?????18

  1. public static void main(String[] args) {  
  2.     Properties pro = System.getProperties();  
  3.     System.out.println(pro.getProperty("file.encoding"));  
  4.       
  5.     try {  
  6.         String destination = new String("中文エキサイト네이버 중국어사전17");  
  7.         if(destination.equals(new String(destination.getBytes("GB2312"), "GB2312")))  
  8.         {  
  9.             System.out.println(destination);  
  10.         }  
  11.           
  12.         destination = new String("中文エキサイト네이버 중국어사전18");  
  13.         if(destination.equals(new String(destination.getBytes("utf-8"), "utf-8")))  
  14.         {  
  15.             System.out.println(destination);  
  16.         }  
  17.     } catch (UnsupportedEncodingException e) {  
  18.         // TODO Auto-generated catch block  
  19.         e.printStackTrace();  
  20.     }  
  21. }  
由于文件的编码是”UTF-8“,而调试配置的是:ISO-8859-1
上面的代码我们创建一个名为 ”CharsetTest3.java“的文件,在CMD下编译执行:

打开CMD,切换到文件所在目录:

  1. D:\javaProject\javatrans\src\main\java\wasdev\sample\servlet>javac CharsetTest3.  
  2. java  
  3. CharsetTest3.java:17: 错误: 编码GBK的不可映射字符  
  4.                         String destination = new String("涓枃銈ㄣ偔銈点偆銉堧  
  5. 劋鞚措矂 欷戧淡鞏挫偓鞝?17");  
  6.   
  7. ^  
  8. CharsetTest3.java:23: 错误: 编码GBK的不可映射字符  
  9.                         destination = new String("涓枃銈ㄣ偔銈点偆銉堧劋鞚措矂  
  10.  欷戧淡鞏挫偓鞝?18");  
  11.                                                                          ^  
  12. 2 个错误  

没有指定编码,与系统默认不符!

  1. D:\javaProject\javatrans\src\main\java\wasdev\sample\servlet>javac CharsetTest3.  
  2. java -encoding utf-8  
使用”-encoding utf-8“指定编码,执行成功。
注意:在CMD在测试的时候最好不要使用包名,不然很麻烦,我真想说恨死Java了(包的查找方式太恶心,或许是因为性能考虑所以写的这么死板)。

我的文件完整路径是:D:\javaProject\javatrans\src\main\java\wasdev\sample\servlet\CharsetTest3.java

可以通过” -Dfile.encoding=utf-8“指定编码方式


但是CMD应该是使用的GBK编码,不支持韩文,我们把韩文复制粘贴到CMD窗口中发现这一点:


只见光标后移了,却看不见内容。

sun.cpu.endian=little CPU的字节序为小结尾,这涉及到”主机字节序和网络字节序“的问题,通常主机字节序为小结尾(Little endian:将低序字节存储在起始地址),网络字节序为大结尾(Big endian:将高序字节存储在起始地址)。但不同的处理器(CPU)、操作系统也有可能不同。

优化后的方法:

  1. /** 
  2.  * 判断字符串的编码 
  3.  * 
  4.  * @param str 
  5.  * @return 
  6.  */  
  7. public static String getEncoding(String str) {  
  8.     String encode[] = new String[]{  
  9.             "UTF-8",  
  10.             "ISO-8859-1",  
  11.             "GB2312",  
  12.             "GBK",  
  13.             "GB18030",  
  14.             "Big5",  
  15.             "Unicode",  
  16.             "ASCII"  
  17.     };  
  18.     for (int i = 0; i < encode.length; i++){  
  19.         try {  
  20.             if (str.equals(new String(str.getBytes(encode[i]), encode[i]))) {  
  21.                 return encode[i];  
  22.             }  
  23.         } catch (Exception ex) {  
  24.         }  
  25.     }  
  26.       
  27.     return "";  
  28. }  
说明:把”UTF-8“放在第一位是因为现在使用的比较普遍,见下图:


显示Google记录的2001年至2012年网络上主要编码的使用情况。参考:https://en./wiki/UTF-8测试代码:

  1. public static void main(String[] args) {  
  2.     String str = "中文エキサイト";  
  3.     String encode = getEncoding(str);  
  4.     System.out.println(encode);  
  5.       
  6.     str = "中文エキサイト네이버 중국어사전";  
  7.     encode = getEncoding(str);  
  8.     System.out.println(encode);  
  9. }  
执行输出为:UTF-8
UTF-8
注意:如果将数组”String encode[]“中”UTF-8“和”GB2312“的位置换一下,结果就会发生改变为:
GB2312
UTF-8
这是为什么?通过监视以下代码中的”destination.getBytes“,我们可以发现是存在重码区域的!

  1. destination = new String("中文エキサイト네이버 중국어사전17");  
  2. if(destination.equals(new String(destination.getBytes("GB2312"), "GB2312")))  
  3. {  
  4.     System.out.println(destination);  
  5. }  
  6.   
  7. destination = new String("中文エキサイト네이버 중국어사전18");  
  8. if(destination.equals(new String(destination.getBytes("utf-8"), "utf-8")))  
  9. {  
  10.     System.out.println(destination);  
  11. }  

如下图:


所以”getEncoding“方法的正确性有是局限性的,一方面是数组中包含的编码的各类是否够全,一方面是需要判断的字符串特征是否明显。


======================文档信息===========================

版权声明:非商用自由转载-保持署名-注明出处

署名(BY) :testcs_dn(微wx笑)

文章出处:[无知人生,记录点滴](http://blog.csdn.NET/testcs_dn)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多