编码检查 UTF-8 浏览器编码

编码问题一直是做网站开发的工程师们很头疼的事,如果你希望自己的网站能够被更多不同语言环境的人浏览和使用,那就一定要解决好编码的问题。

我的经验是,从HTML页面的编码,到PHP程序文件的编码,到数据库的设计以及与PHP之间连接的编码,全部使用UTF-8,这样就能保证你的页面不会出现乱码。

不要总是觉得,你的网站是给中国人用的,不给其他国家的人使用。如果你的中国用户希望保存一些日文、韩文等亚洲文字, 你也没有理由拒绝吧?

还是在一开始就解决好编码的问题为好,做网站,免不了这一步,今天不做,以后迟早要还的。

如果你认为我说的毫无道理,那我给大家引用一篇W3C组织的官方说明吧:

http://www.w3.org/International/questions/qa-forms-utf-8.en.php

注意回答部分的第一句:The best way to deal with encoding issues in (X)HTML forms is to serve all your pages in UTF-8.

用UTF-8吧,肯定没错的,你可以避免很多很多问题。

不过,就算页面使用了UTF-8,也还是会遇到一个问题:

浏览器接收编码的转换问题。

就这个问题的说明,我做了一个测试页面,可以在我的Google SVN下载:

http://leakon.googlecode.com/svn/trunk/leakon/php/detect_utf8/

在浏览器里打开test.php页面即可。

这个页面是UTF-8编码的,如果你不是用SVN软件CheckOut而是直接复制的源码,请记得要把test.php这个文件保存成UTF-8格式。

这个页面有一个Form表单,你可以在输入框内输入中文,然后看地址栏里word字段的值,一个汉字对应3个%,因为UTF-8是变长编码的,针对不同的文字,%的数量是1-4个。

如果你熟悉GBK编码的页面,应该注意到每个汉字对应2个%,因为汉字都是双字节编码的。

这个时候,如果你足够细心,应该可以发现一个冲突的问题。

如果,我在浏览器的地址栏的word字段后面直接输入汉字,得到的是什么结果呢?给大家一个提示,这里最好使用Firefox浏览器,如果你在地址栏输入中文,Firefox会按照GBK的编码方式,按双字节编码,也就是一个汉字对应2个%。

可是你的页面默认是接收UTF-8编码的字符的,给你一个GBK编码,你会解析成乱码。

很多网站存在这个问题,包括Google和Yahoo。 他们只对各自的中文网站就解决了这个问题,英文的和日文的都没有处理。下面我们逐一测试一下:

我们给一个测试用例,中文:百度;GBK:%B0%D9%B6%C8;UTF-8:%E7%99%BE%E5%BA%A6。

看到了吧,GBK是4个%,UTF-8是6个%。

  1. http://www.google.cn/search?hl=zh-CN&q=%E7%99%BE%E5%BA%A6&btnG=Google+%E6%90%9C%E7%B4%A2&meta=
  2. http://www.google.cn/search?hl=zh-CN&q=%B0%D9%B6%C8&btnG=Google+%E6%90%9C%E7%B4%A2&meta=
  3. http://yahoo.cn/s?p=%E7%99%BE%E5%BA%A6&v=web
  4. http://yahoo.cn/s?p=%B0%D9%B6%C8&v=web
  5. http://www.google.com/search?hl=en&q=%E7%99%BE%E5%BA%A6&btnG=Google+Search
  6. http://www.google.com/search?hl=en&q=%B0%D9%B6%C8&btnG=Google+Search
  7. http://search.yahoo.com/search?p=%E7%99%BE%E5%BA%A6&fr=yfp-t-501&toggle=1&cop=mss&ei=UTF-8&vc=&fp_ip=CN
  8. http://search.yahoo.com/search?p=%B0%D9%B6%C8&fr=yfp-t-501&toggle=1&cop=mss&ei=UTF-8&vc=&fp_ip=CN
  9. http://www.google.co.jp/search?hl=ja&newwindow=1&q=%E7%99%BE%E5%BA%A6&btnG=%E6%A4%9C%E7%B4%A2&lr=
  10. http://www.google.co.jp/search?hl=ja&newwindow=1&q=%B0%D9%B6%C8&btnG=%E6%A4%9C%E7%B4%A2&lr=
  11. http://search.yahoo.co.jp/search?p=%E7%99%BE%E5%BA%A6&ei=UTF-8&fr=sfp_as&x=wrt
  12. http://search.yahoo.co.jp/search?ei=UTF-8&fr=sfp_as&p=%B0%D9%B6%C8&meta=vc%3D

