首页 > 转乱码UTF8和UTF-8网页编码

转乱码UTF8和UTF-8网页编码

http://www.lovelucy.info/utf8-vs-utf-8.html#more-794

一、遇到的问题

曾经被字符集间复杂的转换搞怕了,正好新项目要求国际化,需要能够显示多种语言,于是一开始就规定统统使用 UTF-8 编码。

  1. 所有代码文件使用 UTF-8 编码存盘
  2. MySQL数据库所有表,所有字段设置 Collation (中文翻译为“整理”?)属性为 “utf8_general_ci”
  3. 所有页面输出
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">

即便是这样,PHP 从数据库中读取内容,显示到网页上,还是出现了乱码,英文没问题,中文统统都是?问号。这样也行?艰苦卓绝的 debug 开始了……

开始debug

二、调查原因

MySQL 的字符集以繁多而著名,而其默认又是 latin1 的瑞典语编码,数据导入导出的时候一不留神就乱码了。

参考以上链接的官方文档,总结之:MySQL 对于字符集的支持细化到四个层次:

  1. server 服务器级
  2. database 数据库级
  3. table 表级
  4. connection 连接级

确保每一个级别都是使用的 UTF-8 编码。检查了一下,貌似我没有设置 connection 连接级。前 三种字符集级别只是规定了数据存储在 MySQL 中的编码格式,客户端读出数据后完全可以按照自己的意愿来解读数据。最后的 connection 连接级就是规定了客户端以什么编码来解析读取到的数据。也就是说,不论是 php 代码还是 DB 管理软件,在从 MySQL 读取数据之前都需要设定自己作为客户端的编码格式。

思考中

好吧,那么,在任何查询执行之前,先执行一句 set names utf-8。(使用框架进行开发的话,大多数框架应该会自动完成这一步,程序员一般只需要改配置文件)

$conn = mysql_connect($db_host, $db_user, $db_password);

if(!$conn)

die("Could not connect to mysql.");

mysql_select_db($db_name);

mysql_query("set names 'utf-8'");

刷新页面,仍旧乱码。

泪流满面

三、解决

只好再继续调查。¥#$…&%=^*&+%-#!@_@ 苦逼地一天就这样过去了……

咦,等等,官方文档里写的是 set names utf8 哦,和 set names utf-8 有啥区别么?赶紧试一下。

刷新页面,我擦,正常了。

WTF!!

搜了一下,发现被坑的人还真不少。UTF-8应该是标准的写法,在大多数场合都是有中间那个横杠的,只是MySQL这里偏偏就非主流去掉了横杠使用UTF8。

遇到同样问题,而本文未能帮你解决的,这篇乱码总结可能会帮到你。

 

//以下是讨论内容

今天看到大家在讨论,发现这是个很严重而又容易疏忽的问题,我以前也一直是用set names,遂记录下来,也提醒自己一把。

应该使用 mysql_ set_ charset(); 不要使用sql query来设置,有风险。

1.set names与mysql_set_charset有什么区别?

一般情况下, 使用”SET NAMES”就足够了, 也是可以保证正确的. 那么为什么手册又要说推荐使用 mysqli_set_charset(PHP>=5.0.5)呢。手册里面也没有明确说明。我们可以看下php扩展的源代码:

//php-5.2.11-SRC/ext/mysqli/mysqli_nonapi.c line 342

PHP_FUNCTION(mysqli_set_charset)

{

MY_MYSQL *mysql;

zval *mysql_link;

char *cs_name = NULL;

unsigned int len;

if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis()

, “Os”, &mysql_link, mysqli_link_class_entry, &cs_name, &len) == FAILURE) {

return;

}

MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, “mysqli_link”, MYSQLI_STATUS_VALID);

if (mysql_set_character_set(mysql->mysql, cs_name)) {

//** 调用libmysql的对应函数

RETURN_FALSE;

}

RETURN_TRUE;

}

可以看到php的mysql扩展是直接调用了mysql的mysql_set_character_set函数,接下来看看mysql的代码

//mysql-5.1.30-SRC/libmysql/client.c, line 3166:

int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name)

{

struct charset_info_st *cs;

const char *save_csdir= charsets_dir;

if (mysql->options.charset_dir)

charsets_dir= mysql->options.charset_dir;

if (strlen(cs_name) < MY_CS_NAME_SIZE &&

(cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0))))

{

char buff[MY_CS_NAME_SIZE + 10];

charsets_dir= save_csdir;

/* Skip execution of "SET NAMES" for pre-4.1 servers */

if (mysql_get_server_version(mysql) charset= cs;

}

}

//以下省略

可以看到,除了调用real_query设置set names,还设置了mysql的charset变量。

2.这样有什么影响?

mysql_real_escape_string会受到影响,它与mysql_escape_string的区别就 是, 它会考虑”当前”字符集。如果仅仅使用set names,mysql_real_escape_string可能会失效。

例子:

$mysqli = new mysqli(“localhost”, “user”, “pass”, “test”, 3306);

/* check connection */

if (mysqli_connect_errno()) {

printf(“Connect failed: %s ”, mysqli_connect_error());

exit();

}

$mysqli->query(‘SET NAMES gbk’); //使用set names设置字符集

$city = chr(0xbf).chr(0x5c); //0xbf5c是个有效的gbk字符,模拟用户输入

$city = $mysqli->real_escape_string ($city);//使用real_escape进行过滤

