• <转>乱码UTF8和UTF8网页编码


    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\n”, 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’)\n”;
    printf(“Error: %s\n”, $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”来作为设置字符集的操作。

  • 相关阅读:
    Linux关闭jetty服务器脚本
    TreeMap 源码解读
    LinkedHashMap 源码解读
    HashTable 源码解读
    MappedByteBuffer文件句柄释放问题
    HashMap源码解读
    Java 对象创建过程
    java 虚拟机内存介绍
    dubbo 部署
    kotlin 学习入门
  • 原文地址:https://www.cnblogs.com/xiaohong/p/2393327.html
Copyright © 2020-2023  润新知