以上是对Google和Yahoo两大搜索引擎的中文、英文、日文网站进行UTF-8、GBK编码的访问。

可以看到,2家公司只对中文网站做了编码检查,发现不是UTF-8就对关键词进行编码转换,得到了正确的结果,英文和日文都没有处理,给GBK编码时得到的是乱码。

他们就忽略了英文和日文用户搜索中文的需求。

其实,这个问题不是不可以解决的,中文网站都做到了,其他语言的怎么就不行呢?

这个不讨论了,还是说说怎么检查UTF-8编码吧。

其实就是一段正则表达式:

$regex = ‘/^(‘
. ‘[\x09\x0A\x0D\x20-\x7E]|’ # ASCII
. ‘[\xC2-\xDF][\x80-\xBF]|’ # non-overlong 2-byte
. ‘\xE0[\xA0-\xBF][\x80-\xBF]|’ # excluding overlongs
. ‘[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|’ # straight 3-byte
. ‘\xED[\x80-\x9F][\x80-\xBF]|’ # excluding surrogates
. ‘\xF0[\x90-\xBF][\x80-\xBF]{2}|’ # planes 1-3
. ‘[\xF1-\xF3][\x80-\xBF]{3}|’ # planes 4-15
. ‘\xF4[\x80-\x8F][\x80-\xBF]{2}’ # plane 16
. ‘)*\z/x’;

大家从SVN里可以下载源码。

我封装了一个类,DetectUT8,用于检测和转换编码。

过程是这样的:如果检查到不是UTF-8编码,就进行GBK -> UTF-8的转换。因为在地址栏输入的非ASCII字符都会按照GBK编码。

转换函数会检查系统是否启用了 mbstring 函数库,如果没有,则改用 iconv 转换。

这里要注意一点,不要把UTF-8写成utf8,也不要写成其他格式,iconv 的检查比较严格。

如果内置了 mbstring 函数,还是用 mb_detect_encoding 比较保险,但经测试表明,用上面这段正则,编码检测的成功率和 mb_detect_encoding 没有差别。

还要重申一个问题,编码检测的可靠性不是 100%,但应用这种方法已经极大地改善了用户体验,建议大家都采用这种方式。

PHP 文件锁 flock 负载均衡

最近有个项目,采用单台前端服务器提供Web服务,程序需要实时访问后端服务器。后端一共有几十台服务器,但有压力限制,单台负载不能过高,必须做负载均衡。

最简单的方式是用随机数,前端来请求的时候,随机挑选一台后端服务器,但这并不能保证压力平均分布,很有可能在某一段时间内请求都落到同一台服务器上,很容易导致这台服务器停止服务。

后来想到用文件锁的方式,来标记访问计数,顺序访问后端的每一台服务器,让每一台服务器一个周期只被访问一次。

在进行了多次功能测试和压力测试后,验证了这种想法的可行性,然后写了一个IDService类,封装了整个过程。

我在Google提供的SVN服务器上保存了源码,大家可以在

http://leakon.googlecode.com/svn/trunk/leakon/php/flock/flock.php

这个地址看到源码,或者用SVN工具CheckOut到本地。

核心过程,就是初始化的时候给一个ID范围,默认是从0开始,如果你的server_count是32,那么调用getId()方法的时候,我会顺序给你31至0这32个ID,采用文件锁就是考虑到并发请求之间彼此独立,一个进程读数据文件的时候要加独占锁,解锁前,其他进程无法读取数据文件。

ID分配给你了,每个ID对应哪个服务器,就是你自己做映射的事了,保证了这个模块的无关性和独立性,和其他所有模块保持无耦合。

这是在PHP5的环境下写的,const 定义了3个类常量:

LINE_FEED 是换行符,Windows 下是 \r\n,Linux 下是 \n,只是为了方便测试的时候实时查看数据,可以是任意字符,只要不是数字就OK;

MAX_LOAD 是计数器的最大值,计数器都是从0开始,如果有任何一个ID达到了最大值,则所有ID计数器全部归零,开始新的一轮计数,其实这个设置只要大于0就可以,最好不要太大,因为存储数字也是要占用存储空间的,越小,id_data_file的尺寸就越小,硬盘读取就越快;

DATA_BLOCK 是设置一次读文件的数据大小,硬盘的一个文件至少要占一个簇,一般文件系统一个簇是4K,这个取值要跟ID的总量有关系,如果你的LINE_FEED是\n,MAX_LOAD是99(采用文本方式存储,2字节),那么一个ID占用3个字节,如果你有100个ID,那么数据文件占用空间就是300Byue,因此DATA_BLOCK设置为300是最佳值,需要注意,如果ID范围变大,需要同步更改此值,因此我默认设置了2048字节,小于硬盘的一个簇,相对于300字节来说没有性能损失,因为都在一个簇内,数据存储是连续的,硬盘只需一次寻道和一次读写。

源码里有使用说明,很简单,在实例化对象的时候指定ID范围和数据文件位置即可。

已经经过测试,给一些压力测试数据吧:

在AMD3000+和7200转80G硬盘的台式机环境,可以提供到 1300+次/秒 的速度,此时磁盘IO是瓶颈;
换上Linux服务器,具体配置不太清楚,反正是SCSI硬盘,100多G,只是开发用机,性能并不高,但可以提供 5000+次/秒 的速度。

综合2中环境的测试数据,以目前前端服务器的最高负载(最高也就 200+次/秒),以及项目的实际负载,此代码性能足够满足需要。

终于解决favicon.ico无法显示的问题

很久以前就做好了favicon.ico,在浏览器的地址栏前面也显示正常.但是收藏在Firefox的书签工具栏里的时候,这个图标无法显示,所以显示的是空白页: .

我以为是Firefox2.0的问题,因为我用Firefox3.0beta的时候图标显示的非常好,只要在书签工具栏里收藏了这个网站,马上就会显示图标.所以我认为是Firefox2.0的bug.

今天sofish做了一个博客圈的RSS聚合.我再次发现favicon的问题,我的图标没有显示.我开始想,也许不是Firefox的问题.因为Firefox2.0显示别人网站的favicon都没问题,但是我的独独出错.可能是我的某些地方没有写标准.

我先把注意力放在了代码的书写上.

<link rel=”Shortcut Icon” href=”地址” type=”image/x-icon” />

<link rel=”shortcut” href=”/favicon.ico” />

<LINK href=”ico地址” rel=”SHORTCUT ICON”>

有以上这几种写法,都差不多.而且,如果favicon.ico放在根目录之下的话,可以省略这些代码.如果不在根目录下,就要给出地址了.我的代码没问题,ico的名字也是favicon.ico,放在根目录下, 很规范,所以问题不是出在这上面.

然后我就想到了难道是ico文件的问题.这个是我自己用photoshop画的,然后转成了ico,可能对于网络图标的有些规范或者约定俗成的东西没有注意.然后我打开了几个显示良好的blog的ico文件,还有我的ico文件,查看属性.

我首先以为是尺寸的问题,但是发现没有区别,都是16*16.再对比一下,发现问题可能出在ico文件的大小上面.一般的favicon.ico只有1-2K,我的有20+K.所以我换了漫步的图标,试试,硬刷新以后,书签栏上的书签显示良好了

我的结论是,favicon的大小虽然没有被规定应该是多大,但是还是小一点比较好,Firefox3.0还是beta版的浏览器,以后的网络可能会对大的favicon.ico更加宽容,但是现在,还是约定用小一点的图标吧.加载也快一点.

你的favicon显示有问题吗?看看它的大小是否合适吧.还有问题?留言告诉我吧

安装php5出现 mysql configure failed

安装PHP5时出现了错误。
configure: error: mysql configure failed. Please check config.log for more information.
./configure –prefix=/usr/local/php –with-apxs2=/usr/local/apache2/bin/apxs –with-gd –with-jpeg-dir –with-zlib-dir –with-png-dir –with-ttf –with-mysql enable-track-vars 出现的问题。

如果是自己选择GZ包安装的MYSQL。这里要加路径。
加的方法为–with-mysql-dir=/user/local/mysql
切记不可写为:–with-mysql=/user/local/mysql

Web在线任务管理

今天终于找到一个好用的在线任务管理网站:

Remember The Milk

http://www.rememberthemilk.com/

中文名怎么翻译呢?别忘了喝奶?

挺有意思的,小奶牛的图标很可爱,一下就吸引了我,让我决心好好研究一番。

是我自己要寻找这样的网站,最近好忙,每天都有好多工作要做,还有好多事想做。事情总是这样,有时候突然想做点什么,但工作很忙,没有时间;等周末休息的时候,有时间了,却突然不知道该做点什么。

工作的时候,会有很多很多任务,如何安排这些事,每件事应该在什么时候完成,都应该有预先的安排。新的工作环境,对我有很大影响。这里做事都必须有安排。

每周的开始,都要列出本周要完成的任务,以及每个任务的时间点。而且要明确任务结果,必须设置预期结果。

比如,今天是周一,制定本周计划:

  1. 某项目的升级详细设计,周三完成文档,周五完成评审;
  2. 某项目的升级模块,周二完成开发,周三完成测试,周四完成上线;

注意“完成”两个字,每件事,都要早明确的时间点,取得明确的结果。

这里不能使用应该、差不多、也许、大概和基本等等模糊的词汇。

现在我的工作状态越来越好,我觉得这背后的原因,就是做事有安排,合理分配,按时完成。

不过,我还缺少一个工具,来帮我规划个人任务。

以前我是写便签纸,就是黄色的小方块,上面有一条不干胶,记录我今天要做什么。

便签容易写,但不容易改,小小的空间,记不下什么东西,而且不容易保存,过后也想不起来都做了什么。

以前有人教我用Outlook的任务管理来提醒我什么时候该做什么。

我用着不习惯,因为这些都是提醒工具,而互联网工作的性质就是要应对突如其来的各种事件。

就算我订好了下午3点开始写文档,但可能2点半的时候发现线上服务的Bug,必须立即修复,等改完的时候已经是4点半了。当然,3点的时候在屏幕上出现提示框提醒我写文档,但那个时候我可能去理会可能去处理吗?事情过去了,很可能我就会忘了,也许直到第二天早上才会想起来昨天的事情没做完。

这类例子不用再举了,大家肯定都有各自的经历。

今天上午,搜了一下在线任务管理,找到了一个网站列表:

19个在线个人任务管理系统

我一眼就看见了这个RememberTheMilk,小奶牛还挺显眼的。

进去试用了一下,都是Ajax开发的,用户体验很好,可以按标签区分不同类型的任务,每个任务可以指定不同的优先级,还有详细的设置,感觉很好用。

我把一天的工作都写了进去,然后一项一项做,做完一样改一下状态,再做另一个,感觉一天都很有条理,也不会遗漏细小的任务。

具体的设计,很人性化,真的是个不错的工具。

我喜欢这样的网站。

Remember The Milk

简单快捷地管理日常任务。
设计优良的界面让您管理待办事务更加愉快。您甚至可以使用类似下周五两周内来定义事务到期时间。当然您也可以使用键盘快捷方式加速您的日程管理。

Remember The Milk

提醒,无处不在。
通过电邮、短信和即时通讯软件(支持AIM, Gadu-Gadu, Google Talk, ICQ, Jabber, MSN, Skype 和 Yahoo! )接收提醒。

Remember The Milk

按您的方式管理信息。
您可以根据需要创建任意多个列表。所有事件都可以定义标签,并且您可以通过“标签云”看到需要待办的事务。另外,您也可以针对事件创建便签记事。

Remember The Milk

定位您的任务。
使用地图来查看您的任务在现实世界所处的地点,查看他们是否就在附近,并且有计划的以最佳方式完成任务。

Remember The Milk

合作搞掂。
通过你的联络对象或整个世界分享、邮寄和发布任务与列表。提醒亲友做家务。

Remember The Milk

无论何时何地添加任务。
添加任务像发射一封邮件(甚至是打电话)一样简单。在网上查看一个重要的约会?用快速添加项将它添加到你的列表中。

Remember The Milk

随时随地访问你的待办事务。
用你支持web的手机来查看任务。打印表现未来工作的整个列表或者一份星期计划手册。用Apple iCal或者Google Calendar在你的日历中查看工作。订阅使用Atom/RSS的feeds。

Remember The Milk

分配您的时间。
看看今明两天该做的事与你错过未做的事情。设定其为优先,估计你的时间并作适当的推迟。设定每周重复的事情或者两个月后还要做的事情。

Remember The Milk

智能搜索。
您可以使用高级搜索功能查找事件。同时还可以将搜索保存成为智能列表,方便以后查看。

Remember The Milk

享受有条理的生活。
“撤消”功能意味着您再也不必担心操作失误。因此您可以放心地注册账户,并开始使用Remember The Milk。

澄清Java 接口与继承

计算机学院研二的兄弟与我讨论Java,一见面,几个问题全是关于接口,接口有什么用?为什么要用接口?什么时候该使用接口?很庆幸他们不是问我Java如何连接SQL Server,或者是如何开发J2EE应用,这类问题有杀伤力,避之则吉。今年计算机学院本科有个毕业设计课题是做J2ME,选这个题目的学生在5月末都还在苦着脸研究java.util.*这个包,这个这个……唉。

大多数人认为,接口的意义在于顶替多重继承。众所周知Java没有c 那样多重继承的机制,但是却能够实作多个接口。其实这样做是很牵强的,接口和继承是完全不同的东西,接口没有能力代替多重继承,也没有这个义务。接口的作用,一言以蔽之,就是标志类的类别(type of class)。把不同类型的类归于不同的接口,可以更好的管理他们。OO的精髓,我以为,是对对象的抽象,最能体现这一点的就是接口。为什么我们讨论设计模式都只针对具备了抽象能力的语言(比如c 、java、c#等),就是因为设计模式所研究的,实际上就是如何合理的去抽象。(cowboy的名言是“抽象就是抽去像的部分”,看似调侃,实乃至理)。

设计模式中最基础的是工厂模式(Factory),在我最近的一个很简单的应用中,我想尽量的让我的程序能够在多个数据库间移植,当然,这涉及很多问题,单是如何兼容不同DBMS的SQL就让人头痛。我们不妨先把问题简单化,只考虑如何连接不同的数据库。

假设我有很多个类,分别是Mysql.java、SQLServer.java、Oracle.java、DB2.java,他们分别连接不同的数据库,统一返回一个Connection对象,并且都有一个close方法,用于关闭连接。只需要针对你的DBMS,选择不同的类,就可以用了,但是我的用户他会使用什么数据库?我不知道,我希望的是尽量少的修改代码,就能满足他的需要。我可以抽象如下接口:
package org.bromon.test;
public interface DB
{
java.sql.Connection openDB(String url,String user,String password);
void close();
}

这个接口只定义两个方法,没有任何有实际意义的代码,具体的代码由实作这个接口的类来给出,比如Mysql.java:

Package org.bromon.test;
import java.sql.*;
public class Mysql implements DB
{
private String url=”jdbc:mysql:localhost:3306/test”;
private String user=”root”;
private String password=””;
private Connection conn;
public Connection openDB(url,user,password)
{
//连接数据库的代码
}

public void close()
{
//关闭数据库
}
}

类似的当然还有Oracle.java等等,接口DB给这些类归了个类,在应用程序中我们这样定义对象:

org.bromon.test.DB myDB;

使用myDB来操作数据库,就可以不用管实际上我所使用的是哪个类,这就是所谓的“开-闭”原则。但是问题在于接口是不能实例化的,myDB=new DB(),这样的代码是绝对错误的,我们只能myDB=new Mysql()或者myDB=new oracle()。麻烦了,我还是需要指定具体实例化的是哪个类,用了接口跟没用一样。所以我们需要一个工厂:

package org.bromon.test;
public class DBFactory
{
public static DB Connection getConn()
{
Return(new Mysql());
}
}

所以实例化的代码变成:myDB=DBFactory.getConn();
这就是23种模式中最基础的普通工厂(Factory),工厂类负责具体实例化哪个类,而其他的程序逻辑都是针对DB这个接口进行操作,这就是“针对接口编程”。责任都被推卸给工厂类了,当然你也可以继续定义工厂接口,继续把责任上抛,这就演变成抽象工厂(Abstract Factory)。

整个过程中接口不负责任何具体操作,其他的程序要连接数据库的话,只需要构造一个DB对象就OK,而不管工厂类如何变化。这就是接口的意义—-抽象。

继承的概念不用多说,很好理解。为什么要继承呢?因为你想重用代码?这绝对不是理由,继承的意义也在于抽象,而不是代码重用。如果对象A有一个run()方法,对象B也想有这个方法,所以有人就Class B extends A。这是不经大脑的做法。如果在B中实例化一个A,调用A的Run()方法,是不是可以达到同样的目的?如下:
Class B
{
A a=new A();
a.run();
}

这就是利用类的聚合来重用代码,是委派模式的雏形,是GoF一贯倡导的做法。

那么继承的意义何在?其实这是历史原因造成的,最开始的OO语言只有继承,没有接口,所以只能以继承来实现抽象,请一定注意,继承的本意在于抽象,而非代码重用(虽然继承也有这个作用),这是很多Java烂书最严重的错误之一,它们所造成的阴影,我至今还没有完全摆脱,坏书害人啊,尤其是入门类的,流毒太大。什么时候应该使用继承?只在抽象类中使用,其他情况下尽量不使用。抽象类也是不能实例化的,它仅仅提供一个模版而已,这就很能说明问题。

软件开发的万恶之源,一是重复代码而不是重用代码,二是烂用继承,尤以c 程序员为甚。Java中取缔多重继承,目的就是制止烂用继承,实是非常明智的做法,不过很多人都不理解。Java能够更好的体现设计,这是让我入迷的原因之一。

Symfony 如何在项目中使用多个数据库

all:
database1:
class: sfPropelDatabase
param:
dsn:  pgsql://foo:bar@hostname/database1

database2:
class: sfPropelDatabase
param:
dsn: mysql://foo:bar@hostname/database2

先建立一个
PROJECT_DIR/config/database1.schema.xml
再建立一个
PROJECT_DIR/config/database2.schema.xml

最后build一下:

$ symfony propel-build-model

$database1_connection_handler = Propel::getConnection(FooPeer::DATABASE_NAME);
$database2_connection_handler = Propel::getConnection(BarPeer::DATABASE_NAME);

PHP require 绝对路径 autoload

在写PHP程序时,如果文件比较多,目录也比较多,有很多require_once的情况。

如果程序的入口不唯一,并且分布在不同的目录,必须require绝对路径,如果是相对路径,PHP会把入口文件的路径作为基本路径,require里的相对路径都是相对于入口文件的。

这样带来最大的问题就是自己部署的程序,给别人提供接口时,别人根本无法使用!

所以要用绝对路径。

做法就是选一个require的入口文件,大家都要包含这个文件,这个文件的开头加上这么一句:

这个时候PROJECT_BASE_PATH就是从根开始的绝对路径了。

后续的所有require文件,都用这个宏定义去拼接,例如:

就绝对保证不会再出错!

当然,如果你是PHP5的环境,推荐使用__autoload函数,把所有功能都封装到类中,然后在需要的时候自动调用,避免到处require。

数据库导入 乱码 数据被截断 数据不全

今天碰到一个Case,是数据库导入的,很顺利地解决了,跟大家分享一下过程。

环境:

数据源服务器

mysql>show variables like ‘char%’;
character_set_client gbk
character_set_connection gbk
character_set_database gbk
character_set_results gbk
character_set_server gbk
character_set_system utf8

所有环境都是gbk,但备份脚本是这样的:
mysqldump my_db_name /home/leakon/db_bak/dump.sql

打开dump.sql,里面的中文字符都变成乱码了,在这个文件的头部,可以看到这么一行:
/*!40101 SET NAMES utf8 */;

也就是说,mysql在导出的时候,按照utf8字符集,进行了编码转换。

你可以这么理解,你和我都是中国人,说汉语,但我们之间沟通的时候用了一个翻译,这个翻译是英国人。

我就是那个数据源,英国的翻译就是dump.sql,我要跟你说中国话, 但翻译先把汉语翻译成英语,存在他的脑子里了。

这个时候你看他的内容,是没法理解的,这就是你看到的乱码。

下面,要把导出的数据,导入到新数据库中。

注意了,应该用下面的脚本:
mysql –default-character-set=latin1 -uroot my_new_db < dump.sql

我指定了latin1,作为连接mysql服务器的字符集。

实际上,导入的过程,就是让那个英国翻译,把英语再转换成汉语告诉你。这时,指定了latin1字符集, 就是告诉翻译,你让他把脑子里存的话,用英语翻译成汉语告诉你。

这里必须指定latin1,我也很奇怪为什么不指定utf8,我试过,不成功,服务器提示不支持这种编码。

具体原因我还会再研究,目前解决办法就是这样了。

指定字符集,就是让翻译按英语->汉语的转换程序进行翻译,否则,翻译可能是按英语->法语的顺序转换的,也就证明了,为什么导入数据库时,不指定编码,会是奇怪的乱码字符。

这里还有一个重要因素,就是数据库的字符集。

这个 my_new_db 创建的时候指定的是 gbk 字符集,建表语句是:
CREATE DATABASE my_new_db DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci;

如果数据库的字符集和源数据库不一样,那么,在导入时,很肯能出现导入到一半,MySQL报语法错,或者本来10万行数据只导入了2万行就停了。

原因就是编码转换时解析引擎会把其中的某些字符理解为引号等字符,导致语法错乱。

其实,有彻底解决这些编码问题的办法,那就是保证所有过程都指定正确的编码。

下面就以gbk为例:

  1. 源数据库,数据库、表和字符串字段的编码统一设定为gbk
  2. PHP调用程序时,在创建MySQL连接后立即发送一条 set names ‘gbk’ 语句
  3. 导出时指定gbk字符集:mysqldump -uroot –opt –default-character-set=gbk my_db_name > dump.sql
  4. 检查文件内容,应该是正确编码的字符,用普通文本编辑器可以阅读
  5. 目标数据库建库脚本:CREATE DATABASE my_new_db DEFAULT CHARACTER SET gbk COLLATE gbk_chinese_ci
  6. 导入时:mysql –default-character-set=gbk -uroot my_new_db < dump.sql

就这几个简单的步骤,就完全避免编码问题!

如果你要用utf8编码,把上述脚本的gbk都替换成utf8,把 gbk_chinese_ci 替换成 utf8_general_ci。

再重新导入,编码没问题,数据也完全导入了吧。