前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WEB:字符集、编码、乱码 —— 看这篇就够了

WEB:字符集、编码、乱码 —— 看这篇就够了

作者头像
WEBJ2EE
发布2019-07-19 15:08:16
4K0
发布2019-07-19 15:08:16
举报
文章被收录于专栏:WebJ2EEWebJ2EE

1. “联通”怪事

新建一个名为 mytxt.txt 的记事本文件,输入“联通”后,保存并关闭。

再次用记事本打开 mytxt.txt,你会发现:

“联通”乱码了。

2. 字符如何存储与显示?

字符是如何存储的?

存储的是表示字符的“内码”(二进制)。

字符是如何在屏幕上展示的?

  1. 字符的展现离不开字形库(字体)。
  2. 字形库存放的是字符字形以及内码与字形的映射表。

3. 字符集与字符编码

  • 字符集(Charset)是一个系统支持的所有抽象字符的集合。字符是各种文字和符号的总称,包括各国家文字、标点符号、图形符号、数字等。常见字符集有:ASCII字符集、GB2312字符集、BIG5字符集、GB18030字符集、Unicode字符集等。
  • 字符编码(Character Encoding)是一套规则。字符编码就是将字符转换成计算机能识别的二进制串的法则。例如:GBK 字符集可通过查表来完成字符到二进制串的转换。

例如:“联” 的 GBK 编码的二进制表示为 0xC1AA。

3.1. ASCII

ASCII(美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统。它主要用于显示现代英语,是现今最通用的单字节编码系统

  • ASCII字符集:主要包括控制字符(回车键、退格、换行键等);可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
  • ASCII编码:将ASCII字符集转换为计算机可以接受的数字系统的数的规则。使用7位(bits)表示一个字符,共128字符;

3.2. ISO-8859系列

ASCII 是单字节编码系统,但它只用了 7 位,即只能表示 128 个字符。为了表示更多的欧洲常用字符,ISO 组织对 ASCII 进行了扩展,制定了一系列标准扩展 ASCII 码,他们是ISO-8859-1 ~ ISO-8859-16,其中 ISO-8859-1 涵盖了大多数西欧语言字符,应用得最广泛。

  • 它们的全都是单字节编码、且都与 ASCII 编码相兼容;
  • 它们都采用扩充 ASCII 码的形式(即利用 ASCII 没使用的那 128 个字符空间),制定了适用于不同国家和地区的字符集标准。

3.3. BIG5/GB2312/GBK/GB18030

BIG5

  • 通行于中国台湾、中国香港地区的一个繁体字编码方案。
  • 双字节编码,共收录13053个汉字。

世界上 ,沒有一拳解決不了的事,如果有,那就兩拳。 琦玉

GB2312

  • 一个简体中文字符集的中国国家标准。
  • 双字节编码,共收录6763个汉字和682个非汉字图形。
  • 人名、古汉语等方面的罕用字,GB2312不能处理,这导致了后来GBK及GB18030汉字字符集的出现。(例如:GB2312不能表示“喆”、“镕”字)

GBK

  • GB2312的扩充,完全兼容GB2312,并加入对繁体中文、日语、韩语等支持。
  • 双字节编码,共收录了21003个汉字。

GB18030

  • GBK编码的扩充,兼容GBK和GB2312字符集,覆盖中文、日文、朝鲜语和中国少数民族文字。
  • 采用单字节、双字节和四字节三种编码方式,共收录27484个汉字。

3.4. UNICODE与UTF-8

Unicode 是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。它使用4字节的数字来表达每个字母、符号,或者表意文字(ideograph)。每个数字代表唯一的至少在某种语言中使用的符号。

注:Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。UTF-8 则是目前使用最广的一种 Unicode 编码方式。

UTF-8(8-bit Unicode Transformation Format)是一种针对Unicode的可变长度字符编码。UTF-8 用 1 到 4 个字节编码 Unicode 字符。UTF-8的编码规则很简单,只有二条:

  • 对于单字节的符号,字节的第一位设为0,后面 7 位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
  • 对于 n 字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode码。

例:“联” 的 Unicode 编码是 0x8054。由于 0x8054 位于 0x0800-0xFFFF 之间,所以使用 3 字节 UTF-8 转换模板:1110xxxx 10xxxxxx 10xxxxxx。0x8054 的二进制表示是:1000 0000 0101 0100,用这个比特流依次代替模板中的x,得到: 11101000 10000001 10010100,即E8 81 94。

4. “联通”怪事揭秘

“记事本”默认用 GBK 编码保存数据,“联通”两字的GBK编码如下:

巧合的是,“联”的两个字节、“通”的两个字节的起始部分的都是"110"和"10",正好符合 2 字节 UTF8 编码的规律,于是再次打开记事本时,记事本就误认为这是一个用 UTF-8 编码的文件,用 UTF-8 去解析 GBK 的数据,造成了乱码。

5. 编码衍生问题汇总

5.1. BOM 与 UTF-8

1. 什么是 BOM 头?

BOM 头是放在 UTF-8 编码格式文件的头部,占三个字节(0xEF 0xBB 0xBF),用来标识该文件属于UTF-8编码。

注:window记事本在用UTF-8格式保存文件时,会自动加上BOM头。

2. 有什么问题?

有些软件不能正确识别BOM头

比如,Linux/Unix 环境下,不能编译 UTF-8-BOM 格式 Java 文件。

3. 如何去掉 BOM 头?

用软件咯,以Notepad++为例,依次选择【编码】->【转为UTF-8】,保存即可。

5.2. ZipOutputStream 中的编码问题

  • ZipOutputStream 是 Java 生成压缩包最常用的方式。
  • 压缩包中的“路径名”和“文件名”涉及编码问题(即:Entry)。
  • ZipOutputStream 有两大类实现:java.util.zip.ZipOutputStream 和 org.apache.tools.zip.ZipOutputStream。

下面分类说明

1. JDK6 的 java.util.zip.ZipOutputStream

  • JDK6 的 ZipOutputStream,Entry 采用 UTF-8 编码,而且不支持自定义;
  • 一些压缩软件不能正常识别,需要手动调整压缩软件的解析字符集为UTF-8才行;
代码语言:javascript
复制
package encoding;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipDemo {
  public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("d:/webj2ee.zip");
    ZipOutputStream zos = new ZipOutputStream(fos);
    zos.putNextEntry(new ZipEntry("中文路径/中文名称.txt"));
    zos.write("没有什么是一拳解决不了的,如果有,那就两拳。——琦玉".getBytes("UTF-8"));
    zos.closeEntry();
    zos.close();
    fos.close();
  }
}

