php 中的宽字符处理 -- 不完全描述

 

 编码问题是每个程序码农都要搞明白的问题。

 

编码问题简述

ASCII编码,ASCII(American Standard Code for Information Interchange),是一种字符编码标准,它的字符集为英文字符集,它规定字符集中的每个字符均由一个字节表示,指定了字符表编码表,称为ASCII码表。这是最开始的编码。

 

ANSI编码,ASCII码只满足了英文国家的需求,对于汉语日语什么的没有考虑。于是各个国家纷纷制定了适用于自己国家的编码,GB2312BIG5JIS等仅适用于本国字符集的编码标准。这些字符编码标准统称为ANSI编码标准,而ANSI编码一般是兼容ASCII编码的。

例如,GBK的中文编码是双字节来表示的,英文编码是用ascii码表示的,既用单字节表示。但GBK编码表中也有英文字符的双字节表示形式,所以英文字母可以有两种GBK表示方式。为区分中文,将其最高位都定成1。英文单字节最高位都为0。当用GBK解码时,若高字节最高位为0,则用ASCII码表解码;若高字节最高位为1,则用GBK编码表解码。

但是各个国家的ANSI编码相互之间是不兼容的。于是UNICODE出现了。

 

UNICODE编码,简单的说,UNICODE可以把各个国家的语言文字和各种符号都包含进去,相互之间也就不需要转码了(理想情况)。但是UNICODE只是码表,具体到码字的具体编码形式,则又出现了UTF

 

UTFUnicode 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-8GBK都是兼容ASCII的,所以在编辑器切换这两种编码方式的时候,英文的显示是不受影响的。

GBK,GB2312以及Unicode都既是字符集,也是编码方式,而UTF-8只是编码方式,并不是字符集

 

PHP中宽字符的处理

这里把ASCII以外的编码表示的字符都称为宽字符。php提供了丰富的函数处理ASCII字符。对于宽字符的处理,则是依靠mbstring这个扩展提供的一系列扩展函数实现的。

 

例如字符长度的计算是通过mb_strlen实现的:

$str='中文a1';  

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字符一般UCS2UTF16-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://blog.csdn.net/fmddlmyy/article/details/399661

posted @ 2019-06-27 08:52  robotech_erx  阅读(608)  评论(1编辑  收藏  举报

本文版权归作者robotech_erx,转载请注明出处:https://www.cnblogs.com/robotech/