编码小结

1 初识编码

所谓编码,是信息从一种形式或格式转换为另一种形式的过程。

字符编码,从自然语言的字符的一个集合(如字母表或音节表),到其他东西的一个集合(如号码或电脉冲)的映射

ANSI:windows特有,在中国大陆即为GBK (DBCS Double Byte Charecter Set,双字节字符集)
UCS-2:即Unicode,(Universal Multiple-Octet Coded Character Set)
UTF:(UCS Transfer Format,用以存储和传输的格式)
BOM头:纯文本文件开始的几个表示编码格式的字节(Byte Order Mark)
编码方式 BOM
UTF-8 0xEFBBBF
UnicodeBig 0xFEFF
UnicodeSmall 0xFFFE

2 关于ANSI

ANSI不是某种特定的编码,而是windows在不同系统中表示不同的编码。

特别地,美国系统就是ASCII编码;韩国系统就是EUC-KR编码;中国系统就是GBK编码。

graph LR
A(ASCII)==>B(扩展ASCII)
B==>C(GB2312)
C==>E(GBK)
E==>F(GB18030)
B==>D(Big5)

Winodows怎么区别ANSI背后真正的编码?

Windows code pages,即我们经常见到的cpxxx

cp936表示GBK,cp950表示Big5,cp437表示ASCII

3 系统编码的查看与修改

windows

查看:可在命令行下执行chcp来查看当前的code page

修改终端的active cp:在命令行输入chcp xxx(只在终端起作用,不影响系统默认的ANSI编码)

修改系统的cp(根据当前系统的locale来设置,控制面板=>区域=>修改系统区域设置)

Linux

查看:locale


LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=zh_CN.UTF-8
LC_TIME=zh_CN.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=zh_CN.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=zh_CN.UTF-8
LC_NAME=zh_CN.UTF-8
LC_ADDRESS=zh_CN.UTF-8
LC_TELEPHONE=zh_CN.UTF-8
LC_MEASUREMENT=zh_CN.UTF-8
LC_IDENTIFICATION=zh_CN.UTF-8
LC_ALL=

修改:export LC_ALL=zh_CN.GBK

变量优先级

LC_ALL > LC_* >LANG

LANG:所有没有设置的locale变量的默认locale

LANGUAGE:设置应用程序的界面语言

4 国标

graph LR
A(区位码)==>B(GB2312)
B==>C(GBK)
C==>D(GB18030)

区位码

早年的中国编码标准,由4个十进制数字表示一个字符,前两位为"区",后两位为"位"。汉字区号从16开始,位号从1开始。

区位码节选表

01-09区为特殊符号

16-55区为一级汉字,按拼音排序

56-87区为二级汉字,按部首笔画排序

GB2312

基于区位码,用双字节表示汉字和汉字字符,0xA0+区号,0xA0+位号。

以汉字“安”为例

区位码是1618(十进制)

GB2312编码是 0xA0 +16 0xA0 +18 ==> 0xB0 0xB2

GB2312编码范围是0xB0A1~0xF7FE(收录汉字6763+其他字符682)

全角半角

GB编码兼容ASCII,数字2有两个编码。

其中ASCII编码是0x32,由区位码(0218)而来的编码是0xA3B2。

前者单字节的是半角,后者双字节的是全角。

GBK

首字节在0x810xFE,尾字节在0x400xFE,剔除xx7F一条线

GBK和ASCII码区分

ASCII只有0-127(0x00~0x7F),高字节的最高位为0则为ASCII,为1则为中文

GB18030

  • 变长编码
  • 支持少数民族文字
  • 收录范围包括繁体汉字以及日韩汉字
单字节 0x00-0x7F,与ASCII兼容
双字节 首字节0x81到0xFE,尾字节0x40到0xFE(不包括0x7F),与GBK兼容
四字节 第一个字节0x81到0xFE,第二个字节0x30到0x39,第三个字节0x81-0xFE,第四个字节0x30-0x39