2. Apache Ant 的 org.apache.tools.zip.ZipOutputStream

  • 在 JDK6 环境中,常用作 java.util.zip.ZipOutputStream 的替代方案。
  • org.apache.tools.zip.ZipOutputStream 支持自定义 Entry 的编码。
代码语言:javascript
复制
package encoding;

import java.io.FileOutputStream;
import java.io.IOException;

import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;


public class ZipDemo {
  public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("d:/webj2ee.zip");
    ZipOutputStream zos = new ZipOutputStream(fos);
    zos.setEncoding("GBK");
    zos.putNextEntry(new ZipEntry("中文路径/中文名称.txt"));
    zos.write("没有什么是一拳解决不了的,如果有,那就两拳。——琦玉".getBytes("UTF-8"));
    zos.closeEntry();
    zos.close();
    fos.close();
  }
}

3. ≥JDK7 的 java.util.zip.ZipOutputStream

  • JDK7 的 java.util.zip.ZipOutputStream 支持自定义 Entry 的编码。
  • 不需要使用 org.apache.tools.zip.ZipOutputStream 了。
代码语言:javascript
复制
package encoding;

import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipDemo {
  public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("d:/webj2ee.zip");
    ZipOutputStream zos = new ZipOutputStream(fos, Charset.forName("UTF-8"));
    zos.putNextEntry(new ZipEntry("中文路径/中文名称.txt"));
    zos.write("没有什么是一拳解决不了的,如果有,那就两拳。——琦玉".getBytes("UTF-8"));
    zos.closeEntry();
    zos.close();
    fos.close();
  }
}

