>  기사  >  Java  >  Java 웹에서 중국어 인코딩 문제 분석

Java 웹에서 중국어 인코딩 문제 분석

怪我咯
怪我咯원래의
2017-04-10 10:32:531756검색

배경:

코딩 문제는 항상 프로그램 개발자를 괴롭혀 왔습니다. 특히 Java에서는 Java가 크로스 플랫폼 언어이고 다른 플랫폼에서 코딩 간에 전환이 많이 발생하기 때문입니다. 다음으로 Java 인코딩 문제의 근본 원인, Java에서 자주 발생하는 여러 인코딩 형식의 차이점, Java 웹 개발 시 발생할 수 있는 인코딩 문제의 원인 분석을 소개합니다. HTTP 요청의 인코딩 형식을 제어하는 ​​방법, 중국어 인코딩 문제를 방지하는 방법 등

1. 몇 가지 일반적인 인코딩 형식

1.1 인코딩을 해야 하는 이유

  • 컴퓨터에서 정보를 저장하는 최소 단위는 1바이트, 즉 8비트이므로 표현할 수 있는 문자의 범위는 0~255이다.

  • 표현할 기호가 너무 많아 1바이트로 다 표현할 수 없습니다.

1.2 번역 방법

컴퓨터는 다양한 번역 방법을 제공하며 일반적인 방법으로는 ASCII, ISO-8859-1, GB2312, GBK, UTF-8, UTF-16 등이 있습니다. 이것들은 모두 변환 규칙을 규정하고 있습니다. 이 규칙에 따라 컴퓨터는 우리의 문자를 정확하게 표현할 수 있습니다. 이러한 인코딩 형식은 아래에 소개되어 있습니다.

  • ASCII 코드

    총 128개가 있는데 1바이트의 하위 7비트로 표현되며, 0~31은 줄 바꿈, 캐리지 리턴, 삭제 등의 제어 문자이고, 32~126은 키보드를 통해 입력할 수 있는 인쇄 문자이고, 표시될 수 있습니다.

  • ISO-8859-1


  • 128자는 분명히 충분하지 않으므로 ISO 조직은 ASCII를 기반으로 확장되었습니다. ISO-8859-1은 ISO-8859-15로 대부분의 문자를 포함하며 가장 널리 사용됩니다. ISO-8859-1은 여전히 ​​단일 바이트 인코딩으로 총 256자를 나타낼 수 있습니다.

  • GB2312


  • 더블바이트 인코딩이며 전체 인코딩 범위는 A1 ~ F7입니다. 여기서 A1 ~ A9는 기호 영역으로 총 682개의 기호가 포함되어 있으며 B0 ~ F7은 한자 영역으로 6763개의 한자가 포함되어 있습니다.

  • GBk


  • GBK는 GB2312의 확장인 "한자 내부 코드 확장 사양"입니다. 인코딩 범위는 8140 ~ FEFE(XX7F 제외)이며 총 21003개의 한자를 나타낼 수 있습니다. GB2312 인코딩을 사용하며 잘못된 문자가 없습니다.

  • UTF-16


  • 이는 컴퓨터에서 유니코드 문자에 액세스하는 방법을 구체적으로 정의합니다. UTF-16은 유니코드 변환 형식을 표현하기 위해 2바이트를 사용합니다. 즉, 어떤 문자든 2바이트로 표현합니다. 2바이트는 16비트이므로 UTF-16이라고 합니다. 문자를 나타내는 것이 매우 편리합니다. 2바이트가 하나의 문자를 나타내지 않으므로 문자열 작업이 크게 단순화됩니다.

  • UTF-8


  • UTF-16에서는 2바이트를 일률적으로 사용하여 문자를 표현하는 것이 간단하고 편리하지만, 문자의 대부분을 1바이트로 표현하면 저장 공간이 두 배가 됩니다. 네트워크 대역폭이 제한되면 문제가 발생합니다. 이 경우 네트워크 전송 트래픽이 증가합니다. UTF-8은 가변 길이 기술을 사용합니다. 각 인코딩 영역은 서로 다른 문자 길이를 가지며 1~6바이트까지 다양한 문자를 구성할 수 있습니다.

    UTF-8에는 다음과 같은 인코딩 규칙이 있습니다.

    • 1바이트라면 최상위 비트(8번째 비트)가 0으로, 이는 ASCII 문자(00~7F)임을 의미합니다.

    • 1바이트라면 시작 11이면 연속된 1의 개수는 이 문자의 바이트 수를 나타냅니다.

    • 1바이트인 경우 10으로 시작하는 것이 그렇지 않은 경우 첫 번째 바이트, 현재 문자의 첫 번째 바이트를 얻으려면 앞으로 기다려야 합니다.