/* this query will fail, cause we didn’t escape $city */

if (!$mysqli->query(“INSERT into myCity(name) VALUES (‘$city’)”)) {

print “INSERT into myCity (name) VALUES (‘$city’) ”;

printf(“Error: %s ”, $mysqli->error);

}

var_dump($city);

var_dump($mysqli->client_encoding());

$mysqli->close();

3.解决方案

mysqli_set_charset函数对PHP和Mysql有版本要求,必须当mysql版本大于5,PHP版本大于5.0.5时,此函数才有 效。至于另一个mysql_set_charset函数,则更要求PHP版本大于5.2.3时才能有效。对于mysql4.1以上版本,使用”SET character_set_client=binary;”

推荐使用mysql_set_charset设置字符集的方案,只有在环境不允许的情况下,我们才推荐使用第二种binary编码的方案。但是无论在什么情况下,都禁止使用”SET NAMES”来作为设置字符集的操作。

转载于:https://www.cnblogs.com/xiaohong/archive/2012/03/13/2393327.html

更多相关:

  • 一.通过Keepalived搭建MySQL双主模式的高可用集群系统1.MySQL Replication介绍:MySQL Replication是MySQL自身提供的一个主从复制功能,其实也就是一台MySQL服务器(称为Slave)从另一台MySQL服务器(称为Master)上复制日志,然后解析日志并应用到自身的过程。MySQL Re...

  • 1、打开https://oneinstack.com/auto/选择Stack:LNMTY → ×安装Nginx → √安装JAVA:Tomcat7.0、JDK1.7 → √安装数据库MySQL5.7、DB密码root、DB安装方式二进制安装 → √Pure-FTPd √redis √memcached ×hhvm √iptables...

  • 注意:由于流程太过于繁杂,且坑多,这里只保留关键步骤,具体小问题百度! ________________________ 先进入CentOS6.8图形化界面(如阿里云没有默认的图形化界面,自己去安装https://blog.csdn.net/fenglixiong123/article/details/71138017) 【步骤...

  • 为了简单省事,我在CentOS 6.3上直接使用yum方法来安装MySQL,安装很顺利,但是我发现root用户登录不了。 下面是参考网上的资料并实操后的笔记整理 yum -y install mysql-server service mysqld start 安装过程中没有任何提示,也不知道root的密码设置的是多少。我打算从...

  • 破解MySQL密码 一、MySQL5.7.5之前 只要有系统root密码就可以破解: [root@host-131 ~]# vim /etc/my.cnf               //在配置文件中加入如下内容 [mysqld] skip-grant-tables[root@host-131 ~]# systemctl resta...

  • 1. set的初始化 set可以这样初始化 set iset{ 1,2,3 }; set iset2 = { 1,2,3 }; 在初始化set的时候,若出现重复的元素 set iset3{ 1,2,3,3,3 }; set iset4 = { 1,2,3,3,3 };...

  •     Set 对象存储的值总是唯一的 Set 对象方法 方法描述add添加某个值,返回Set对象本身。clear删除所有的键/值对,没有返回值。delete删除某个键,返回true。如果删除失败,返回false。forEach对每个元素执行指定操作。has返回一个布尔值,表示某个键是否在当前 Set 对象之中。 Set 对象...

  • 我现在的vimrc配置文件 runtime! debian.vim "设置编码 set encoding=utf-8 set fencs=utf-8,ucs-bom,shift-jis,gb18030,gbk,gb2312,cp936 set fileencodings=utf-8,ucs-bom,chinese"语言设置...

  • ACCEPT acm作业 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=204 因为老师是在集合那里要我们做这道题。所以我很是天真的就以为要用集合做,结果发现网上都是用数组简单明了地实现了,显得我的代码,特么地超级恶心!!!!!!! 在这里存档一下,别人就不要看了...

  • USE [svnhost]GO/****** 对象:  StoredProcedure [dbo].[up_Page2005]    脚本日期: 05/21/2008 11:27:05 ******/SET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOCREATE proc [dbo].[up_P...

  • nan 是not a number ,inf是无穷大 numpy.nan_to_num(x): 使用0代替数组x中的nan元素,使用有限的数字代替inf元素...

  • 简介 Simple Reference  基础CUDA示例,适用于初学者, 反映了运用CUDA和CUDA runtime APIs的一些基本概念.Utilities Reference  演示如何查询设备能力和衡量GPU/CPU 带宽的实例程序。Graphics Reference  图形化示例展现的是 CUDA, OpenGL,...

  • 在做开发的过程中难免需要给内核及下载的一些源码打补丁,所以我们先学习下Linux下使用如如何使用diff制作补丁以及如何使用patch打补丁。...

  • 我在调研ATS 4.2.3挂载SSD的过程中,遇到很多坑,特此详细记录我摸索的主要过程,以便大家以后避免之。 基本思路可以完全照搬参考文献[2][3] 下面的安装假定是以root用户身份进行的,Linux服务器已经安装好系统,磁盘已经做好分区。 首先需要认识我们的Linux服务器的硬件配置和软件情况 硬件配置: DELL...

  • 该博文整理一些在使用stl编程过程中遇到的小经验: 1.在多线程环境下面打印调试,如何使用cout及时刷新到屏幕上? 在C中我们经常这样使用: printf("Hello World "); fflush(stdout); 如果使用stl,我们可以这样使用: cout << "Hello World" << endl <...