Java字符编码及获取文件编码

字符编码

iso8859-1

单字节编码。

最早的编码是iso8859-1,和 ascii 编码相似。属于单字节编码,最多能表示的字符范围是 0-255,应用于英文系列。

iso8859-1是单字节编码,自身不能显示中文,若要显示中文,必须和其他能显示中文的编码配合,如:GBKUTF-8

以 “中文” 为例,这两个字不存在 iso8859-1 编码,以 gb2312 编码为例,应该是d6d0 cec4两个字符,使用 iso8859-1 编码的时候则将它拆开为 4 个字节来表示:d6 d0 ce c4事实上,在进行存储的时候,也是以字节为单位处理的)。而如果是 UTF 编码,则是 6 个字节e4 b8 ad e6 96 87

由于是单字节编码,和计算机最基础的表示单位一致,所以在很多协议上,默认使用该编码。

gb2312/GBK/gb18030

双字节编码。

专门用来表示汉字,是双字节编码,兼容 iso8859-1 编码。其中 gbk 编码能够用来同时表示繁体字和简体字,而 gb2312 只能表示简体字,gbk 是兼容 gb2312 编码的。gb18030 又向下兼容 gbk。gb18030>gbk>gb2312

unicode

这是最统一的编码,可以用来表示所有语言的字符,而且是定长双字节(也有四字节的)编码,包括英文字母在内。不兼容iso8859-1编码的,也不兼容任何编码很多软件内部是使用unicode编码来处理的,比如 Java。

utf-8

不定长编码。

考虑到 unicode 不便于传输和存储。因此而产生了 utf 编码,utf 编码兼容 iso8859-1 编码,同时也可以用来表示所有语言的字符。
不过,utf 编码是不定长编码,每一个字符的长度从 1-6 个字节不等。另外,utf 编码自带简单的校验功能。一般来讲,英文字母都是用一个字节表示,而汉字使用三个字节。

Java对字符编码的处理

getBytes(Charset charset)

将字符串按照 charset 编码,并以字节数组形式返回。比如 “中文”,如果 charset 为gbk,则返回字节数组 [d6 d0 ce c4] 。如果 charset 为utf8,则返回 [e4 b8 ad e6 96 87] 。如果是iso8859-1由于无法编码,最后返回 3f 3f ? ?

String str = "中文";   //这是个unicode
byte[] gbk_bytes = str.getBytes("GBK");     //unicode -> GBK
byte[] utf8_bytes = str.getBytes("UTF-8");  //unicode -> UTF-8
byte[] iso8859_bytes = str.getBytes("ISO-8859-1");  //unicode -> ISO-8859-1

System.out.println(Charset.defaultCharset());

for(byte b : gbk_bytes){
    System.out.println(Integer.toHexString(b));
}
System.out.println("------");

for(byte b : utf8_bytes){
    System.out.println(Integer.toHexString(b));
}
System.out.println("------");

for(byte b : iso8859_bytes){
    System.out.println(Integer.toHexString(b));
}

输出:
UTF-8
ffffffd6
ffffffd0
ffffffce
ffffffc4
------
ffffffe4
ffffffb8
ffffffad
ffffffe6
ffffff96
ffffff87
------
3f
3f

注:String str = “中文”; 这是个 unicode 编码

Charset.defaultCharset():获取当前环境字符编码。

String(byte bytes[], String charsetName)

指定字符编码的构造函数,将字节数组按照指定的字符编码进行解码,返回字符串。

String str = "中文";   //这是个unicode
byte[] gbk_bytes = str.getBytes("GBK");     //unicode -> GBK
byte[] utf8_bytes = str.getBytes("UTF-8");  //unicode -> UTF-8
byte[] iso8859_bytes = str.getBytes("ISO-8859-1");  //unicode -> ISO-8859-1

System.out.println(new String(gbk_bytes, "GBK"));
System.out.println(new String(utf8_bytes, "UTF-8"));
System.out.println(new String(iso8859_bytes, "ISO-8859-1"));