2. Java 코딩이 필요한 시나리오

​2.1 I/O 작업에 존재하는 인코딩

위에 표시된 대로 Reader 클래스는 Java I/O에서 문자를 읽는 상위 클래스이고, InputStream 클래스는 바이트를 읽는 상위 클래스입니다. I/O 프로세스 중에는 읽은 바이트를 문자로 변환하는 작업을 처리하고, 특정 바이트를 문자로 디코딩하는 작업을 StreamDecoder에 맡깁니다. StreamDecoder의 디코딩 프로세스 중에 Charset 인코딩 형식은 사용자가 지정해야 합니다. Charset을 지정하지 않으면 로컬 환경의 기본 문자 집합이 사용됩니다. 예를 들어 중국어 환경에서는 GBK 인코딩이 사용됩니다.

예를 들어, 다음 코드는 파일 읽기 및 쓰기 기능을 구현합니다.

 String file = "c:/stream.txt"; 
 String charset = "UTF-8"; 
 // 写字符换转成字节流
 FileOutputStream outputStream = new FileOutputStream(file); 
 OutputStreamWriter writer = new OutputStreamWriter( 
 outputStream, charset); 
 try { 
    writer.write("这是要保存的中文字符"); 
 } finally { 
    writer.close(); 
 } 
 // 读取字节转换成字符
 FileInputStream inputStream = new FileInputStream(file); 
 InputStreamReader reader = new InputStreamReader( 
 inputStream, charset); 
 StringBuffer buffer = new StringBuffer(); 
 char[] buf = new char[64]; 
 int count = 0; 
 try { 
    while ((count = reader.read(buf)) != -1) { 
        buffer.append(buffer, 0, count); 
    } 
 } finally { 
    reader.close(); 
 }

애플리케이션에 I/O 작업이 포함될 때 통합 인코딩 및 디코딩 Charset 문자 집합을 지정하는 데 주의를 기울이는 한 일반적으로 잘못된 코드 문제는 발생하지 않습니다.

2.2 메모리 연산 코딩

메모리의 문자에서 바이트로 데이터 유형 변환을 수행합니다.

1. String 클래스는 문자열을 바이트로 변환하는 메서드를 제공하고 바이트를 문자열로 변환하는 생성자도 지원합니다.

아아아아

2. Charset은 각각 char[]에서 byte[]로의 인코딩과 byte[]에서 char[]로의 디코딩에 해당하는 인코딩 및 디코딩을 제공합니다.

아아아아

​...

3. 자바로 인코딩하고 디코딩하는 방법

Java 코딩 클래스 다이어그램

먼저 Charset.forName(charsetName)을 통해 지정된 charsetName에 따라 Charset 클래스를 설정한 다음 Charset에 따라 CharsetEncoder 객체를 생성한 다음 CharsetEncoder.encode를 호출하여 문자열을 인코딩합니다. 실제 인코딩은 서로 다릅니다. 프로세스는 이 클래스에서 수행됩니다. 다음은 String.getBytes(charsetName) 인코딩 과정의 타이밍 다이어그램입니다

Java 코딩 시퀀스 다이어그램

위 그림에서 볼 수 있듯이 charsetName을 기반으로 Charset 클래스를 찾은 다음 이 문자 세트 인코딩을 기반으로 CharsetEncoder가 생성됩니다. 이 클래스는 모든 문자 인코딩 세트의 상위 클래스입니다. 하위 클래스에서 인코딩을 구현하려면 CharsetEncoder 객체를 얻은 후 encode 메서드를 호출하여 인코딩을 구현할 수 있습니다. 이것이 String.getBytes 인코딩 방식이며 StreamEncoder 등 다른 방식도 유사합니다.