5.3. String.getBytes(charset) 与 new String(bytes, charset)

  • String.getBytes(String charset) 返回字符串在指定 charset 编码下的 byte 数组表示;
  • new String(byte[], charset) 是按照 charset 指定的编码来解析 byte[] 为字符串。

1. 不要想当然认为它们可逆:

代码语言:javascript
复制
public static void main(String[] args) throws UnsupportedEncodingException {
  String r1 = new String("联通".getBytes("ISO8859-1"), "ISO8859-1");
  System.err.println(r1);
}

注:ISO8859-1根本无法表示中文,当然也就无法得到“联通”两字在ISO8859-1中的编码值了,所以再通过new String()还原就更无从谈起了。

2. 下面这种形式常用于网络数据传输:

代码语言:javascript
复制
public static void main(String[] args) throws UnsupportedEncodingException {
  String sent = new String("联通".getBytes("UTF-8"), "ISO8859-1");
  System.out.println(sent);
  String recv = new String(sent.getBytes("ISO8859-1"), "UTF-8");
  System.out.println(recv);
}

5.4. I/O 流与字符集

  • Java 的 I/O 流,分“字节流”与“字符流”;

  • “字符流”与“字节流”转换时,存在编码问题,需要注意;

示例:

代码语言:javascript
复制
package encoding;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class IOs {
  public static void main(String[] args) throws IOException {

    // 1. “字符流” -> “字节流”
    try (FileOutputStream fos = new FileOutputStream("d:/one-punch-man.txt");
        OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");) {
      osw.write("没有什么是一拳解决不了的,如果有,那就两拳。——琦玉");
    } catch (IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }

    // 2. “字节流” -> “字符流”
    try (FileInputStream fis = new FileInputStream("d:/one-punch-man.txt");
        InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
        BufferedReader br = new BufferedReader(isr);) {
      String l = br.readLine();
      System.out.println(l);
    } catch (IOException e) {
      throw new RuntimeException(e.getMessage(), e);
    }
  }
}

5.5. JVM 默认字符集

1. 有一些接口与 JVM 默认字符集有关:

  • String.getBytes()、new String(byte[]);
  • new OutputStreamWriter(OutputStream out);
  • new InputStreamReader(InputStream in)
  • new FileWriter(...)
  • URLEncoder.encode(String)

2. JVM 默认字符集如何确定?

先上一段JDK源码

  • 不主动配置 -Dfile.encoding 的情况下,默认是操作系统的编码;
  • 配置 JVM 启动参数 -Dfile.encoding,可更改 JVM 默认字符集编码:
  • JVM 启动后, 修改 file.encoding 只会修改配置项值,不会改变 JVM 默认字符集编码;
代码语言:javascript
复制
java -Dfile.encoding=utf-8 ...

5.6. Eclipse - Console 中的编码


不慌,

轮到“前端”的编码问题了

5.7. 浏览器如何解析 HTML 文件?

解析策略:

万能用例代码:

代码语言:javascript
复制
protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    StringBuffer sb = new StringBuffer();
    sb.append("\r\n");
    sb.append("<!DOCTYPE html><html><head>\r\n");
    sb.append("  <meta charset=\"gbk\">\r\n");
    sb.append("  <title></title>\r\n");
    sb.append("</head><body>\r\n");
    sb.append("\t没有什么是一拳解决不了的,如果有,那就两拳。——琦玉\r\n");
    sb.append("</body></html>");
    String html = sb.toString();

    // HTML类型
    response.setContentType("text/html");

    // BOM头
    response.getOutputStream().write(0xEF);
    response.getOutputStream().write(0xBB);
    response.getOutputStream().write(0xBF);
    response.getOutputStream().write(html.getBytes("UTF-8"));
  }

例1: 没有BOM头、没有content-type,按<meta chaset>解析

例2: 有BOM头,按UTF-8解析

例3: 没BOM头,有content-type,按content-type解析

5.8. 浏览器如何解析外部 JS 文件?

通过<script>标记引入外部 JS:

<script charset="UTF-8" src="webj2ee.js">