输出:
中文
中文
??

因为iso8859-1没有中文对应的编码,getBytes()返回的就是未知字节数组,所以反向解析的时候,也是解析不出来的,所以返回乱码。

字符编码转换

UTF-8 -> GBK、GBK -> UTF-8转换

public class ConvertDemo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        String str="中文";

        /* UTF-8 -> GBK */
        byte[] utf8_bytes = str.getBytes("UTF-8");
        System.out.println("转换前UTF-8=" + Arrays.toString(utf8_bytes));
        byte[] gbkBytes = convert(utf8_bytes, "UTF-8", "GBK");
        System.out.println("转换后GBK=" + Arrays.toString(gbkBytes));
        System.out.println("转换后GBK=" + new String(gbkBytes, "GBK"));

        /* GBK -> UTF-8 */
        byte[] gbk_bytes = str.getBytes("GBK");
        System.out.println("转换前GBK=" + Arrays.toString(gbk_bytes));
        byte[] utfBytes = convert(gbk_bytes, "GBK", "UTF-8");
        System.out.println("转换后UTF-8=" + Arrays.toString(utfBytes));
        System.out.println("转换后UTF-8=" + new String(utfBytes, "UTF-8"));
    }

    public static byte[] convert(byte[] bytes, String fromCharsetName, String toCharsetName) throws UnsupportedEncodingException {
        String string = new String(bytes, fromCharsetName);
        byte[] bytes1 = string.getBytes(toCharsetName);

        return bytes1;
    }
}
输出:
转换前UTF-8=[-28, -72, -83, -26, -106, -121]
转换后GBK=[-42, -48, -50, -60]
转换后GBK=中文
转换前GBK=[-42, -48, -50, -60]
转换后UTF-8=[-28, -72, -83, -26, -106, -121]
转换后UTF-8=中文
  1. 先按照原先的字符编码将字节数组解码成字符串
  2. getBytes()指定需要转换的字符集进行编码,返回编码后的字节数组。

不同字符编码间转换

String str = "中文";   //这是个unicode
byte[] gbk_bytes = str.getBytes("GBK");     //unicode -> GBK
byte[] utf8_bytes = str.getBytes("UTF-8");  //unicode -> UTF-8
byte[] iso8859_bytes = str.getBytes("ISO-8859-1");  //unicode -> ISO-8859-1

//UTF->GBK->UTF
String str11 = new String(utf8_bytes,"GBK");
String str12 = new String(str11.getBytes("GBK"),"UTF-8");
System.out.println(str11);
System.out.println(str12);
System.out.println("+++++++");

//UTF->ISO->UTF
String str21 = new String(utf8_bytes,"ISO-8859-1");
String str22 = new String(str21.getBytes("ISO-8859-1"),"UTF-8");
System.out.println(str21);
System.out.println(str22);
System.out.println("+++++++");

//GBK->UTF->GBK
String str31 = new String(gbk_bytes,"UTF-8");
String str32 = new String(str31.getBytes("UTF-8"),"GBK");
System.out.println(str31);
System.out.println(str32);
System.out.println("+++++++");

//GBK->ISO->GBK
String str41 = new String(gbk_bytes,"ISO8859-1");
String str42 = new String(str41.getBytes("ISO8859-1"),"GBK");
System.out.println(str41);
System.out.println(str42);

输出:
涓枃
中文
+++++++
中文
中文
+++++++
����
锟斤拷锟斤拷
+++++++
ÖÐÎÄ
中文
  • UTF->GBK->UTF
String str11 = new String(utf8_bytes,"GBK");

字节数组 utf8_bytes = [e4 b8 ad e6 96 87],按照 GBK 解码,因为 GBK 固定两位解析,所以,解析结果是 涓枃

String str12 = new String(str11.getBytes("GBK"),"UTF-8");

