【java】字符编码问题

0.常识

  1. Java的内存编码是Unicode,UTF-16
  2. Linux   默认ISO-8859-1
  3. Win32  默认GB2312

  4. 字符集charset:多个字符的集合。例如GB2312是中国国家标准的简体中文字符集。
  5. 字符编码(Character encoding):把字符集中的字符编码(映射)为特定的字节或字节序列的规则,以便文本在存储和在网络中传输。

0.1 常用字符集&字符编码

字符集 字符编码 描述
ASCII ASCII 0x00-0x7F, 单字节/一个字符
ISO-8859-1 ISO-8859-1 0x00-0xFF扩展,字节
GB2312 GB2312

中文简体,字节,兼容

GBK GBK 中文简繁,字节 0x8140~0xFEFE,除xx7F
Unicode UTF-16 字节,0000~FFFF
Unicode UTF-8 变长设计

Unicode

(UTF-16)

UTF-8 字节
0000 - 007F 0xxxxxxx 1
0080 - 07FF 110xxxxx 10xxxxxx 2
0800 - FFFF 1110xxxx 110xxxxx 10xxxxxx 3

ASCII码为单字节,用7位二进制数表示,最高位为0,即00000000-01111111或0x00-0x7F。码表如下

                         

Unicode对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字。

GB系列这一类的中文是双字节,单字节也支持(英文兼容)GBK码表                

1.常见问题

1.1 中文乱码

此处讲的是Java编程过程中遇到的问题,由于Java的内存字符集是Unicode编码,0x4e00 - 0x9fa5范围对应的是中文,在0800 - FFFF第三个范围内,所以中文在

I/O,包含文件读写、数据库读写、网络IO等等,所有的C/S、B/S架构间的通信数据如果包含中文,当客户端和服务端的编解码字符集选用不统一的话都会导致乱码。

1.1.1 文件IO

用文件做byte[]字节流的中转,有重载方法指定字符编码或者使用系统编码,系统编码可以使用参数-D

void fileWriteAndRead(String wcharset, String rcharset) throws Exception {
    String filename = "fileCharset.txt";
    File file = new File(filename);

    String content = "English 中文";
    FileOutputStream out = new FileOutputStream(file);

    OutputStreamWriter writer = new OutputStreamWriter(out, wcharset); //指定字符集
    writer.write(content);
    writer.close();

    // 读取同一个文件,换字符集尝试各种效果
    FileInputStream in = new FileInputStream(file);
    InputStreamReader reader = new InputStreamReader(in, rcharset);
    char[] buf = new char[64];
    StringBuilder sb = new StringBuilder();
    int readCount = 0;
    while ((readCount = reader.read(buf)) > 0) {
        sb.append(buf, 0, readCount);
    }
    reader.close();
    System.out.println("write-charset:"+wcharset+"\t"+content+"\t"+"read-charset:"+rcharset+"\t"+sb.toString());
}
    @Test
    public void diffCharsets() throws Exception{
        String[] charsets = {"ISO-8859-1","GBK","UTF-8"};
        for (String wchar : charsets) {
            for (String rchar : charsets) {
                fileWriteAndRead(wchar, rchar);
            }
        }
    }

由于写入使用的字符集和读取使用的字符集不一致,如从一个gbk编码的文本文件中用utf-8编码解析读取到的字节流会出现很多

做个实验,使用左侧的字符集将String content = "English 中文";这个字符串写入文本,然后用表头的字符集解析,会出先怎样的乱码?

写入encode\读取decode ISO-8859-1 GBK UTF-8
ISO-8859-1 单 English ?? English ?? English ??
GBK 双 English ÖÐÎÄ English 中文 English ����
UTF-8 变(1&3) English 中æ English 涓枃 English 中文

GBK转换成UTF-8再转成GBK:锟斤拷锟斤拷锟斤拷锟叫癸拷锟斤拷

有表格可得这些字符集都是兼容ASCII字符集的 英文不会有乱码的问题,而中文如果编解码使用的字符编码不一样的话会导致乱码。

   

上述例子中,主要通过StreamEncoder进行

如果不指定String charset,会使用默认的编码,取用-D参数file.encoding指定的字符编码,如果lookup没有找到会使用UTF-8

    public static Charset defaultCharset() {
        if (defaultCharset == null) {
            synchronized (Charset.class) {
                String csn = AccessController.doPrivileged(
                    new GetPropertyAction("file.encoding"));
                Charset cs = lookup(csn);
                if (cs != null)
                    defaultCharset = cs;
                else
                    defaultCharset = forName("UTF-8");
            }
        }
        return defaultCharset;
    }

1.1.1 数据库读写

很多人有过类似经历,用mysql客户端连接上mysql服务,执行sql语句,无论是查询还是插入修改,如果sql语句中包含中文,极有可能出现乱码:查询出的数据,插入到表中的字段......

造成乱码的原因就是你ddl创建表结构的时候声明的字符集和你当前的客户端字符集、连接字符集不同,

解决方式

mysql>

set character_set_client=gbk;
set character_set_connection = gbk;
set character_set_results=gbk;

Java 

jdbc_url=jdbc:mysql://ip:port/db_name?useUnicode=true&characterEncoding=gbk

1.1.2 网络IO

如http通信,最常见的是浏览器页面出现乱码,一般通过设置浏览器编码

发布了92 篇原创文章 · 获赞 14 · 访问量 5823

猜你喜欢

转载自blog.csdn.net/sarafina527/article/details/103576149