文本本来是UTF-8的。但是会出现乱码,类似:
ååæ¬èå¨å¨ä¸æ·ï¼å¨è¿ä¸ªä¸çä¸ï¼
ææå欢çå°æ¹å°±æ¯å¨æ¿ã
ââ æå¦ç
这种在显示的时候出现很让人郁闷,但不能不解决它,怎么办?写了一个无耻的函数:
XML/HTML代码
- function getUtf8Content($content,$default=null){
- $dContent = utf8_decode($content);
- if(mb_detect_encoding($dContent) == mb_detect_encoding($content)){//这句话有点妖。多试试就知道了
- if($default !== null){
- $content = $default ;
- }else{
- if(strpos($dContent,"??????")===false){
- $content = $dContent;
- }
- }
- }
- return $content;
- }
不管了,能解决问题就好。。黑黑,不要怕丑
ssh连接时,发现屏幕上一堆乱码,恐怕这种事情谁都遇到过吧。(我是使用SSH secure shell登录的)
这种情况的发生大多是安装时,语言包选择为中文导致的。一般有以下几种解决方法
1、RH(估计centOS也行)
vim /etc/sysconfig/i18n
内容改为:
LANG="zh_CN.GB18030"
LANGUAGE="zh_CN.GB18030:zh_CN.GB2312:zh_CN"
SUPPORTED="zh_CN.GB18030:zh_CN:zh:en_US.UTF-8:en_US:en"
SYSFONT="lat0-sun16"
2、UBUNTU
中文版的ubuntu遇到这种问题,可以尝试使用putty,因为putty可以通过修改 font, character set 设置来解决。
设置:
Window -> Appearance -> Font settings 选择宋体或新宋体:
Window -> Translation -> Character set translation on received data 选择 UTF-8:
然后基本就没有问题了,
或者尝试使用secureCRT登录,这款软件也能够自动识别。
再或者尝试把语言换为英文?
修改Ubuntu的命令行语言环境的2个步骤:
1、修改/etc/default/locale
如不存在则新建一个
如下:
LANG='en_US' #中文可以用zh_CN
LANGUAGE='en_US:en' #中文可以用zh_CN:zh
2、reboot即可
locale命令可以列出当前系统所用的所有语言设置
即使你搜索google,基本上也只有这几种解决方案了。呵呵,全部列出来,希望有用
前几天的问题仍然在思索,这些网上搜索下来的文章,都将成为我的资料库中的参考资料。
原文:http://www.v-sky.com/blog/?p=215
为什么页面出现乱码?为什么数据库里出现乱码?为什么这些乱码的出现几率飘忽不定了?诸如此类的乱码问题困扰了很多WEB开发人员。假如不将这 背后的细节扫扫清楚,那么我们的确不知道什么时候乱码又出现了,如果你确实没有时间关心这些细节,那么你可以直接看文章最后的总结。
我们所遇到的乱码多数情况是发生在有中文字符的时候,这是由于计算机各种编码的标准不同而造成的,首先我们有必要了解一下计算机编码的发展 史,ASCII码、GB2312、GBK、GB18030、UNICODE、UTF-8、UTF-16、ISO-8859-1…,简而言之它们都是为了满 足不同时期,不同对象的需求,以自己的一套标准尽可能多地表示所需要的符号信息,如果你需要了解得更深入,可以google一下相关资料。(PS:计算机 领域的很多技术发展都是很有意思的)正是因为有这么多不同标准的编码存在,稍不留神我们就走入了编码“陷阱”,如何避免这些“陷阱”了?
一、注意文件编码和字符编码
用记事本建立一个新的文件,默认是ASCII码,我们另存为其他编码类型,例如unicode,如果用UtraEdit或者Emeditor编 辑,你会发现编码类型的选择范围更大。试想一下如果在编码A类型的文件中存在了编码B的字符会有什么现象了?多数情况下会出现乱码。例如我们在一个文件编 码为GB2312的网页文件中写入了UTF-8的字符,那么这个网页浏览的时候就出现了乱码,而我们在浏览器中再自己设定一下编码为UTF-8,页面就恢 复正常了。当然也有例外情况,如果这两种编码是扩展关系,其中的部分字符编码是相同的,这个时候当然不会出现乱码了,例如GBK就兼容了BG2312的所 有字符。(PS:这里有个有趣的现象,新建一个记事本,然后输入“联通”两个字,保存再打开,你会发现它变成了乱码。具体原因你可以google一下,简 单说来是记事本错误地识别了字符编码。)
第一条原则:保持文件编码和字符编码的一致性。(PS:尤其是在客户端使用编辑器更改文件的编码类型时更要注意,有些编辑器只改变了文件编码,而没有将内部的字符编码同样做转换。)
二、 指定网页编码
目前,主流浏览器都遵循RFC标准,他们会优先考虑服务端对Header中的content-type属性的设置,无论你是在server层做的全局设置,还是你的服务端脚本临时设置,你都需要清楚地知道网页否指定了你预定的编码。
如果服务端对Header没有做处理,那么浏览器会识别Meta信息中的content-type,例如,这个网页的编码就指定了是UTF-8,如果其中存在GB的字符,那么乱码就出现了。
第二条原则:清楚地为网页指定我们预定的编码,最便捷的方式是在服务端指定。
三、 留意Form这个数据入口
Form的数据提交看起来是一个很简单的过程,事实上在这个背后浏览器给我们做了很多工作:(详细可参考W3C网站上的TR文档)
1. 检测这个Form是否标准,包括name属性是否存在,disabled是否可用等等一系列信息,同时获取到各种表单的当前值
2. 对于一个检测通过的Form,要把它内部的表单元素的值按照他们在文档中的出现顺序,以name/value的形式记录为一组数据集。
3. 根据Form的enctype属性指定的content-type对获取到的一组数据集做URL编码
4. 最后根据action 和method属性来向服务端发送这组数据集
这里我们需要特别留意的是第三步,这里做URL编码的数据集本身是什么字符编码了?通常情况下它会遵照网页 本身的编码,这就是为何我在第二原则里建议你要明确指定你选定的网页编码。假如你的网页是GBK编码,那么表单提交到服务端的数据就是GBK的编码,如果 网页是UTF-8,那么提交过去的就是UTF-8的。这里就有一个问题,如果客户端提交的是UTF-8,而我们服务端(包括数据库)都统一使用的是GBK 的编码,那会出现什么问题了?答案是:那会很棘手,服务端不得不对这个UTF-8的数据做编码转换,转换为了GBK才能入库,否则你的数据库里存入的就是 UTF-8的编码,乱码又出现了。
事实上这里还有个例外的情况,在W3C的标准中Form是有一个ACCEPT-CHARSET属性的,目的单独指定FORM中的编码,这个比文档的编 码设置优先级要高。我个人认为开发中用到的可能性不大,一个指定了编码A的文档,为何要发送编码B的字符呢?如果有真有这个需要,那么在服务端做转换就可 以了,没有必要在客户端弄出两种不同的编码,更何况IE这个强硬的家伙又是背离标准的。
A character encoding is a method of converting a sequence of bytes into a sequence of characters.
If ACCEPT-CHARSET is not specified, the form will be submitted in the character encoding specified for the document. If the form includes characters that fall outside the character set specified for the document, Microsoft Internet Explorer will attempt to determine an appropriate character set. If an appropriate character set cannot be determined, then the characters will be encoded as HTML numeric character references. For more information on character sets and numerical character references, see HTML Character Sets. (参考:MSNDhttp://msdn2.microsoft.com/en-us/library/ms533061.aspx)
因此,我们还是“忘记”这个属性吧,没有它,我们的FORM一样可以运转得很好。(也正是因为浏览器默认会给FORM做很多不错的工作,所以我们看到的很多富文本编辑器在提交编辑内容时,都会先把这个内容写入到一个表单的value中,让浏览器自动给它编码。)
第三个原则:清楚地认识Form中的数据的编码是依照网页本身编码的,在提交到服务端之前,这组数据是做了格式化处理,然后做了URL编码。目前服务端不会智能到自动识别FORM发送过来的编码类型,更不会智能到给你自动转换为服务端统一使用的编码。
四、 特别留意AJAX这个数据入口
AJAX的流行让XMLHTTPRequest这个“数据入口”使用更频繁了,但遗憾的是XMLHTTPRequest对象本身没有像浏览器对 Form的处理那么勤劳,同时它的无刷新特性让我们可以动态将服务端返回给我们的数据显示在当前已经被渲染过的页面内,相对于FORM这个数据入 口,XMLHTTPRequest还存在一个“数据出口”的问题,因此这里的乱码现象更加频繁。
1、“数据入口”
一般情况下,XMLHTTPRequest发送的数据来源有两种:页面源码和XMLHTTPRequest所在的Javascript的文件源码。为何会将它们分开了?这里我们来假设几种不同情况:
• JS获取用户在表单元素中的输入数据,然后设置XMLHTTPRequest
在上文中已经提到了Form表单中数据的编码问题,用户输入的字符编码是和网页编码一致的,因此这里JS获取的数据编码和网页编码是一致的。
• JS源码中预设的数据
某些情况下,我们会将一些预定参数在程序中写死(这里就不要讨论程序扩展性了),那么这种情况的编码就得取决于文件编辑时编辑器输出字符 的编码了。这就是我们的一个准则中建议的,你需要保持文件编码和字符编码的统一。JS本身是基于UNICODE编码的,所以这是我推荐文件用UTF-8编 码的理由之一。
现在我们已经知道了XMLHTTPRequest发送的数据的编码,接着我们来看发送数据会出现什么问题。
XMLHTTPRequest不同于表单数据的发送,它不会给我们的数据做序列化,不会自动给我们做URL编码,也不能在客户端指定发送的编码类 型。(PS:虽然setRequestHeader方法可以设置发送请求Header中的charset,不过我经过测试和对Header信息的监视,发 现这个设置从Header中看是有效的,但服务端接收到的数据始终是UTF-8的,查了些资料没有找到原因在哪儿,暂且认为XMLHTTPRequest 只能以UTF-8格式发送数据吧。但值得“庆幸”的是无论客户端的网页是什么编码,这里发送的UTF-8编码的数据都是正确转换过的,否则让我们自己在客 户端写转换功能那就相当棘手了)为了保证数据send到服务端不出问题,我们只有自己动手做XMLHTTPRequest没有给我们完成的工作了。
• 指定请求Header中的content-type
XMLHTTPRequest.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
• 将序列化的数据进行URL编码
这里可以使用encodeURI和encodeURIComponent
这样以来,那么服务端就可以正确接收到我们POST过去的数据集了,这里需要注意,服务端得到的数据集是UTF-8格式的,是否做编码转换,那就是视你具体情况而定了。
2、“数据出口”
无论XMLHTTPRequest GET的是一个静态页面,还是一个动态页面,XMLHTTPRequest都不关心,它只关心 responseText得到数据的编码,默认的编码是UTF-8,而这个编码是可以通过服务端直接设置的,这个在上文的指定网页编码中已经提到了。
因此当你需要在页面显示responseText的内容时,你需要很清楚地知道页面的编码和responseText的编码,如果他们不统一,那乱码的麻烦就有出来了,不过现在你知道问题在哪儿了。
第四个原则:在使用AJAX时,尤其要注意数据流动中造成的编码类型改变。AJAX发送的是UTF-8编码,接收的编码默认是UTF-8,但允许服务端重新指定。
总结:避免乱码的最简洁方法就是系统中所有文本文件都统一使用最通用的编码,(目前在WEB应用上当然是 UTF-8了)并在页面中指定charset,同时在服务端设置header中的charset,防止客户端浏览器的设置造成编码不一致。但如果你目前的 系统已经存在了编码不统一情况,那么你就需要根据具体情况找到做编码转换代价最小的地方,或许上面的分析能帮你解决问题。
以上分析我的测试环境是IE6 、IE7、Firefox,有兴趣的话也可以动手测测,欢迎交流,如果有什么不当之处希望指出。
同样,翻出以前的文章,当然,也是转贴的,不过对于PHP开发人员来说,BOM这个问题应该算是很严重的,经常性会遇到header already sent,但就是找不到有输出。什么原因呢?BOM应该占了很大的比例。所以还是翻出老文章,再加强一下记忆。
来源:http://www.goalercn.com/html/tech/program/c/157.html 作者:未知
这是一篇程序员写给程序员的趣味读物。所谓趣味是指可以比较轻松地了解一些原来不清楚的概念,增进知识,类似于打RPG游戏的升级。整理这篇文章的动机是两个问题:
问题一:
使用Windows记事本的“另存为”,可以在GBK、Unicode、Unicode big endian和UTF-8这几种编码方式间相互转换。同样是txt文件,Windows是怎样识别编码方式的呢?
我很早前就发现Unicode、Unicode big endian和UTF-8编码的txt文件的开头会多出几个字节,分别是FF、FE(Unicode),FE、FF(Unicode big endian),EF、BB、BF(UTF-8)。但这些标记是基于什么标准呢?
问题二:
最近在网上看到一个ConvertUTF.c,实现了UTF-32、UTF-16和UTF-8这三种编码方式的相互转换。对于Unicode(UCS2)、GBK、UTF-8这些编码方式,我原来就了解。但这个程序让我有些糊涂,想不起来UTF-16和UCS2有什么关系。
查了查相关资料,总算将这些问题弄清楚了,顺带也了解了一些Unicode的细节。写成一篇文章,送给有过类似疑问的朋友。本文在写作时尽量做到通俗易懂,但要求读者知道什么是字节,什么是十六进制。
0、big endian和little endian
big endian和little endian是CPU处理多字节数的不同方式。例如“汉”字的Unicode编码是6C49。那么写到文件里时,究竟是将6C写在前面,还是将49写在前面?如果将6C写在前面,就是big endian。还是将49写在前面,就是little endian。
“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了王位。
我们一般将endian翻译成“字节序”,将big endian和little endian称作“大尾”和“小尾”。
1、字符编码、内码,顺带介绍汉字编码
字符必须编码后才能被计算机处理。计算机使用的缺省编码方式就是计算机的内码。早期的计算机使用7位的ASCII编码,为了处理汉字,程序员设计了用于简体中文的GB2312和用于繁体中文的big5。
GB2312(1980年)一共收录了7445个字符,包括6763个汉字和682个其它符号。汉字区的内码范围高字节从B0-F7,低字节从A1-FE,占用的码位是72*94=6768。其中有5个空位是D7FA-D7FE。
GB2312支持的汉字太少。1995年的汉字扩展规范GBK1.0收录了21886个符号,它分为汉字区和图形符号区。汉字区包括21003个字符。2000年的GB18030是取代GBK1.0的正式国家标准。该标准收录了27484个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字。现在的PC平台必须支持GB18030,对嵌入式产品暂不作要求。所以手机、MP3一般只支持GB2312。
从ASCII、GB2312、GBK到GB18030,这些编码方法是向下兼容的,即同一个字符在这些方案中总是有相同的编码,后面的标准支持更多的字符。在这些编码中,英文和中文可以统一地处理。区分中文编码的方法是高字节的最高位不为0。按照程序员的称呼,GB2312、GBK到GB18030都属于双字节字符集 (DBCS)。
有的中文Windows的缺省内码还是GBK,可以通过GB18030升级包升级到GB18030。不过GB18030相对GBK增加的字符,普通人是很难用到的,通常我们还是用GBK指代中文Windows内码。
这里还有一些细节:
GB2312的原文还是区位码,从区位码到内码,需要在高字节和低字节上分别加上A0。
在DBCS中,GB内码的存储格式始终是big endian,即高位在前。
GB2312的两个字节的最高位都是1。但符合这个条件的码位只有128*128=16384个。所以GBK和GB18030的低字节最高位都可能不是1。不过这不影响DBCS字符流的解析:在读取DBCS字符流时,只要遇到高位为1的字节,就可以将下两个字节作为一个双字节编码,而不用管低字节的高位是什么。
2、Unicode、UCS和UTF
前面提到从ASCII、GB2312、GBK到GB18030的编码方法是向下兼容的。而Unicode只与ASCII兼容(更准确地说,是与ISO-8859-1兼容),与GB码不兼容。例如“汉”字的Unicode编码是6C49,而GB码是BABA。
Unicode也是一种字符编码方法,不过它是由国际组织设计,可以容纳全世界所有语言文字的编码方案。Unicode的学名是"Universal Multiple-Octet Coded Character Set",简称为UCS。UCS可以看作是"Unicode Character Set"的缩写。
根据维基百科全书(http://zh.wikipedia.org/wiki/)的记载:历史上存在两个试图独立设计Unicode的组织,即国际标准化组织(ISO)和一个软件制造商的协会(unicode.org)。ISO开发了ISO 10646项目,Unicode协会开发了Unicode项目。
在1991年前后,双方都认识到世界不需要两个不兼容的字符集。于是它们开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode2.0开始,Unicode项目采用了与ISO 10646-1相同的字库和字码。
目前两个项目仍都存在,并独立地公布各自的标准。Unicode协会现在的最新版本是2005年的Unicode 4.1.0。ISO的最新标准是10646-3:2003。
UCS规定了怎么用多个字节表示各种文字。怎样传输这些编码,是由UTF(UCS Transformation Format)规范规定的,常见的UTF规范包括UTF-8、UTF-7、UTF-16。
IETF的RFC2781和RFC3629以RFC的一贯风格,清晰、明快又不失严谨地描述了UTF-16和UTF-8的编码方法。我总是记不得IETF是Internet Engineering Task Force的缩写。但IETF负责维护的RFC是Internet上一切规范的基础。
3、UCS-2、UCS-4、BMP
UCS有两种格式:UCS-2和UCS-4。顾名思义,UCS-2就是用两个字节编码,UCS-4就是用4个字节(实际上只用了31位,最高位必须为0)编码。下面让我们做一些简单的数学游戏:
UCS-2有2^16=65536个码位,UCS-4有2^31=2147483648个码位。
UCS-4根据最高位为0的最高字节分成2^7=128个group。每个group再根据次高字节分为256个plane。每个plane根据第3个字节分为256行 (rows),每行包含256个cells。当然同一行的cells只是最后一个字节不同,其余都相同。
group 0的plane 0被称作Basic Multilingual Plane, 即BMP。或者说UCS-4中,高两个字节为0的码位被称作BMP。
将UCS-4的BMP去掉前面的两个零字节就得到了UCS-2。在UCS-2的两个字节前加上两个零字节,就得到了UCS-4的BMP。而目前的UCS-4规范中还没有任何字符被分配在BMP之外。
4、UTF编码
UTF-8就是以8位为单元对UCS进行编码。从UCS-2到UTF-8的编码方式如下:
UCS-2编码(16进制) UTF-8 字节流(二进制)
0000 - 007F 0xxxxxxx
0080 - 07FF 110xxxxx 10xxxxxx
0800 - FFFF 1110xxxx 10xxxxxx 10xxxxxx
例如“汉”字的Unicode编码是6C49。6C49在0800-FFFF之间,所以肯定要用3字节模板了:1110xxxx 10xxxxxx 10xxxxxx。将6C49写成二进制是:0110 110001 001001, 用这个比特流依次代替模板中的x,得到:11100110 10110001 10001001,即E6 B1 89。
读者可以用记事本测试一下我们的编码是否正确。
UTF-16以16位为单元对UCS进行编码。对于小于0x10000的UCS码,UTF-16编码就等于UCS码对应的16位无符号整数。对于不小于0x10000的UCS码,定义了一个算法。不过由于实际使用的UCS2,或者UCS4的BMP必然小于0x10000,所以就目前而言,可以认为UTF-16和UCS-2基本相同。但UCS-2只是一个编码方案,UTF-16却要用于实际的传输,所以就不得不考虑字节序的问题。
5、UTF的字节序和BOM
UTF-8以字节为编码单元,没有字节序的问题。UTF-16以两个字节为编码单元,在解释一个UTF-16文本前,首先要弄清楚每个编码单元的字节序。例如收到一个“奎”的Unicode编码是594E,“乙”的Unicode编码是4E59。如果我们收到UTF-16字节流“594E”,那么这是“奎”还是“乙”?
Unicode规范中推荐的标记字节顺序的方法是BOM。BOM不是“Bill Of Material”的BOM表,而是Byte Order Mark。BOM是一个有点小聪明的想法:
在UCS编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在实际传输中。UCS规范建议我们在传输字节流前,先传输字符"ZERO WIDTH NO-BREAK SPACE"。
这样如果接收者收到FEFF,就表明这个字节流是Big-Endian的;如果收到FFFE,就表明这个字节流是Little-Endian的。因此字符"ZERO WIDTH NO-BREAK SPACE"又被称作BOM。
UTF-8不需要BOM来表明字节顺序,但可以用BOM来表明编码方式。字符"ZERO WIDTH NO-BREAK SPACE"的UTF-8编码是EF BB BF(读者可以用我们前面介绍的编码方法验证一下)。所以如果接收者收到以EF BB BF开头的字节流,就知道这是UTF-8编码了。
Windows就是使用BOM来标记文本文件的编码方式的。
6、进一步的参考资料
本文主要参考的资料是 "Short overview of ISO-IEC 10646 and Unicode" (http://www.nada.kth.se/i18n/ucs/unicode-iso10646-oview.html)。
我还找了两篇看上去不错的资料,不过因为我开始的疑问都找到了答案,所以就没有看:
1."Understanding Unicode A general introduction to the Unicode Standard" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter04a)
2."Character set encoding basics Understanding character set encodings and legacy encodings" (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=IWS-Chapter03)
顺便说一下:记事本保存成UTF8默认是带BOM的,好象没有办法去掉;editplus在选项里可以进行选择:始终带BOM,智能识别,不带BOM这些选项。所以,尽量是选择不带BOM比较好。