将解析结果涓枃进行 UTF-8 编码,得到字节数组为[e4 b8 ad e6 96 87],再进行 UTF-8 解码,就会得到结果中文。能转换成功根本原因在于:UTF-8的中文编码位数大于GBK

  • UTF->ISO->UTF
    同理,UTF->ISO->UTF 也能成功。

  • GBK->UTF->GBK

String str31 = new String(gbk_bytes,"UTF-8");

字节数组gbk_bytes = [d6 d0 ce c4],按照 UTF-8 解码,因为 UTF-8 是变长字节编码,解析会失败,解析结果是 ����

String str32 = new String(str31.getBytes("UTF-8"),"GBK");

将解析结果 ���� 进行 UTF-8 编码,解析结果未知,故 GBK 编码也是乱码。

  • GBK->ISO->GBK
    因为 GBK 比 ISO 的字节编码位数大,所以会成功。

结论:

  • UTF-8 编码可以用 UTF-8/GBK/ISO8859-1解码后再编码回去
  • GBK 编码后只能用 GBK/ISO8859-1 解码后再编码回去

setCharacterEncoding()

该函数用来设置 http请求或者相应的编码

对于 request,是指提交内容的编码,指定后可以通过getParameter()则直接获得正确的字符串,如果不指定,则默认使用iso8859-1编码,需要进一步处理

值得注意的是在执行setCharacterEncoding()之前,不能执行任何getParameter()。而且,该指定只对POST方法有效,对GET方法无效。分析原因,应该是在执行第一个getParameter()的时候,java将会按照编码分析所有的提交内容,而后续的getParameter()不再进行分析,所以setCharacterEncoding()无效。而对于 GET 方法提交表单是,提交的内容在 URL 中,一开始就已经按照编码分析所有的提交内容,setCharacterEncoding()自然就无效。

解决在JSP页面获取表单的值时会出现乱码

原因:iso-8859-1 是JAVA网络传输使用的标准字符集,而gb2312是标准中文字符集,当你作出提交表单等需要网络传输的操作的时候,就需要把 iso-8859-1 转换为 gb2312 字符集显示,否则如果按浏览器的 gb2312 格式来解释 iso-8859-1 字符集的话,由于两者不兼容,所以会是乱码。

有两种解决转换方法:

  • 一种是在调用getParameter()之前通过 request.setCharacterEncoding 设置字符编码。
  • 另一种是调用new String(str.getBytes("iso8859-1"), "UTF-8"); 编码后解码

获取文件编码格式

使用第三方包 detector

public static String getFileCharsetName(File file) throws Exception {
    CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
	
	/*ParsingDetector可用于检查HTML、XML等文件或字符流的编码,
	 * 构造方法中的参数用于指示是否显示探测过程的详细信息,为false不显示。
    */
    detector.add(new ParsingDetector(false));
	
	/*JChardetFacade封装了由Mozilla组织提供的JChardet,它可以完成大多数文件的编码测定。
	 * 所以,一般有了这个探测器就可满足大多数项目的要求,如果你还不放心,可以再多加几个探测器,
	 * 比如下面的ASCIIDetector、UnicodeDetector等。
    */
    detector.add(JChardetFacade.getInstance());
    detector.add(ASCIIDetector.getInstance());
    detector.add(UnicodeDetector.getInstance());
    Charset charset = null;
    try {
        charset = detector.detectCodepage(file.toURI().toURL());
    } catch (Exception e) {
        e.printStackTrace();
        throw e;
    }
    String charsetName = "GBK";
    if (charset != null) {
        if (charset.name().equals("US-ASCII")) {
            charsetName = "ISO_8859_1";
        } else if (charset.name().startsWith("UTF")) {
            charsetName = charset.name();// 例如:UTF-8,UTF-16BE.
        }else if(charset.name().equals("GB2312")){
            charsetName="GBK";
        }
    }
    return charsetName;//返回最终的编码格式
}

你可能感兴趣的:(Java,java)