encoding

encoding in windows netbeans txt and command console

Posted by Lucas on April 17, 2016

未完,待续

让Netbean预设使用UTF-8编码

Netbeans在7.4之后可以直接拖拉方式來开启单一项目了以前都必需要先建一個空白项目,但是由于没有建立项目,所以 Netbeans 不知道你的编码规则,预设是 x-windows-950,所以必需修改设置 netbeans.conf(在软件安装目录下的etc文件夹中),找到 netbeans_default_options 屬性,在最后面加上 -J-Dfile.encoding=UTF-8。

netbeans_default_options=”…(前面省略) -J-Dfile.encoding=UTF-8”

改变txt的编码

windows会自动给txt文本添加bom,这是不可见的符号,会造成各种影响。所以应该使用notpad++修改txt文本为txt无bom编码。

首先了解什么是BOM?

BOM是BYTE ORDER MARK,也就是字节序标记,它是Unicode标准的一部分。详情见维基百科Byte order mark.

微软操作系统在创建Unicode编码的文本文件时,会在文件中放置BOM。通常BOM是用来标示Unicode纯文本字节流的,用来提供一种方便的方法让文本处理程序识别读入的.txt文件是哪个Unicode编码(UTF-8,UTF-16BE,UTF-16LE)。Windows相对对BOM处理比较好,是因为Windows把Unicode识别代码集成进了API里,主要是CreateFile()。打开文本文件时它会自动识别并剔除BOM。Windows用BOM是有历史原因的,因为它最初脱胎于多代码页的环境,而引入Unicode时Windows的设计者又希望能在用户不注意的情况下同时兼容Unicode和非Unicode(Multiple byte)文本文件,就只能借助这种小trick了。(吐槽:微软对兼容性的要求确实是到了非常偏执的地步,任何一点破坏兼容性的做法都不允许,以至于很多时候是自己绑住自己的双手)相比之下,Linux这样的系统在多locale的环境中浸染的时间比较短,再加上社区本身也有足够的动力轻装前进,所以干脆一步到位进入UTF-8。

参考:知乎陈甫鸼

问题一、UTF-8也是多字节编码,为什么可以不需要BOM来标明字节序?

UTF-8、UTF-16、UTF-32 这些是用于把抽象的编号(code point)编码为二进制数据流的方案。 UTF-8 的编码单位是 byte,它用一个接一个的1至4个 byte 来编码一个字符,不存在字节序问题。 UTF-16/32 的编码单位是16或32个 bit,将16或32个 bit 存储为2或4个字节时就有字节序问题了。

阮一峰谈Ascii、Unicode和UTF-8

阮一峰谈Ascii、Unicode和UTF-8

注:这篇文章需要翻墙。

Unicode与utf-8和utf-16的区别

各种编码方式

Ascii码

ASCII码一共规定了128个字符的编码,包括32个不能打印出来的控制符号。比如大写的字母A是65(二进制01000001)。这128个符号,只占用了一个字节的后面7位,最前面的1位统一规定为0。

非Ascii码

英语用128个字符编码就够了,但是用来表示其他语言,128个符号是不够的。于是不同国家有不同的编码体系。

大端还是小端

大端存储(big endian)和小端存储(little endian)是cpu处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49,写到文件里时,如果将6C写在前面,就是big endian,将49写在前面,就是little endian。那么,计算机怎么知道某一个文件到底采用哪一种方式编码?

计算机怎么知道某一个文件到底采用哪一种方式编码?

Unicode规范中定义,每一个文件的最前面分别加入一个表示编码顺序的字符,这个字符的名字叫做”零宽度非换行空格”(ZERO WIDTH NO-BREAK SPACE),用FEFF表示。(这正好是两个字节,而且FF比FE大1)

如果一个文本文件的头两个字节是FE FF,就表示该文件采用大头方式;如果头两个字节是FF FE,就表示该文件采用小头方式。

1)ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对Windows简体中文版,如果是繁体中文版会采用Big5码)。

2)Unicode编码默认的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。这个选项用的little endian格式。

3)Unicode big endian编码与上一个选项相对应。

4)UTF-8编码,UTF-8是Unicode的实现方式之一。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

谈谈Unicode编码

参考文章 程序员趣味读物:谈谈Unicode编码

字符编码、内码

字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期计算机的内码只是ASCII编码;现在的计算机为了支持简体中文,使用了GB2312、GBK或GB18030。关于中文编码,详细见 中文编码

cmd打开UTF-8编码的文本显示乱码的原因:

应该注意到,现在的许多中文windows的缺省内码还是GBK。比如我的windows10系统上面的cmd默认代码页是936(ANSI/OEM-简体中文GBK),并且只有另外一个可选项是437(OEM-美国)。因为GBK对于与Unicode是不兼容的,Unicode只与ASCII兼容(更准确说,是与ISO-8859-1兼容),而uft-8是Unicode的一种实现方法,所以cmd打开UTF-8编码的txt文件会出现乱码。这时应该将txt文件的编码方法改为GBK,或者将cmd使用的系统字符集暂时改为UTF-8。我们这里采用第二种方式解决命令行的乱码以及编码的问题:

