javaweb/mysql数据库乱码/io流乱码的原因与解决方法

1.编码的产生:我们的存储的基本单元是一个byte字节,然而人类的语言太多,无法用一个基本单元来表示,然而为了拆分或者一些相应的翻译工作,导致了编码的产生

 

2.常见的编码格式

ASCll :用一个字节的低7位共128个来表示,0-31表示控制字符,32-126表示打印字符, ASCll的二进制第一位也就是最高位,目的是用来做奇偶检验的,所谓奇偶校验,是指在代码传送过程中用来检验是否出现错误的一种方法,一般分奇校验和偶校验两种。奇校验规定:正确的代码一个字节中1的个数必须是奇数,若非奇数,则在最高位添1;偶校验规定:正确的代码一个字节中1的个数必须是偶数,若非偶数,则在最高位添1;看到这是似乎对奇偶检验还是较为模糊,不大明白,例如字母A的ASCII码为1000001,若使用偶校验的结果是:01000001,奇检验的结果是11000001;奇偶检验是事先设定好了的,当CPU读取存储的数据时,它会再次把前8位中存储的数据相加,计算结果是否与校验位相一致。从而一定程度上能检测出内存错误,奇偶校验只能检测出错误而无法对其进行修正,同时虽然双位同时发生错误的概率相当低,但奇偶校验却无法检测出双位错,检验到错误的时候,接收数据方就会要求发送方重新传数据

范例:

串行数据在传输过程中,由于干扰,可能使位变为1,(为什么不变0?脉冲)这种情况,称为出现了"误码"。传输中的错误,叫"检错"。发现错误后消除错误叫"纠错"。最简单的检错方法是"奇偶校验",一般在同步传输方式中常采用奇校验,而在异步传输方式中常采用偶校验

ISO-8859-1:由于128字符不够用,于是ISO组织在ASCll的基础上制定一系列的标准来拓展ASCll编码(ISO-8859-1~ISO-8859-15),ISO-8859-1也是作为java web中默认的解析方式

GB2312:全称是《信息交换用汉字编码字符奇基本集》,占两个字节,其中A1~A9是符号区,B0~F7是汉字区

GBK:全称《汉字内码拓展规范》,是对GB2312的拓展,它的编码是与GB2312相兼容的,也就是说用GB2312编码的汉字可以用GBK来编码而不会乱码

GB18030:我国强制标准,可能是单字节 双字节 四个字节,与GB2312编码是兼容的,我国标准,但是使用并不广泛

UTF-16:定义了Unicode(世界上所有的语言都可以用这本字典来相互翻译,是java和xml的基础)在计算机中的存取方法,用两字节来表示Unicode的转换格式,在表示上相对来说简单方便,但是相对于UTF-8来内存空间增大了很多

UTF-8:对每个编码区域有不同的字码长度,不同类型字符可以有1-6个字节组成

UTF-8的编码原则:

如果是一个字节,最高位为0,

如果一个字节以11开头,表示相应的首字节

如果以10开始,则表示他不是首字节,需要向前查找才能得到当前字符的首字符

例如:

1字节 0xxxxxxx

2字节 110xxxxx 10xxxxxx

3字节 1110xxxx 10xxxxxx 10xxxxxx

4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

说明:UTF-8用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。 将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数(ASCll标准编码为7位)。

 

3.本地io流的编解码

1)乱码的原因

文件的传输时使用二进制数传输(备注:字符的显示是经过相应的转换之后得到的结果),在转换成二进制的过程中具有编码和解码两个过程,编码与解码一致那么就不会乱码,假如不一致就会乱码。例如传入的文件java默认使用的是gbk解码,而我们原本使用的是utf8编码,那么在解码的时候会相应的乱码。

2)解决方法

a)在知道原文件的编码的情况下

    InputStreamReader isr = new InputStreamReader(new FileInputStream(""),"utf-8");

b)在不知道编码的情况下获取本地文件的编码方法如下

     //简写EncodingDetect的使用方法
     String pathname = "F:/test/file1/file2.txt"; //获取编码格式 String encode = EncodingDetect.getJavaEncode(pathname);
     System.
out.println(encode);     
EncodingDetect.readFile(pathname, encode);

c)在不知道编码的情况下获取网络文件的编码方法如下