중국어 문자가 "?"로 표시되는 경우가 종종 있는데, 이는 아마도 ISO-8859-1 인코딩을 잘못 사용했기 때문일 수 있습니다. ISO-8859-1 인코딩 후에는 한자가 정보를 잃게 됩니다. 우리는 이를 일반적으로 알 수 없는 문자를 흡수하는 "블랙홀"이라고 부릅니다. 대부분의 기본 Java 프레임워크 또는 시스템의 기본 문자 집합 인코딩은 ISO-8859-1이므로 잘못된 문자가 발생하기 쉽습니다. 나중에 다양한 형태의 문자가 어떻게 나타나는지 분석하겠습니다.

여러 인코딩 형식 비교

GB2312와 GBK의 인코딩 규칙은 유사하지만 GBK는 범위가 더 넓고 모든 중국어 문자를 처리할 수 있으므로 GB2312를 GBK와 비교할 때 GBK를 선택해야 합니다. UTF-16과 UTF-8은 모두 유니코드 인코딩을 처리하며 인코딩 규칙은 동일하지 않습니다. 상대적으로 말하면 UTF-16 인코딩이 가장 효율적이고 문자를 바이트로 변환하는 것이 더 쉽고 문자열을 수행하는 것이 더 좋습니다. 운영. 로컬 디스크와 메모리 간 사용에 적합하며 문자와 바이트 간을 빠르게 전환할 수 있습니다. 예를 들어 Java의 메모리 인코딩은 UTF-16 인코딩을 사용합니다. 그러나 네트워크 전송은 바이트 스트림을 쉽게 손상시킬 수 있으므로 네트워크 간 전송에는 적합하지 않습니다. 바이트 스트림이 손상되면 복구가 어렵습니다. 이에 비해 UTF-8은 네트워크 전송에 더 적합하며 단일을 사용합니다. -ASCII 문자에 대한 바이트 저장 또한 단일 문자의 손상은 다른 후속 문자에 영향을 미치지 않습니다. 따라서 UTF-8은 인코딩 효율성과 인코딩 보안의 균형을 유지하며 이상적인 중국어 인코딩입니다. 방법.

4. 자바 웹에 관련된 코덱

I/O가 있는 중국어의 경우 인코딩이 관련됩니다. 앞서 언급한 것처럼 I/O 작업으로 인해 인코딩이 발생하며, I/O로 인해 발생하는 대부분의 잘못된 코드는 네트워크 I/O입니다. 네트워크 작업이 필요하며 네트워크를 통해 전송되는 데이터는 바이트 단위이므로 모든 데이터는 바이트로 직렬화되어야 합니다. Java에서 직렬화할 데이터는 직렬화 가능 인터페이스를 상속해야 합니다.