命令 CHCP nnn,其中nnn 指定代码页编号。不加参数键入 CHCP 显示活动代码页编号。nnn指定一已有的系统字符集,该字符集在CONFIG.SYS文件中由COUNTRY命令定义。

chcp 65001 就是换成UTF-8代码页

chcp 936 可以换回默认的GBK

如果想正确显示UTF-8字符,可以按照以下步骤操作:

1.打开CMD.exe命令行窗口

2.通过 chcp命令改变代码页,UTF-8的代码页为65001 chcp 65001 执行该操作后,代码页就被变成UTF-8了。但是,在窗口中仍旧不能正确显示UTF-8字符。

3.修改窗口属性,改变字体 在命令行标题栏上点击右键,选择”属性”->”字体”,将字体修改为True Type字体”Lucida Console”,(经过尝试,发现Consolas也可以)然后点击确定将属性应用到当前窗口。 这时使用type命令就可以显示UTF-8文本文件的内容了: type filename.txt

4.通过以上操作并不能完全解决问题,因为显示出来的内容有可能不完全。可以先最小化,然后最大化命令行窗口,文件的内容就完整的显示出来了。

补充:MS-DOS为以下国家和语言提供字符集(代码页描述)

1258 越南语   1257 波罗的语   1256 阿拉伯语   1255 希伯来语   1254 土耳其语   1253 希腊语   1252 拉丁 1 字符 (ANSI)   1251 西里尔语   1250 中欧语言   950 繁体中文   949 朝鲜语   936 简体中文(默认)   932 日语   874 泰国语   850 多语种 (MS-DOS Latin1)   437 MS-DOS 美国英语

摘记和问题

程序在处理文本的时候如何区分ANSI和无BOM的UTF8这两种编码方式

可以参考知乎上一个问题: 程序在处理文本的时候如何区分ANSI和无BOM的UTF8这两种编码方式

我比较支持Belleve的“根据统计规律(频率、Markov链)猜测,但是不能确定。”这样的说法。

根据自己的尝试,我写了多个的txt文本并保存,之后打开绝大多数的文本的时候都是以ANSI(在中文环境里是GBK)的编码方式进行读取,而程序在处理少数的文本编码方式是utf-8。比如在一个新建的txt文本中写入“联通”二字(注意不包括双引号)后保存,然后直接双击打开显示的是乱码。

通过UltraEdit文本编辑器打开该文本,可以看到该文本是以UTF-8的编码方式打开的,显示的是乱码,共占用3个字节,使用十六进制编辑功能观察到文本的十六进制是6A CD A8,这是非常奇怪的现象,因为一个汉字使用UTF-8的编码方式就需要3个字符了,两个汉字理应6个字节;当使用UltraEdit换用GBK编码方式的时候,发现文件大小只有2个字节,十六进制是6A,3F。

而使用Notepad++的ANSI格式去观察,发现显示的是”联通”,而使用Notepad++的UTF-8无BOM格式编码(标准的UTF-8)和UTF-8有格式编码方式去观察,发现显示的是”xC1xAA”。

总之,这个编码问题确实是很难彻底搞得明白,特别是不知道这两种文本编辑器的编码格式转换机理。

注意到:如果先使用Notepad++将文本编码方式改为ANSI的话,那么再用UltraEdit,也是无法正常显示的,但是我的确曾经不知道怎么弄的,使得UltraEdit也能在GBK编码方式下显示出“联通”。总之,UltraEdit不是一般人能玩的,不知道是怎么回事,UltraEdit的编码转化和十六进制的功能貌似很混乱,虽然用十六进制功能可以查看编码,但是其工作机制、编码转化机制相比Notepad++复杂多了,我的头被弄得很晕。我决定弃用UltraEdit。

以下是对于 “编码”二字,先使用Notepad++确定文本的编码方式,然后令UltraEdit分别用GBK和UTF-8的编码方式打开文本的情况。情况相对来说的确很复杂,特别是文本在UltraEdit和Notepad++之间的共同修改和同步;以及UltraEdit在开启或关闭十六进制功能下,文本在GBK和UTF—8编码方式之间的转化方式是不同的;跟在十六进制码之后所显示的乱码也是不同的。这里我让十六进制功能在UltraEdit转换文本编码方式时是关闭的,另外很少关注跟在十六进制码之后所显示的乱码,着重于文本在Notepad++和UltraEdit之间的显示。尽管如此,还是难以弄清楚UltraEdit,所以我已经弃疗了。还是notepad++好呀,简单易用好理解。

Unicode