5 UnicodeUTF-8

各国编码标准互不兼容,推出统一标准Unicode

两者关系可以类比成区位码同GB2312的关系

绝大多数程序只支持双字节,即UCS-2

UTF-8:针对Unicode的可变长字符编码(多字节串,第一个字节在C0到FD之间,后面的字节在80到BF之间)

Unicode与UTF-8的转换

Unicode UTF-8 有效位数 编码范围
U-00000000 ~ U-0000007F 0xxxxxxx 7 0x00-0x7F
U-00000080 ~ U-000007FF 110xxxxx 10xxxxxx 11 0xC080-0xDFBF
U-00000800 ~ U-0000FFFF 1110xxxx 10xxxxxx 10xxxxxx 16 0xE08080-0xEFBFBF
U-00010000 ~ U-001FFFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 21 0xF0…-0xF7…
U-00200000 ~ U-03FFFFFF 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 26 0xF8…-0xFB…
U-04000000 – U-7FFFFFFF 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 31 0xFC…-0xFD…

openssl实现(crypto/asn1/a_utf8.c)

  • Unicode转UTF-8
int UTF8_putc(unsigned char *str, int len, unsigned long value)
{
if (!str)
len = 6; /* Maximum we will need */
else if (len <= 0)
return -1;
if (value < 0x80) {
if (str)
*str = (unsigned char)value;
return 1;
}
if (value < 0x800) {
if (len < 2)
return -1;
if (str) {
*str++ = (unsigned char)(((value >> 6) & 0x1f) | 0xc0);
*str = (unsigned char)((value & 0x3f) | 0x80);
}
return 2;
}
if (value < 0x10000) {
if (len < 3)
return -1;
if (str) {
*str++ = (unsigned char)(((value >> 12) & 0xf) | 0xe0);
*str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
*str = (unsigned char)((value & 0x3f) | 0x80);
}
return 3;
}
if (value < 0x200000) {
if (len < 4)
return -1;
if (str) {
*str++ = (unsigned char)(((value >> 18) & 0x7) | 0xf0);
*str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
*str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
*str = (unsigned char)((value & 0x3f) | 0x80);
}
return 4;
}
if (value < 0x4000000) {
if (len < 5)
return -1;
if (str) {
*str++ = (unsigned char)(((value >> 24) & 0x3) | 0xf8);
*str++ = (unsigned char)(((value >> 18) & 0x3f) | 0x80);
*str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
*str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
*str = (unsigned char)((value & 0x3f) | 0x80);
}
return 5;
}
if (len < 6)
return -1;
if (str) {
*str++ = (unsigned char)(((value >> 30) & 0x1) | 0xfc);
*str++ = (unsigned char)(((value >> 24) & 0x3f) | 0x80);
*str++ = (unsigned char)(((value >> 18) & 0x3f) | 0x80);
*str++ = (unsigned char)(((value >> 12) & 0x3f) | 0x80);
*str++ = (unsigned char)(((value >> 6) & 0x3f) | 0x80);
*str = (unsigned char)((value & 0x3f) | 0x80);
}
return 6;
}
  • UTF-8转Unicode