解析逻辑如下:

例1:没有BOM头、没有content-type,有charset声明;

例2:有 BOM 头;

例3:没有BOM头、有content-type;

5.9. URL编码、解码函数?

5.9.1. escape()、unescape()

escape() 是将字符转换为Unicode编码值。解码是通过unescape()函数。

  1. ISO 8859-1字符集内的字母(A-Z、a-z)、数字(0-9)、标点符号(* @ - _ + . /)不会被转换;
  2. ISO 8859-1字符集内其它字符,都会以%xy格式表示(xy为字符的16进制表示,因为ISO 8859-1是单字节编码,所以2位就够了);
  3. 其它字符(比如中文),转换成%uxxxx的格式(xxxx为字符的16进制 Unicode表示)。

注意:ECMAScript v3 标准不建议使用escape()处理URL编码。应该使用encodeURI和encodeURIComponent()来代替

5.9.2. encodeURI()

encodeURI()是将字符串进行UTF-8编码。解码通过decodeURI()。

  1. ISO8859-1字符集内的字母(A-Z、a-z)、数字(0-9)、标点符号( - _ . ! ~ * ' ( ) )不会被转换。而且该方法的目的是对 URI 进行完整的编码,因此对以下在 URI 中具有特殊含义的 ASCII 标点符号( ; / ? : @ & = + $ , # )也不会被转换。
  2. ISO 8859-1字符集内其它字符,都会以%xy格式表示(xy为字节的16进制表示);
  3. 其它字符首先会按照UTF-8规则转换为字节串,每个字节再以%xy的形式表示。其中xy就是字节的16进制表示形式。

5.9.3. encodeURIComponent()

encodeURIComponent()也是将字符串进行UTF-8编码,它比encodeURI的编码还要彻底,在encodeURI的基础上,将那些在URI中有特殊含义的标点符号也一起编码了。

  1. ISO8859-1字符集内的字母(A-Z、a-z)、数字(0-9)、标点符号( - _ . ! ~ * ' ( ) )不会被转换。
  2. ISO 8859-1字符集内其它字符,都会以%xy格式表示(xy为字符的16进制表示);
  3. 其它字符首先会按照UTF-8规则转换为字节串,每个字节再以%xy的形式表示。其中xy就是字节的16进制表示形式。

注意:这个函数通常用于将一个URL当做一个参数放在另一个URL中。

5.9.4. URLEncoder.encode(charset)

Java 端的URL编码类,解码类为 java.net.URLDecoder。

  1. ISO8859-1字符集内的字母(A-Z、a-z)、数字(0-9)、标点符号( - _ . *)不会被转换。
  2. 空格会被转换为“+”。
  3. 其它字符首先会按照某种编码规则转换为1个或多个字节,每个字节再以%xy的形式表示。其中xy就是字节的16进制表示形式。

注意:建议使用 UTF-8 字符集进行编码。

注意:基本等同于 JS 的 encodeURIComponent 函数(主要是标点、空格不同)。

5.10. Http Header 中的编码

Http 的 Header 中传递的内容(比如:Cookie),编解码统一用的是ISO8859-1字符集,而且不能更改,所以在Header中不能使用非ASCII字符。

示例:在Cookie中传输中文会报错

代码语言:javascript
复制
<%@ page language="java" pageEncoding="UTF-8"%>
<% 
  Cookie c = new Cookie("name", "联通");
  response.addCookie(c);
%>
<!DOCTYPE html>
<html><head>
  <meta charset="utf-8">
  <title></title>
</head><body></body></html>

注:如果要在Cookie中存储中文信息,可以用base64等技术编码一下。

5.11. GET 请求中的编码、解码

5.11.1. URL结构

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

  • Port :对应Tomcat的<Connector port=”8080″/> 配置。
代码语言:javascript
复制
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

  • ContextPath:对应<Context path=”/code”/> 配置。
代码语言:javascript
复制
<Context docBase="code" path="/code" reloadable="true" source="org.eclipse.jst.jee.server:code"/>

  • ServletPath:对应web.xml中Servlet的<url-pattern>配置
代码语言:javascript
复制
<servlet>
     <servlet-name>Code</servlet-name>
     <servlet-class>com.Code</servlet-class>
</servlet>
<servlet-mapping>
     <servlet-name>Code</servlet-name>
     <url-pattern>/servlet/servlet/*</url-pattern>
</servlet-mapping>

  • PathInfo: 对应我们请求的具体的 Servlet。
  • QueryString 是要传递的参数。

5.11.2. 浏览器如何编码 PathInfo 和 QueryString

备注:

  1. 分析浏览器编码,使用的是Fiddler抓包工具;
  2. “联通”的UTF-8编码为:0xE88194 0xE9809A
  3. “联通”的GBK编码为:0xC1AA 0xCDA8
  • CHROME:对PathInfo和QueryString 都采用 UTF-8 编码

  • Firefox:对 PathInfo 和 QueryString 都采用 UTF-8 编码

  • Safari:对 PathInfo 和 QueryString 都采用UTF-8编码

  • IE:对 PathInfo 采用 UTF-8 编码;对 QueryString 采用 GBK 编码;

  • IE:如果在【选项 -> 高级 -> 国际】里面,取消勾选【以UTF-8形式发送URL路径】选项,则IE对PathInfo和QueryString都将采用GBK编码;

5.11.3. 服务器如何解码 PathInfo 和 QueryString

5.11.3.1. PathInfo 解码(实际是 URI,包含PathInfo):

Tomcat 对 URI 解码的字符集由 Connector 中的 URIEncoding 属性指定,默认 ISO-8859-1。

代码语言:javascript
复制
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="UTF-8"/>

5.11.3.2. 对QueryString:

  • 通过 request.getParameter 可获取 QueryString 中的参数值。
  • QueryString 是通过HTTP的Header传到服务端的,并且也在URL中。
  • QueryString 的解码是在第一次调用request.getParameter发生的。
  • QueryString 的解码字符集要么是Header中ContentType中定义的Charset要么就是默认的 ISO-8859-1,要使用ContentType中定义的编码就要设置useBodyEncodingForURI为true。

总结:不要在GET请求中使用中文字符。如果必须要传输中文字符,那可以先用encodeURIComponent()方法对中文字符编码,再发送GET请求。

5.12. Request 中的编码问题(POST请求)

  • 通过request.getParameter可获取POST请求中的参数值。
  • POST请求中的参数是通过HTTP的BODY传递到服务端的。
  • POST请求中的参数解码是在第一次调用request.getParameter发生的。
  • POST请求中的参数的解码字符集由request.getCharacterEncoding的值确定。

5.12.1. 通过 Form 发的 POST 请求:

  • 当点击 submit 按钮时,浏览器会根据网页的charset对表单填的参数进行编码,然后提交到服务器端。
  • 在默认情况下浏览器在提交Form表单时,提交的content-type中不会含有charset信息的(即request.getCharacterEncoding()会得到null值),所以如果没有设置requeset.setCharacterEncoding,那么表单提交的数据将会被按照系统的默认编码方式解析。

5.12.2. 通过 $.ajax 发的 POST 请求:

  • $.ajax 始终使用 UTF-8 对 POST 参数进行编码,然后提交到服务器;
  • $.ajax 经过配置,可以在 POST 请求 Header 中的 conten-type 字段携带 charset 信息;

最佳实践:下面的4部分字符集要统一、建议使用UTF-8

  1. HTML、JSP文件自身字符集要是UTF-8;
  2. HTML中 <meta charset="UTF-8"> 要声明为UTF-8;
  3. Ajax 请求中 contentType中的charset也要声明为UTF-8;
  4. 增加一个EncodingFilter,配置request.setCharacterEncoding为UTF-8;(可以考虑使用Spring的编码过滤器)

5.13. Response 中的编码问题

代码语言:javascript
复制
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write("没有什么是一拳解决不了的,如果有,那就两拳。——琦玉");

  1. content-type 用于指示浏览器采用何种编码解析数据流;
  2. 当使用 writer 回写数据时,response.setCharacterEncoding 用于指示服务器采用何种编码将字符流转换为字节流,写会浏览器。

5.14. 认识 Spring 的编码过滤器

代码语言:javascript
复制
<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>forceEncoding</param-name>  
        <param-value>true</param-value>  
    </init-param>  
</filter>  
<filter-mapping>  
    <filter-name>characterEncodingFilter</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping> 

Spring 源码:

代码语言:javascript
复制
@Override  
protected void doFilterInternal(HttpServletRequest request, 
       HttpServletResponse response, FilterChain filterChain)  
   throws ServletException, IOException {  
   if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {  
        request.setCharacterEncoding(this.encoding);  
        if (this.forceEncoding) {  
            response.setCharacterEncoding(this.encoding);  
        }  
    }  
    filterChain.doFilter(request, response);  
 }

所以encoding=true、forceEncoding=true合起来,意味着:

代码语言:javascript
复制
request.setCharacterEncoding("UTF-8");  
response.setCharacterEncoding("UTF-8");

5.15. JSP 文件头中的编码问题

代码语言:javascript
复制
<%@ page language="java" pageEncoding="UTF-8" 
  contentType="text/html; charset=UTF-8"%>

  • pageEncoding:一方面,编译JSP时,指示采用何种编码解析当前JSP文件。
  • contentType:就是 response.setContentType。

注:如果不写contentType,只有 pageEncoding,则自动生成与 pageEncoding 一样编码的 contentType。

5.16. 䶮——“飞龙在天”

字音:yǎn; 起源:五代时南汉刘岩为自己名字造的字; 含义:“飞龙在天”的意思;

5.16.1. “䶮”的特殊性

GBK编码字符“䶮”,在Unicode中存在两种表示:PUA区的0xE863、非PUA区的0x4DAE。

注:PUA, Private Use Area

5.16.2. “䶮”为什么特殊?

GBK字符集中有80个增补字符最初并未在Unicode中定义,于是使用了Unicode的PUA区域的代码点表示。后来Unicode使用非PUA区域代码点正式定义了这80个字符。这样就出现有80个汉字在Unicode定义的代码点区域中有两种不同的表示方法。

GBK字符集80个增补字符:

5.16.3. “䶮”——可能会遇到什么问题?

例如:

Oracle使用ZHS16GBK字符集存储字符“䶮”,但AIX系统从数据库中读出后,展示为问号 (?)。

问题原因:

该问题是由Oracle ZHS16GBK字符集和IBM® GBK 转换器之间GBK未定义的代码范围Unicode映射的不兼容性导致的。

测试代码:

代码语言:javascript
复制
public class Code {
  public static void main(String[] args) {
    System.err.println(System.getProperty("file.encoding"));
    printBytes(""); // GBK
  }

  private static void printBytes(String chars) {
    byte[] bytes = chars.getBytes();
    StringBuffer sb = new StringBuffer();
    for(int i=0; i<bytes.length; i++){
      int mask = 0x80;
      do{
        if((mask & bytes[i]) != 0){
          sb.append("1");
        }else{
          sb.append("0");
        }
      }while((mask >>= 1) !=0);
      sb.append(" ");
    }
    System.err.println(sb.toString());
  }
}

AIX上的运行结果:

REDHAT上的运行结果:

5.17. 文件下载中的编码问题

...直接看代码吧...

代码语言:javascript
复制
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    String fileContent = "没有什么是一拳解决不了的,如果有,那就两拳。——琦玉";
    String fileName = "琦玉名言.txt";

    String encodedFileName = null;
    if (request.getHeader("User-Agent").toLowerCase().indexOf("firefox") > -1) {
      encodedFileName = new String(fileName.getBytes("UTF-8"), "ISO8859-1");
    } else {
      encodedFileName = URLEncoder.encode(fileName, "UTF-8");
    }

    response.setContentType("application/octet-stream");
    response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName);
    response.getOutputStream().write(fileContent.getBytes("UTF-8"));
  }


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WebJ2EE 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 字符是如何存储的?
  • 存储的是表示字符的“内码”(二进制)。
  • 字符是如何在屏幕上展示的?
  • 3. 字符集与字符编码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档