텍스트 조각의 실제 크기를 어떻게 계산해야 합니까? 한 번 문제에 직면했습니다. 쿠키 크기를 압축하고 네트워크 전송량을 줄이는 방법을 찾고 싶었는데, 그 당시 다른 압축 알고리즘을 선택했고 그 결과를 발견했습니다. 압축 후 문자 수는 줄어들었지만 바이트 수는 줄어들지 않았습니다. 소위 압축은 인코딩을 통해 여러 개의 단일 바이트 문자를 하나의 다중 바이트 문자로 변환하는 것뿐입니다. 감소된 것은 String.length()이지만 최종 바이트 수는 아닙니다. 예를 들어, 두 문자 "ab"는 일부 인코딩을 통해 이상한 문자로 변환됩니다. 문자 수는 2에서 1로 변경되지만, 이 이상한 문자를 UTF-8로 인코딩하면 인코딩 후에 3이 되거나 3이 될 수 있습니다. 더 많은 바이트. 같은 이유로 예를 들어 정수 1234567을 문자로 저장하면 UTF-8을 사용하여 인코딩하려면 7바이트가 필요하고, UTF-16을 사용하여 인코딩하려면 14바이트가 필요하지만 저장할 정수 바이트로 저장하려면 4가 필요합니다. 따라서 텍스트 조각의 크기나 문자 자체의 길이를 보는 것은 의미가 없습니다. 같은 문자를 다른 인코딩으로 저장하더라도 최종 저장 크기가 달라지므로 인코딩 유형을 살펴봐야 합니다. 문자에서 바이트로.

  我们能够看到的汉字都是以字符形式出现的,例如在 Java 中“淘宝”两个字符,它在计算机中的数值 10 进制是 28120 和 23453,16 进制是 6bd8 和 5d9d,也就是这两个字符是由这两个数字唯一表示的。Java 中一个 char 是 16 个 bit 相当于两个字节,所以两个汉字用 char 表示在内存中占用相当于四个字节的空间。

  这两个问题搞清楚后,我们看一下 Java Web 中那些地方可能会存在编码转换?

  用户从浏览器端发起一个 HTTP 请求,需要存在编码的地方是 URL、Cookie、Parameter。服务器端接受到 HTTP 请求后要解析 HTTP 协议,其中 URI、Cookie 和 POST 表单参数需要解码,服务器端可能还需要读取数据库中的数据,本地或网络中其它地方的文本文件,这些数据都可能存在编码问题,当 Servlet 处理完所有请求的数据后,需要将这些数据再编码通过 Socket 发送到用户请求的浏览器里,再经过浏览器解码成为文本。这些过程如下图所示:

  一次 HTTP 请求的编码示例

  4.1 URL 的编解码

  用户提交一个 URL,这个 URL 中可能存在中文,因此需要编码,如何对这个 URL 进行编码?根据什么规则来编码?有如何来解码?如下图一个 URL:

  上图中以 Tomcat 作为 Servlet Engine 为例,它们分别对应到下面这些配置文件中:

  Port 对应在 Tomcat 的 2d40a7d47a79d09f1e13ed7afa569ba8 中配置,而 Context Path 在 cc9811071a6b9ec4ee8fd6740dade5e9 中配置,Servlet Path 在 Web 应用的 web.xml 中的

