MySQL:SELECT INTO使用的字符编码?

MySQL:SELECT INTO使用的字符编码?,mysql,utf-8,character-encoding,Mysql,Utf 8,Character Encoding,我正试图从MySQL数据库中导出一些数据,但该表中的unicode发生了奇怪而奇妙的事情 我将关注一个字符,左边的smartquote:“ 当我使用控制台中的选择时,它将无问题打印: mysql> SELECT text FROM posts; +-------+ | text | +-------+ | “foo” | +-------+ 这意味着数据将以utf-8[0]的形式发送到我的终端(这是正确的) 但是,当我使用SELECT*FROM posts to OUTFILE'/tm

我正试图从MySQL数据库中导出一些数据,但该表中的unicode发生了奇怪而奇妙的事情

我将关注一个字符,左边的smartquote:“

当我使用控制台中的
选择
时,它将无问题打印:

mysql> SELECT text FROM posts;
+-------+
| text  |
+-------+
| “foo” |
+-------+
这意味着数据将以utf-8[0]的形式发送到我的终端(这是正确的)

但是,当我使用
SELECT*FROM posts to OUTFILE'/tmp/x.csv'…;
时,输出文件的编码不正确:

$ cat /tmp/x.csv
“fooâ€
具体来说,
用七(7!)个字节编码:
\xc3\xa2\xe2\x82\xac\xc5\x93

这是什么编码?或者我如何告诉MySQL使用不太合理的编码

此外,还有一些杂项事实:

  • SELECT@@character\u set\u数据库
    返回
    latin1
  • text
    列是一个
    VARCHAR(42)
    
    mysql>描述帖子;
    +-------+-------------+------+-----+---------+-------+
    |字段|类型|空|键|默认|额外|
    +-------+-------------+------+-----+---------+-------+
    |文本| varchar(42)| NO | MUL ||
    +-------+-------------+------+-----+---------+-------+
    
  • 编码为utf-8产生
    \xe2\x80\x9c
  • \xe2\x80\x9c
    解码为
    latin1
    ,然后重新编码为
    utf-8
    产生
    \xc3\xa2\xc2\x80\xc2\x9c
    (6字节)
  • 另一个数据点:
    (utf-8:
    \xe2\x80\xa6
    )编码为
    \xc3\xa2\xe2\x82\xac\xc2\xa6

[0]:由于智能引号不包含在任何8位编码中,并且我的终端正确呈现utf-8字符。

在运行
SELECT
之前,您需要在MySQL提示符下发出
charset utf8
。这会告诉服务器输出结果的内容。

在选择之前尝试
设置字符集
=utf8de>或
拉丁语1
等。。。 见:


或者
设置名称utf8;
可能有用…

为了具体解决您的问题“这是什么?”,您自己已经回答了:

我怀疑这是因为“列值使用二进制字符集转储。实际上,没有字符集转换。”-dev.mysql.com/doc/refman/5.0/en/select-into.html

这就是MySQL内部存储
utf8
编码数据的方式。这是一种效率极低的Unicode存储变体,显然对大多数字符使用了整整三个字节,并且不支持四字节UTF-8序列


至于如何使用
转换为OUTFILE
将其转换为真正的UTF-8,我不知道。不过,使用其他
mysqldump
方法可以做到这一点。

您可以使用CLI工具执行MySQL查询(我相信即使使用输出格式,它也会打印出CSV)应该进行字符集转换,并且仍然允许访问连接,等等。

< p>许多程序/标准(包括MySQL)假设“LATIN1”是“CP1252”,因此0x80字节被解释为一个欧元符号,这就是<代码> \xE2\x82\xac >位(u+20ac)从中间来的地方。

当我尝试此操作时,它工作正常(但请注意我是如何将数据放入的,以及db服务器上设置的变量):

从外壳上看:

/tmp$ hexdump -C x.csv
00000000  e2 80 9c 0a                                       |....|
00000004

希望这里有一个有用的小贴士…

正如您所看到的,我的MySQL数据库使用的是
latin1
,系统是
utf-8

mysql>显示诸如“character\\u set\\u%”之类的变量;
+--------------------------+--------+
|变量名称|值|
+--------------------------+--------+
|字符集客户机拉丁1|
|字符集连接拉丁1|
|字符集数据库拉丁1|
|字符集文件系统二进制|
|字符集结果拉丁1|
|字符集服务器拉丁1|
|字符集系统utf8|
+--------------------------+--------+
一组7行(0.00秒)
每次我尝试导出表时,都会得到奇怪的编码CSV文件。 因此,我提出:

mysql_query("SET NAMES CP1252");
header('Content-Type: text/csv; charset=cp1252');
header('Content-Disposition: attachment;filename=output.csv');
就像在我的生活中一样


然后我得到了纯UTF-8输出。

我发现这很有效

SELECT convert(col_name USING latin1) FROM posts INTO OUTFILE '/tmp/x.csv' …;

MySQL的较新版本有一个选项,可以在outfile子句中设置字符集:

SELECT col1,col2,col3 
FROM table1 
INTO OUTFILE '/tmp/out.txt' 
CHARACTER SET utf8
FIELDS TERMINATED BY ','

为什么要使用这个,而不是mysqldump?!我使用了
选择进入
,因为我想在导出之前过滤并加入数据。不过,我可能不需要这样做……因为有些数据比完全破坏的数据要好。你可以克隆数据库,并对克隆进行必要的更新,以获得所需的导出。这是可行的…但在这一点上,我想我只是要写一个小Python脚本来为我做转储。我想知道为什么我们通常围绕这个问题,而不是解决它。这不会改变结果。您得到的结果与没有它的结果相同?或者您得到的结果不同,但仍然不正确。并且设置
charset-latin1
doesn也不会更改结果。我得到了相同的结果。我怀疑这是因为“列值使用二进制字符集转储。实际上,没有字符集转换。”-啊…嗯…那么,出于好奇,MySQL是如何在内部编码unicode数据的?我希望我知道。我在写这个答案时翻阅了文档,但找不到任何具体内容。它不是UCS-2,不是UTF-8,也不是UTF-16。我只是对MySQL的“UTF-8”有着挥之不去的被动认识存储不是UTF-8,也不是很优化。可能值得提出一个新问题。因此,文档似乎在撒谎(或者至少是误导)。@taavi似乎找到了答案-MySQL的“latin1”实际上是cp1252,所以MySQL将文本解码为cp1252,然后将其编码为UTF-8。太棒了!答对了。就是这样:
“\xe2\x80\x9c”.解码(“cp1252”).编码(“utf-8”)
产生
“\xc3\xa2\xe2\x82\xac\xc5\x93”
。谢谢!如果我在这里错了,请纠正我,但这意味着
SELECT convert(col_name USING latin1) FROM posts INTO OUTFILE '/tmp/x.csv' …;
SELECT col1,col2,col3 
FROM table1 
INTO OUTFILE '/tmp/out.txt' 
CHARACTER SET utf8
FIELDS TERMINATED BY ','