• <转>乱码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”来作为设置字符集的操作。

  • 相关阅读:
    如何通过命令行窗口查看sqlite数据库文件
    eclipse自动补全的设置
    文本装饰
    注释和特殊符号
    文本装饰
    网页背景
    通过ArcGIS Server admin 查看和删除已注册的 Web Adaptor
    通过 ArcGIS Server Manager 查看已安装的 Web Adaptor
    通过 ArcGIS Server Manager 验证 DataStore
    Windows上安装ArcGIS Enterprise——以 Windows Server 2012 R2上安装 ArcGIS 10.8为例
  • 原文地址:https://www.cnblogs.com/xiaohong/p/2393327.html
Copyright © 2020-2023  润新知