int UTF8_getc(const unsigned char *str, int len, unsigned long *val)
{
const unsigned char *p;
unsigned long value;
int ret;
if (len <= 0)
return 0;
p = str; /* Check syntax and work out the encoded value (if correct) */
if ((*p & 0x80) == 0) {
value = *p++ & 0x7f;
ret = 1;
} else if ((*p & 0xe0) == 0xc0) {
if (len < 2)
return -1;
if ((p[1] & 0xc0) != 0x80)
return -3;
value = (*p++ & 0x1f) << 6;
value |= *p++ & 0x3f;
if (value < 0x80)
return -4;
ret = 2;
} else if ((*p & 0xf0) == 0xe0) {
if (len < 3)
return -1;
if (((p[1] & 0xc0) != 0x80)
|| ((p[2] & 0xc0) != 0x80))
return -3;
value = (*p++ & 0xf) << 12;
value |= (*p++ & 0x3f) << 6;
value |= *p++ & 0x3f;
if (value < 0x800)
return -4;
ret = 3;
} else if ((*p & 0xf8) == 0xf0) {
if (len < 4)
return -1;
if (((p[1] & 0xc0) != 0x80)
|| ((p[2] & 0xc0) != 0x80)
|| ((p[3] & 0xc0) != 0x80))
return -3;
value = ((unsigned long)(*p++ & 0x7)) << 18;
value |= (*p++ & 0x3f) << 12;
value |= (*p++ & 0x3f) << 6;
value |= *p++ & 0x3f;
if (value < 0x10000)
return -4;
ret = 4;
} else if ((*p & 0xfc) == 0xf8) {
if (len < 5)
return -1;
if (((p[1] & 0xc0) != 0x80)
|| ((p[2] & 0xc0) != 0x80)
|| ((p[3] & 0xc0) != 0x80)
|| ((p[4] & 0xc0) != 0x80))
return -3;
value = ((unsigned long)(*p++ & 0x3)) << 24;
value |= ((unsigned long)(*p++ & 0x3f)) << 18;
value |= ((unsigned long)(*p++ & 0x3f)) << 12;
value |= (*p++ & 0x3f) << 6;
value |= *p++ & 0x3f;
if (value < 0x200000)
return -4;
ret = 5;
} else if ((*p & 0xfe) == 0xfc) {
if (len < 6)
return -1;
if (((p[1] & 0xc0) != 0x80)
|| ((p[2] & 0xc0) != 0x80)
|| ((p[3] & 0xc0) != 0x80)
|| ((p[4] & 0xc0) != 0x80)
|| ((p[5] & 0xc0) != 0x80))
return -3;
value = ((unsigned long)(*p++ & 0x1)) << 30;
value |= ((unsigned long)(*p++ & 0x3f)) << 24;
value |= ((unsigned long)(*p++ & 0x3f)) << 18;
value |= ((unsigned long)(*p++ & 0x3f)) << 12;
value |= (*p++ & 0x3f) << 6;
value |= *p++ & 0x3f;
if (value < 0x4000000)
return -4;
ret = 6;
} else
return -2;
*val = value;
return ret;
}

6 区分不同编码

  • 有BOM头

直接根据BOM头区分

  • 没有BOM头

需要大量的编码分析

通常应用会有自己庞大的词库,常见词组编码组合,匹配度越高,越有可能是该编码。

7 Urlencode

不同于上面的编码,上面的编码时字符和数字的对应,url编码是字符替换,将非ASCII字符和一些容易引起问题的字符替换。

uri允许的字符分为保留字符和未保留字符

8 X509证书中DN项的string类型(crypto/asn1/a_mbstr.c)

  • 输入编码控制

      openssl中定义了以下编码格式
    MBSTRING_ASC(ASCII),MBSTRING_BMP(UCS-2),MBSTRING_UNIV(UCS-4), MBSTRING_UTF8(UTF-8)
    默认是MBSTRING_ASC,可以在证书请求命令中加-utf8设置为MBSTRING_UTF8
    `openssl req -new -key test.key-config test.conf -out test.req -utf8`
    注意输入编码格式要跟配置文件实际的编码一致
  • 输出编码控制

    输出的编码格式由String类型决定

    ASN1 String类型 编码
    Numeric,Printable,IA5,T61 MBSTRING_ASC
    BMP MBSTRING_BMP
    Universal MBSTRING_UNIV
    UTF8 MBSTRING_UTF8

    可以通过配置文件中string_mask控制证书DN项的String类型

    string_mask 支持的String类型
    default PrintableString, T61String, BMPString
    pkix PrintableString, BMPString
    utf8only UTF8Strings
    nombstr PrintableString, T61String
  • openssl中生成证书请求时,String类型的确认

    Numeric < Printable < IA5 < T61 < BMP < Universal < UTF8

    根据DN项的字符范围,选择最小的符合类型

  • 避免证书中出现中文乱码

  1. 将配置文件转码成UTF-8格式(GBK转UTF-8)

    iconv -f GBK -t UTF-8 old.conf -o new.conf

  2. 证书请求时加上-utf8

    openssl req -new -key test.key -config new.conf -out test.req -utf8