从前多种字符集存在时,那些做多语言软件的公司遇上过很大麻烦,他们为了在不同的国家销售同一套软件,不仅要处处小心不要搞错,还要把软件中的文字在不同的字符集中转来转去。UNICODE 对于他们来说是一个很好的一揽子解决方案,于是Windows从NT时代开始就采用了UTF-16编码,很多流行的编程平台,例如.Net,Java,Qt还有Mac下的Cocoa等都是使用UTF-16作为基础的字符编码。例如代码中的字符串,在内存中相应的字节流就是用UTF-16编码过的。从这时开始,WINDOWS 系统终于无需要加装各种本土语言系统,就可以显示全世界上所有文化的字符了。

但是,UNICODE 在制订时没有考虑与任何一种现有的编码方案保持兼容,这使得 GBK 与UNICODE 在汉字的内码编排上完全是不一样的,没有一种简单的算术方法可以把文本内容从UNICODE编码和另一种编码进行转换,这种转换必须通过查表来进行。

如前所述,UNICODE 是用多于16bits,两个字节以上长度来表示为一个字符,他总共可以组合出65535以上不同的字符,这大概已经可以覆盖世界上所有文化的符号(unicode现在的规模可以容纳100多万个符号,它的表示上限目前是0x10FFFFF)。 UNICODE 来到时,一起到来的还有计算机网络的兴起,UNICODE 如何在网络上传输也是一个必须考虑的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF8就是每次8个位传输数据,而UTF16就是每次16个位,只不过为了传输时的可靠性,从UNICODE到 UTF时并不是直接的对应,而是要过一些算法和规则来转换。

受到过网络编程加持的计算机僧侣们都知道,在网络里传递信息时有一个很重要的问题,就是对于数据高低位的解读方式,一些计算机是采用低位先发送的方法,例如我们PC机采用的 INTEL 架构;而另一些是采用高位先发送的方式。在网络中交换数据时,为了核对双方对于高低位的认识是否是一致的,采用了一种很简便的方法,就是在文本流的开始时向对方发送一个标志符——如果之后的文本是高位在位,那就发送”FEFF”,反之,则发送”FFFE”。不信你可以用二进制方式打开一个UTF-X格式的文件,看看开头两个字节是不是这两个字节?

从“联通”说开去

讲到这里,我们再顺便说说一个很著名的奇怪现象:当你在 windows 的记事本里新建一个文件,输入”联通”两个字之后,保存,关闭,然后再次打开,你会发现这两个字已经消失了,代之的是几个乱码!呵呵,有人说这就是联通之所以拼不过移动的原因。

其实这是因为GB2312编码与UTF8编码产生了编码冲撞的原因。

当一个软件打开一个文本时,它要做的第一件事是决定这个文本究竟是使用哪种字符集的哪种编码保存的。软件一般采用三种方式来决定文本的字符集和编码:

检测文件头标识,提示用户选择,根据一定的规则猜测

最标准的途径是检测文本最开头的几个字节,开头字节 Charset/encoding,如下表:

EF BB BF UTF-8 FF FE UTF-16/UCS-2, little endian FE FF UTF-16/UCS-2, big endian FF FE 00 00 UTF-32/UCS-4, little endian. 00 00 FE FF UTF-32/UCS-4, big-endian. 当你新建一个文本文件时,记事本的编码默认是ANSI(代表系统默认编码,在中文系统中一般是GB系列编码), 如果你在ANSI的编码输入汉字,那么他实际就是GB系列的编码方式,在这种编码下,”联通”的内码是:

c1 1100 0001 aa 1010 1010 cd 1100 1101 a8 1010 1000 注意到了吗?第一二个字节、第三四个字节的起始部分的都是”110”和”10”,正好与UTF8规则里的两字节模板是一致的,

于是当我们再次打开记事本时,记事本就误认为这是一个UTF8编码的文件。 根据Unicode和UTF-8转换的规则 Unicode UTF-8 0000 - 007F 0xxxxxxx 0080 - 07FF 110xxxxx 10xxxxxx 0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx 让我们把第一个字节的110和第二个字节的10去掉,我们就得到了”00001 101010”,再把各位对齐,补上前导的0,就得到了”0000 0000 0110 1010”,不好意思,这是UNICODE的006A,也就是小写的字母”j”,而之后的两字节用UTF8解码之后是0368,这个字符什么也不是。这就是只有”联通”两个字的文件没有办法在记事本里正常显示的原因。

而如果你在”联通”之后多输入几个字,其他的字的编码不见得又恰好是110和10开始的字节,这样再次打开时,记事本就不会坚持这是一个utf8编码的文件,而会用ANSI的方式解读之,这时乱码又不出现了。

参考:

字符编解码的故事 这篇有少许错误,比如Unicode字符集和UTF-x的编码方式的混淆,以及对全角和半角表述不清等,但是很好理解,很好入门。 关于字符编码,你所需要知道的 这篇文章写得很棒