角度
-
OS、内存、浏览器、服务器、编程语言
-
参考:Java+Tomcat例子,字符集编码与乱码_果冻的猿宇宙的博客-CSDN博客
- 其专栏包括了编码的中级深究
-
编码判断错误风险:原字符丢失,变成了现在的编码字符
OS
-
Windows通常叫ANSI。当写入磁盘时,则是所使用程序指定。
- ANSI:Mircosoft为了统一编码称呼而创,实际编码是根据locale的。比如中国就是gbk。
-
chcp:change code page
- 修改默认编码,但注意,如果在cmd等终端中,仅仅只是修改了其显示的编码,系统编码不变,需要在设置-时间-区域-管理-更改系统区域设置(可以设置为Beta---UTF-8)
- GBK=936
- UTF-8=65001
-
文件系统:
- Windows:
- 文件名:一般UTF-16(Unicode)
- 文件内容:默认系统编码ANSI
- 特殊文件:以magic number开头,比如.class文件是
CA FE BA BE(16进制),像无扩展名,txt,md是没有的 - Modified UTF8:非文本文件
- Unicode的BOM(固定的):UTF-16一定有BOM,UTF-8可能没有
- 因为UTF-8模式固定,因此很多时候程序会错误判断为UTF8 without BOM
- 程序读取文件内容缺省编码当然是ANSI
- 比如javac编译时如果文件不是GBK(在Windows中其缺省就是Windows默认字符ANSI),则需要添加选项
-encoding xxx
- 比如javac编译时如果文件不是GBK(在Windows中其缺省就是Windows默认字符ANSI),则需要添加选项
- 特殊文件:以magic number开头,比如.class文件是
- Windows:
-
编码推断:使用ASCII在开头设置编码,如XML
<?xml version="1.0" encoding="UTF-8"?>- 智能编辑器根据这些固定编码,错误字符提示
内存
- 一般显示出的字符都用UTF-16在内存保存,剪切不会进行编码,因此进行复制粘贴时没有编码问题
- 在保存时才会进行编码保存,读取时又解码显示
- Java:
- 参考:blog.csdn.net/ShawGolden/…
- String由char[]组成,char占2B,操作char时是2B2B地进行,即 code unit。
- 但是String在JVM可能会被压缩,所以不能简单地看成char[].length*2B
- 容量:由于char最多表示65535码点的Unicode
- 代理对方法:因此抽取\uD800-\uDBFF、\uDC00-\uFFF(1024x1024~=100W...)为保留码点,2个保留码点表示一个抽象字符
- 因此>65535的由2个char组成
- 比较智能的IDE(如IDEA)复制在Java代码中自动转为2个unicode码
- byte编码是Latin1/UTF16 编码
- 字符流在保存字符时一样是先转为Latin1/UTF16
浏览器
- BOM > Content-Type的charset > meta(通常只适合静态文件) > 缺省(比如系统默认,locate)
- 比较好的IDE也会这样设置文件编码
服务器
- 比如tomcat(智能IDE是会根据这个值自动变化文件编码的,比如IDEA,Ecplise)
- JSP文件编码感知
- web.xml#web-app.jsp-config.jsp-property-group#page-encoding > pageEncoding > content-type > 编辑器缺省编码
- 当删除web.xml,指定其他时,可能需要写写meta才会自动转换过来(比如IDEA),反之一样
- 响应流编码
- content-type > pageEncoding > web.xml#web-app.jsp-config.jsp-property-group#page-encoding > 缺省ISO-8859-1 (都是通过设置Content-Type)
- 对于tomcat来说,缺省就设置content-type编码为ISO-8859-1,因此meta是无效的
- 而且同时设置pageEncoding 和page-encoding会报错
- content-type:可以代码设置(必须在写入前),也可以是jsp page指令中的content-type属性(在生成的JSP Servlet中调用一样的resp方法进行设置)
编程语言
- 如Java
- 默认编码是java.nio.charset.Charset#defaultCharset
- 由方法可知,file.encoding > Java库编译时指定的(与OS有关)
- 决定了InputStreamReader/OutputStreamWriter的默认编码
- 注意:Servlet中的resp.getWriter()
- 缺省为org.apache.coyote.Constants.DEFAULT_BODY_CHARSET(ISO-8859-1)
- 调用前设置了encoding才使用设置的编码,否则缺省。且后面设置的都忽视
- 而且JSP中的out对象JspWriter就是装饰了resp.getWriter(即使用resp.getWriter)
- 因此解决乱码方法:在doFilter前设置resp编码
- 注意:Servlet中的resp.getWriter()
秒理解乱码
-
javac编译程序:有
-encoding选项,这个选项就是用来指定源文件编码的- 如果源文件和终端的编码不一样,那么编译的.class文件当然是有乱码的
-
java标准输出:也是有编码的(默认编码为终端编码),如果显示的终端编码和输出编码不一样,当然会乱码
-
java内置日志输出:可以使用
-Dfile.encoding指定输出编码,也可以在/conf/logging.properties指定ConsoleHanlder的编码,或者其他Handler的编码 -
说白了关键在终端或者执行程序所处环境
- 通常程序的默认编码都是默认终端编码
- 如java标准输出,根本就好像写死了为终端编码,只有终端没有编码时才会用其他方式
- 终端:
- cmd程序:也可以用chcp命令修改编码
- IDEA控制台:可以在Settings中找Console设置编码
- 通常程序的默认编码都是默认终端编码
-
再来个比较恶心的:tomcat
- tomcat启动,使用
start启动,导致logging配置编码需要设置为系统编码,至于-Dfile.encoding到不用,因为这个默认就是系统编码,只是logging配置指定了输出编码而已 - 修改tomcat启动命令(catalina.bat/.sh)
- 在
:doStart处,set _EXECJAVA=start "%TITLE%" %_RUNJAVA%- 改为
set _EXECJAVA=start "%TITLE%" cmd /k "chcp 65001 && %_RUNJAVA%
- 改为
- 在
:execCmd处,在每个命令之后补上" - 这就爽了,start启动cmd,cmd可以执行多个命令,那就先执行chcp吧!!!!
- 但要注意其他命令也start命令有点不太一样,因此都要修改一点,亦或者start单独拎出来咯(但是反正我只用start, 也就不管其他了, ctrl+c结束程序)
- 注意: 即便是可设置编码终端, start也会导致启动cmd窗口,如IDEA
- 在
- 话说我直接删除start "%TITLE%"不好吗。。。。
- 就在当前终端执行。。。。先chcp一下就好了。。。。
- tomcat启动,使用