9 vim中的各种encoding

支持中文编码的基础

  • 编译时包含+multi_byte和+iconv两个特性,可以用:version命令查看

编码设置项

  • encoding(enc)

      vim内部的使用编码,影响vim内部的buffer,菜单文本,消息文本等
    Unix下默认等于locale,Windows下则是当前code page
    只在启动的时候设置一次,建议始终设置为utf-8
  • fileencodings(fencs)

      打开文件时,会从此列表中所列选项逐一探测文件编码,并且将fileencoding设置为最终探测到的字符编码方式。最好将unicode放到最前面,latin1放到最后面。
  • fileencoding(fenc)

      打开文件时,会根据所识别的编码设置
    保存文件时,会根据filecoding的设置值来保存
  • termencoding(tenc)

      在终端环境下使用时,用来告诉vim当前终端所使用的编码,用来显示

vim中编码转换

graph LR
A(打开时fileencodings探测)==>B(fileencoding)
B==>C(内部编码encoding)
C==>D(根据termencoding转码显示)
  • 打开文件,从fileencoding转成encoding,然后将转换后的内容放到buffer里面。在转换过程中如果含有不支持的字符,会丢失
  • 保存文件,相反的过程
  • 终端使用vim的时候,将内部编码转换为termencoding显示。如果含有不支持的字符会显示问号,但是不影响编辑。如果没有设置,则直接使用encoding,不转换

推荐设置

:set encoding=utf-8
:set termencoding=utf-8
:set fileencoding=utf-8
:set fileencodings=ucs-bom,utf-8,cp936,gb18030,big5,euc-jp,euc-kr,latin1

fencview

内置的编码识别机制,识别率是很低的

推荐使用fencview,该插件使用词频统计的方式识别编码,正确率非常高。

最新文章

  1. [地图SkyLine二次开发]关于IE内存限制问题(1G)
  2. 使用Qt installer framework制作安装包
  3. 网页内容导出word/excel的js代码
  4. QQ空间自动发广告解决方法
  5. PHP学习笔记十四【面向对象】
  6. 苹果2014WWDC亮点之个人浅见
  7. 【ASP.NET Web API教程】2.3 与实体框架一起使用Web API
  8. php观察者模式
  9. 201521123061 《Java程序设计》第十四周学习总结
  10. html学习第一弹の常用标签的归类
  11. poj 1375
  12. GP card规范学习笔记
  13. MathWorks官方消息:神经网络工具箱不能编译
  14. Python自学笔记-装饰器1(廖雪峰的网站)
  15. offsetHeight、scrollHeight、clientHeight、height
  16. mybatis例子
  17. 软工读书笔记 week 7 ——《构建之法》
  18. 微信小程序——手把手教你写一个微信小程序
  19. pct_free
  20. shell if [ -d filename]

热门文章

  1. VS 提升代码辨识度 (工欲善其事必先利其器)新手开发必备!
  2. Gym-101673 :East Central North America Regional Contest (ECNA 2017)(寒假自训第8场)
  3. DES算法,JAVA,遇到的问题
  4. 字符串转json格式
  5. MySQL安装配置错误\日常使用错误
  6. 《DSP using MATLAB》Problem 5.22
  7. CH4701 天使玩偶
  8. 【添加tomcat里lib下的jar包】eclipse中The project cannot be built until build path errors are resolved
  9. 下载并安装oracle 11g客户端
  10. Cassandra基础