编码问题简述
ASCII 编码, ASCII(American Standard Code for Information Interchange), 是一种字符编码标准, 它的字符集为英文字符集, 它规定字符集中的每个字符均由一个字节表示, 指定了字符表编码表, 称为 ASCII 码表. 这是最开始的编码.
ANSI 编码, ASCII 码只满足了英文国家的需求, 对于汉语日语什么的没有考虑. 于是各个国家纷纷制定了适用于自己国家的编码, GB2312,BIG5,JIS 等仅适用于本国字符集的编码标准. 这些字符编码标准统称为 ANSI 编码标准, 而 ANSI 编码一般是兼容 ASCII 编码的.
例如, GBK 的中文编码是双字节来表示的, 英文编码是用 ascii 码表示的, 既用单字节表示. 但 GBK 编码表中也有英文字符的双字节表示形式, 所以英文字母可以有两种 GBK 表示方式. 为区分中文, 将其最高位都定成 1. 英文单字节最高位都为 0. 当用 GBK 解码时, 若高字节最高位为 0, 则用 ASCII 码表解码; 若高字节最高位为 1, 则用 GBK 编码表解码.
但是各个国家的 ANSI 编码相互之间是不兼容的. 于是 UNICODE 出现了.
UNICODE 编码, 简单的说, UNICODE 可以把各个国家的语言文字和各种符号都包含进去, 相互之间也就不需要转码了(理想情况). 但是 UNICODE 只是码表, 具体到码字的具体编码形式, 则又出现了 UTF.
UTF(Unicode Translation Format), 它是 Unicode(UCS)的实现 (或存储) 方式, 称为 Unicode 转换格式. Unicode 的实现方式不同于编码方式. 一个字符的 Unicode 编码是确定的. 但是在实际传输过程中, 由于不同系统平台的设计不一定一致, 以及出于节省空间的目的, 对 Unicode 编码的实现方式有所不同.
UTF 有三种实现方式:
UTF-16: 其本身就是标准的 Unicode 编码方案, 又称为 UCS-2, 它固定使用 16 bits(两个字节)来表示一个字符. 由于固定使用两个字节, UTF-16 是不能完全表示 UNICODE 的, 使用 2 字节的只是一部分 UNICODE, 很多辅助平面的 UNICODE 需要 3-4 个字节才能表示.
UTF-32: 又称为 UCS-4, 它固定使用 32 bits(四个字节)来表示一个字符.
UTF-8: 最广泛的使用的 UTF 方案, UTF-8 使用可变长度字节来储存 Unicode 字符, 例如 ASCII 字母继续使用 1 字节储存, 重音文字, 希腊字母或西里尔字母等使用 2 字节来储存, 而常用的汉字就要使用 3 字节. 辅助平面 (UNICODE 分为好多平面) 字符则使用 4 字节. UTF-8 更便于在使用 Unicode 的系统与现存的单字节的系统进行数据传输和交换. 与前两个方案不同: UTF-8 以字节为编码单元, 没有字节序的问题.
UTF-8 的编码形式:
0000 - 007F 这部分是最初的 ascii 部分, 按原始的存储方式, 即 0xxxxxxx.
0080 - 07FF 这部分存储为 110xxxxx 10xxxxxx
0800 - FFFF 这部分存储为 1110xxxx 10xxxxxx 10xxxxxx
可以看到, UTF-8 也是兼容 ASCII 的.
由于 UTF-8 和 GBK 都是兼容 ASCII 的, 所以在编辑器切换这两种编码方式的时候, 英文的显示是不受影响的.
GBK,GB2312 以及 Unicode 都既是字符集, 也是编码方式, 而 UTF-8 只是编码方式, 并不是字符集.
PHP 中宽字符的处理
这里把 ASCII 以外的编码表示的字符都称为宽字符. PHP 提供了丰富的函数处理 ASCII 字符. 对于宽字符的处理, 则是依靠 mbstring 这个扩展提供的一系列扩展函数实现的.
例如字符长度的计算是通过 mb_strlen 实现的:
- $str='中文 a 字 1 符';
- echo strlen($str); // 字节个数, 14
- echo mb_strlen($str,'UTF-8'); // 选定内码为 UTF-8, 中文作为一个字节, 6
- echo mb_strlen($str,'gbk'); //8
- echo mb_strlen($str,'gb2312'); //10
这是网上的一个文章中的例子. mb_strlen 的第二个参数就是用来指明 $str 的所使用的编码的, 而不是要求函数以某种编码方式计算长度.
$str 中字串的编码方式就是当前脚本文件的编码方式 (这里是 UTF-8), 所以只有 mb_strlen($str,'UTF-8') 得出正确结果. 不知道为什么这种明显的错误在网上不止一处的文章里出现.
mb_strlen 的函数原型:
mixed mb_strlen ( string $str [, string $encoding = mb_internal_encoding() ] )
第二个参数默认是 mb_internal_encoding(), 需要注意的是 mb_internal_encoding()一般并不和当前的脚本文件的编码方式相同, 脚本最常用的是 UTF-8, 没有更改设置的情况下 mb_internal_encoding()一般是 ISO-8859-1, 也就是 Latin-1 编码.
脚本文件的编码和 mb_internal_encoding()没有必要一致, 进行相关处理的处理的时候指明参数的正确编码方式就行, 比如这里的 mb_strlen. 有的时候可能没有机会设置这个参数, 出现乱码的时候, 可以考虑下是不是这里的问题.
其他常用的函数还有:
- mb_?convert_?encoding (容易出危险的函数)
- mb_?ereg_?replace_?callback (也是容易出危险的函数)
- mb_?split
- mb_?substr
等等.
网页的乱码
为了防止网页出现乱码, 概括说来, 做到以下四点就好了:
PHP 脚本文件本身使用 UTF-8 字符集.
网页使用 utf-8 编码.
MySQL 数据库使用 utf8 字符集.
各类字符串操作使用 Multibyte String 系列函数.
这只是一个简略的概括, 更多信息可以参考其他文章.
和操作系统的交互
操作系统 (这里说 Windows) 的 API 也是有编码的问题的. 就像大家都知道的, UNICODE 出来之前, 操作系统通过内码来确定相应的 ANSI 代码页以处理不同的语言. UNICODE 出来后, 则定义了另一套的函数. 例如 MessageBox 函数, 有两个版本:
- MessageBoxA
- MessageBoxW
A 代表 ANSI 代码页, W 是宽字符, 即 Unicode 字符. Windows 中的 Unicode 字符一般指 UCS2 的 UTF16-LE 编码.(这里称为宽字符有些莫名其妙的感觉, 相对于单字节的 ASCII 来说, ANSI 编码也是宽的..)
编译时通过预定义宏来决定具体使用哪一个函数. 但是系统的内核是 UNICODE 的, 所有 A 系列函数最后都会转化为对 W 函数的调用.
相应的, 运行时库的函数也是有两个版本函数的.
那么需要调用操作系统的功能, 比如打开一个文件的时候:
- $filename = "我的文件. txt";
- //$filename = iconv('UTF-8','GBK', $filename);// 转换为 GBK 编码
- $file_handler = fopen($filename);
脚本文件的编码是 UTF-8, 那么这里打开会失败, 需要转换为 GBK 编码. 没有去看 fopen 的源码, 但是这里应该是调用 c 运行时库的 A 版本的函数.
那么 PHP 有没有使用宽字符的版本呢? 文档里没找到, 但是即使有的话, 调用之前也得转化为 UTF16-LE 编码再调用. 和 A 版本的函数一样麻烦(转换一下也不是很麻烦).
PHP-cli 程序从命令行获得字符串是什么编码呢? 自然是和系统的当前代码页一致的 ANSI 编码.
另外, 读取 utf8 的文件的时候要注意, 开始的第一行可能是三个不可见的字符:
ef bb bf
也就是 BOM, 注意处理, 免得出问题.
参考:
字符集与字符编码简介, http://blog.csdn.net/gogor/article/details/5323599 .
谈谈 Windows 程序中的字符编码,.
来源: http://www.bubuko.com/infodetail-3105153.html