<servlet-mapping> 
        <servlet-name>junshanExample</servlet-name> 
        <url-pattern>/servlets/servlet/*</url-pattern> 
 </servlet-mapping>

  66e1775cbd9d5002635ae3285442ba88 中配置,PathInfo 是我们请求的具体的 Servlet,QueryString 是要传递的参数,注意这里是在浏览器里直接输入 URL 所以是通过 Get 方法请求的,如果是 POST 方法请求的话,QueryString 将通过表单方式提交到服务器端。

  上图中 PathInfo 和 QueryString 出现了中文,当我们在浏览器中直接输入这个 URL 时,在浏览器端和服务端会如何编码和解析这个 URL 呢?为了验证浏览器是怎么编码 URL 的我选择的是360极速浏览器并通过 Postman 插件观察我们请求的 URL 的实际的内容,以下是 URL:

  HTTP://localhost:8080/examples/servlets/servlet/君山?author=君山

  君山的编码结果是:e5 90 9b e5 b1 b1,和《深入分析 Java Web 技术内幕》中的结果不一样,这是因为我使用的浏览器和插件和原作者是有区别的,那么这些浏览器之间的默认编码是不一样的,原文中的结果是:

 

  君山的编码结果分别是:e5 90 9b e5 b1 b1,be fd c9 bd,查阅上一届的编码可知,PathInfo 是 UTF-8 编码而 QueryString 是经过 GBK 编码,至于为什么会有“%”?查阅 URL 的编码规范 RFC3986 可知浏览器编码 URL 是将非 ASCII 字符按照某种编码格式编码成 16 进制数字然后将每个 16 进制表示的字节前加上“%”,所以最终的 URL 就成了上图的格式了。

  从上面测试结果可知浏览器对 PathInfo 和 QueryString 的编码是不一样的,不同浏览器对 PathInfo 也可能不一样,这就对服务器的解码造成很大的困难,下面我们以 Tomcat 为例看一下,Tomcat 接受到这个 URL 是如何解码的。

  解析请求的 URL 是在 org.apache.coyote.HTTP11.InternalInputBuffer 的 parseRequestLine 方法中,这个方法把传过来的 URL 的 byte[] 设置到 org.apache.coyote.Request 的相应的属性中。这里的 URL 仍然是 byte 格式,转成 char 是在 org.apache.catalina.connector.CoyoteAdapter 的 convertURI 方法中完成的:

protected void convertURI(MessageBytes uri, Request request) 
 throws Exception { 
        ByteChunk bc = uri.getByteChunk(); 
        int length = bc.getLength(); 
        CharChunk cc = uri.getCharChunk(); 
        cc.allocate(length, -1); 
        String enc = connector.getURIEncoding(); 
        if (enc != null) { 
            B2CConverter conv = request.getURIConverter(); 
            try { 
                if (conv == null) { 
                    conv = new B2CConverter(enc); 
                    request.setURIConverter(conv); 
                } 
            } catch (IOException e) {...} 
            if (conv != null) { 
                try { 
                    conv.convert(bc, cc, cc.getBuffer().length - 
 cc.getEnd()); 
                    uri.setChars(cc.getBuffer(), cc.getStart(), 
 cc.getLength()); 
                    return; 
                } catch (IOException e) {...} 
            } 
        } 
        // Default encoding: fast conversion 
        byte[] bbuf = bc.getBuffer(); 
        char[] cbuf = cc.getBuffer(); 
        int start = bc.getStart(); 
        for (int i = 0; i < length; i++) { 
            cbuf[i] = (char) (bbuf[i + start] & 0xff); 
        } 
        uri.setChars(cbuf, 0, length); 
 }

  从上面的代码中可以知道对 URL 的 URI 部分进行解码的字符集是在 connector 的 fb5e5a423fbf569b89d2849dcbd1c1d3 中定义的,如果没有定义,那么将以默认编码 ISO-8859-1 解析。所以如果有中文 URL 时最好把 URIEncoding 设置成 UTF-8 编码。

QueryString을 구문 분석하는 방법은 무엇입니까? GET HTTP 요청의 QueryString과 POST HTTP 요청의 form 매개변수는 Parameter로 저장되며, request.getParameter를 통해 매개변수 값을 얻습니다. request.getParameter 메소드가 처음 호출될 때 디코딩됩니다. request.getParameter 메소드가 호출되면 org.apache.catalina.connector.Request의parseParameters 메소드를 호출합니다. 이 메소드는 GET 및 POST에 의해 전달된 매개변수를 디코딩하지만 해당 디코딩 문자 세트는 다를 수 있습니다. POST 형식의 디코딩은 나중에 소개됩니다. QueryString의 디코딩 문자 집합은 어디에 정의되어 있습니까? HTTP 헤더를 통해 서버로 전송되며 URL에도 포함되어 있습니다. URI의 디코딩 문자 집합과 동일합니까? PathInfo 및 QueryString에 대해 서로 다른 인코딩 형식을 사용하는 이전 브라우저에서 디코딩된 문자 집합이 확실히 일관성이 없을 것이라고 추측할 수 있습니다. 이는 실제로 QueryString의 디코딩 문자 세트가 헤더의 ContentType에 정의된 Charset이거나 ContentType에 정의된 인코딩을 사용하려면 커넥터의 049fccce83ad14370527fa17eead4739 useBodyEncodingForURI가 true로 설정됩니다. 이 구성 항목의 이름은 다소 혼란스럽습니다. 전체 URI를 디코딩하는 데 BodyEncoding을 사용하지 않고 QueryString을 디코딩하는 데만 BodyEncoding을 사용합니다.

위의 URL 인코딩 및 디코딩 프로세스로 판단하면 상대적으로 복잡하며 인코딩 및 디코딩은 애플리케이션에서 완전히 제어할 수 없습니다. 따라서 애플리케이션의 URL에 ASCII가 아닌 문자를 사용하지 않도록 해야 합니다. 매우 어려울 수 있습니다. 물론 서버 측의 5de43e088bab17594573baec6d1f4ce2에서 두 매개변수 URIEncoding 및 useBodyEncodingForURI를 설정하는 것이 가장 좋습니다.

4.2 HTTP 헤더 인코딩 및 디코딩

클라이언트가 HTTP 요청을 시작할 때 위의 URL 외에도 헤더에 Cookie, RedirectPath 등과 같은 다른 매개변수를 전달할 수도 있습니다. 이러한 사용자 설정 값에는 Tomcat이 이를 어떻게 디코딩하는지에 대한 인코딩 문제가 있을 수도 있습니다. ?

요청된 Header 항목이 디코딩되지 않으면 MessageBytes의 toString 메소드가 호출되어 바이트에서 문자로 변환되는 기본 인코딩도 ISO-8859입니다. -1. 헤더의 다른 디코딩 형식을 설정할 수 없으므로 헤더에서 ASCII가 아닌 문자의 디코딩을 설정하면 확실히 문자가 깨집니다.

헤더를 추가할 때도 마찬가지입니다. 헤더에 ASCII가 아닌 문자를 전달하지 마십시오. 이를 전달해야 하는 경우 먼저 이러한 문자를 org.apache.catalina.util.URLEncoder로 인코딩한 다음 헤더에 추가할 수 있습니다. 이렇게 하면 브라우저에서 서버로 전송되는 동안 정보가 손실되지 않습니다. 이러한 항목에 액세스하려고 할 때 해당 문자 집합에 따라 정보를 디코딩하면 좋을 것입니다.

4.3 POST 형식의 코딩 및 디코딩

앞서 언급했듯이 POST 폼에서 제출한 매개변수의 디코딩은 request.getParameter가 처음 호출될 때 발생합니다. POST 폼 매개변수 전송 방법은 HTTP의 BODY를 통해 서버에 전달됩니다. 페이지에서 제출 버튼을 클릭하면 브라우저는 먼저 ContentType의 Charset 인코딩 형식에 따라 양식에 채워진 매개변수를 인코딩한 다음 서버에 제출합니다. 또한 서버는 디코딩을 위해 ContentType의 문자 세트를 사용합니다. 따라서 일반적으로 POST 형식을 통해 제출된 매개변수에는 문제가 없으며 문자셋 인코딩은 자체적으로 설정하며 request.setCharacterEncoding(charset)을 통해 설정할 수 있다.

또한 multipart/form-data 유형 매개변수의 경우, 즉 업로드된 파일 인코딩도 ContentType에서 정의한 문자 세트 인코딩을 사용합니다. 업로드된 파일은 서버의 로컬 임시 디렉터리에 바이트 단위로 전송된다는 점에 유의할 필요가 있습니다. 이 프로세스에는 문자 인코딩이 포함되지 않지만 실제 인코딩은 파일 콘텐츠를 매개변수에 추가하는 것입니다. 이 인코딩을 사용하여 인코딩할 수 없는 경우 기본 인코딩 ISO-8859-1이 사용됩니다.

4.4 HTTP BODY 인코딩 및 디코딩

사용자가 요청한 리소스가 성공적으로 획득되면 해당 콘텐츠는 응답을 통해 클라이언트 브라우저로 반환됩니다. 이 프로세스는 먼저 브라우저에서 인코딩된 다음 디코딩되어야 합니다. 이 프로세스의 인코딩 및 디코딩 문자 세트는 response.setCharacterEncoding을 통해 설정할 수 있으며, 이는 request.getCharacterEncoding 값을 재정의하고 브라우저가 반환된 소켓 스트림을 수신하면 헤더의 Content-Type을 통해 클라이언트에 반환됩니다. 반환된 HTTP 헤더의 Content-Type이 문자 세트를 설정하지 않은 경우 브라우저는 83eaeb38979c9c011652cfc0d11b7e9f

  访问数据库都是通过客户端 JDBC 驱动来完成,用 JDBC 来存取数据要和数据的内置编码保持一致,可以通过设置 JDBC URL 来制定如 MySQL:url="jdbc:mysql://localhost:3306/DB?useUnicode=true&characterEncoding=GBK"。

  5、常见问题分析

  下面看一下,当我们碰到一些乱码时,应该怎么处理这些问题?出现乱码问题唯一的原因都是在 char 到 byte 或 byte 到 char 转换中编码和解码的字符集不一致导致的,由于往往一次操作涉及到多次编解码,所以出现乱码时很难查找到底是哪个环节出现了问题,下面就几种常见的现象进行分析。

  5.1 中文变成了看不懂的字符

  例如,字符串“淘!我喜欢!”变成了“Ì Ô £ ¡Î Ò Ï²»¶ £ ¡”编码过程如下图所示:

 

  字符串在解码时所用的字符集与编码字符集不一致导致汉字变成了看不懂的乱码,而且是一个汉字字符变成两个乱码字符。

  5.2 一个汉字变成一个问号

  例如,字符串“淘!我喜欢!”变成了“??????”编码过程如下图所示:

 

  将中文和中文符号经过不支持中文的 ISO-8859-1 编码后,所有字符变成了“?”,这是因为用 ISO-8859-1 进行编解码时遇到不在码值范围内的字符时统一用 3f 表示,这也就是通常所说的“黑洞”,所有 ISO-8859-1 不认识的字符都变成了“?”。

  5.3 一个汉字变成两个问号

  例如,字符串“淘!我喜欢!”变成了“????????????”编码过程如下图所示:

 

  这种情况比较复杂,中文经过多次编码,但是其中有一次编码或者解码不对仍然会出现中文字符变成“?”现象,出现这种情况要仔细查看中间的编码环节,找出出现编码错误的地方。

  5.4 一种不正常的正确编码

  还有一种情况是在我们通过 request.getParameter 获取参数值时,当我们直接调用

  String value = request.getParameter(name); 会出现乱码,但是如果用下面的方式

  String value = String(request.getParameter(name).getBytes(" ISO-8859-1"), "GBK");

  解析时取得的 value 会是正确的汉字字符,这种情况是怎么造成的呢?

  看下如所示:

 

  这种情况是这样的,ISO-8859-1 字符集的编码范围是 0000-00FF,正好和一个字节的编码范围相对应。这种特性保证了使用 ISO-8859-1 进行编码和解码可以保持编码数值“不变”。虽然中文字符在经过网络传输时,被错误地“拆”成了两个欧洲字符,但由于输出时也是用 ISO-8859-1,结果被“拆”开的中文字的两半又被合并在一起,从而又刚好组成了一个正确的汉字。虽然最终能取得正确的汉字,但是还是不建议用这种不正常的方式取得参数值,因为这中间增加了一次额外的编码与解码,这种情况出现乱码时因为 Tomcat 的配置文件中 useBodyEncodingForURI 配置项没有设置为”true”,从而造成第一次解析式用 ISO-8859-1 来解析才造成乱码的。

6. 요약

이 기사에서는 먼저 여러 일반적인 인코딩 형식 간의 차이점을 요약한 다음 중국어를 지원하는 여러 인코딩 형식을 소개하고 사용 시나리오를 비교합니다. 그런 다음 코딩 문제와 관련된 Java 위치와 Java에서 코딩이 지원되는 방식을 소개합니다. 네트워크 I/O를 예로 들어 HTTP 요청에서 인코딩이 존재하는 위치와 Tomcat의 HTTP 프로토콜 분석에 중점을 두고 마지막으로 우리가 일반적으로 직면하는 잘못된 코드 문제의 이유를 분석합니다.

요약하자면, 중국어 문제를 해결하려면 먼저 문자-바이트 인코딩과 바이트-문자 디코딩이 어디서 발생하는지 파악해야 합니다. 가장 일반적인 장소는 데이터를 디스크에 읽고 저장하는 곳이거나 네트워크를 통해 전달되는 데이터입니다. . 전염. 그런 다음 이러한 위치에 대해 이러한 데이터를 작동하는 프레임워크 또는 시스템이 인코딩을 제어하고, 인코딩 형식을 올바르게 설정하고, 소프트웨어 또는 운영 체제 플랫폼의 기본 인코딩 형식을 사용하지 않는 방법을 파악하십시오.


위 내용은 Java 웹에서 중국어 인코딩 문제 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.