不同程序角度编码问题

105 阅读5分钟

角度

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
  • 编码推断:使用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编码

秒理解乱码

  • 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一下就好了。。。。