public static String getFileEncode(String path) {

        /*
         * detector是探测器,它把探测任务交给具体的探测实现类的实例完成。
         * cpDetector内置了一些常用的探测实现类,这些探测实现类的实例可以通过add方法 加进来,如ParsingDetector、
         * JChardetFacade、ASCIIDetector、UnicodeDetector。
         * detector按照“谁最先返回非空的探测结果,就以该结果为准”的原则返回探测到的
         * 字符集编码。使用需要用到三个第三方JAR包:antlr.jar、chardet.jar和cpdetector.jar
         * cpDetector是基于统计学原理的,不保证完全正确。
         */
        CodepageDetectorProxy detector = CodepageDetectorProxy.getInstance();
        /*
         * ParsingDetector可用于检查HTML、XML等文件或字符流的编码,构造方法中的参数用于
         * 指示是否显示探测过程的详细信息,为false不显示。
         */
         detector.add(new ParsingDetector(false));
        /*
         * JChardetFacade封装了由Mozilla组织提供的JChardet,它可以完成大多数文件的编码
         * 测定。所以,一般有了这个探测器就可满足大多数项目的要求,如果你还不放心,可以
         * 再多加几个探测器,比如下面的ASCIIDetector、UnicodeDetector等。
         */
        detector.add(JChardetFacade.getInstance());// 用到antlr.jar、chardet.jar
        // ASCIIDetector用于ASCII编码测定
        // detector.add(ASCIIDetector.getInstance());
        // UnicodeDetector用于Unicode家族编码的测定
        // detector.add(UnicodeDetector.getInstance());
        java.nio.charset.Charset charset = null;
          URL url=null;
        try {
            url = new  URL(path);
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        try {
            charset = detector.detectCodepage(url);
             
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        if (charset != null)
            return charset.name();
        else
            return null;
    }; 

备注:本地文件可以使用EncodingDetect  jar包来获取文件的编码,利用统计学得出的结论,并不是百分百正确,但是准确率在百分之99以上,网络爬虫可以使用cpdetector_1.0.10.jar*,基于统计学。

 

4.mysql数据库乱码

解决方法:如下

useUnicode=true&characterEncoding=utf8&characterSetResults=utf8  //需要与数据的编码集一致,否则可能插入出错(例如数据库是latin1,而我们设置的utf-8),数据库的字符集可以设置也可以查看。(mysql数据库中)

含义如下:本质:useUnicode=true&characterEncoding=utf- 8的作用是指定character_set_client和character_set_connection的值,characterSetResults=utf8对应character_set_results

 

5.web乱码

1)post请求方式乱码

原因:默认使用的iso-8859-1解析,导致乱码//客户端需要与服务器端保持编解码一致

解决方法:采用request.setcharacter(“utf-8”)(request.setCharacterEncoding()是在接受数据之后进行相应的解码),或者是response.setcharacter(“utf-8”),也可是以response.setContentType("text/json; charset=UTF-8")来解决,可以使用拦截器拦截设置编码;

a)基于配置的springframework内置拦截器如下,需要放在所有的拦截的最前面,防止在正确解码前以及被解码导致乱码

  <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>forceResponseEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

b)在spring中不基于配置的方式配置拦截器如下

在类路径下建立meta-inf文件夹,然后新建services,然后建立一个文件(javax.servlet.ServletContainerInitializer),在文件中写下全类名(cn.test.config.MainConfig)

package cn.test.config;

import org.springframework.web.filter.CharacterEncodingFilter;

import javax.servlet.*;
import javax.servlet.annotation.HandlesTypes;
import java.util.EnumSet;
import java.util.Set;

public class MainConfig implements ServletContainerInitializer {

    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
       // characterEncodingFilter添加拦截器
        FilterRegistration.Dynamic characterEncodingFilter = ctx.addFilter("characterEncodingFilter", CharacterEncodingFilter.class);
        characterEncodingFilter.setInitParameter("encoding","utf-8");
        characterEncodingFilter.setInitParameter("forceRequestEncoding", "true");
        characterEncodingFilter.setInitParameter("forceResponseEncoding","true");
        characterEncodingFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST),true,"/*");
    }
}

 

2)get请求方式乱码(post的解决方法对get无效)

原因:对于get方法表单提交的内容在URL解析开始时就已经解码,所以setCharacterEncoding()无效。

解决方法:

在Tomcat的server.xml中的:

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"     URIEncoding="GBK" />

posted @ 2018-07-21 14:19  fatale  阅读(929)  评论(0编辑  收藏  举报