J'essaie d'exporter certaines données d'une base de données MySQL, mais des choses étranges et merveilleuses se produisent pour unicode dans cette table.
Je vais me concentrer sur un personnage, la smartquote de gauche: "
Lorsque j'utilise SELECT
depuis la console, il est imprimé sans problème:
mysql> SELECT text FROM posts;
+-------+
| text |
+-------+
| “foo” |
+-------+
Cela signifie que les données sont envoyées à mon terminal en tant que utf-8 [0] (ce qui est correct).
Cependant, lorsque j'utilise SELECT * FROM posts INTO OUTFILE '/tmp/x.csv' …;
, Le fichier de sortie est pas correctement codé:
$ cat /tmp/x.csv
“fooâ€
Plus précisément, le “
Est codé avec sept (7!) Octets: \xc3\xa2\xe2\x82\xac\xc5\x93
.
De quel encodage s'agit-il? Ou comment pourrais-je dire à MySQL d'utiliser un encodage moins déraisonnable?
En outre, quelques faits divers:
SELECT @@character_set_database
Renvoie latin1
text
est une VARCHAR(42)
:
mysql> DESCRIBE posts;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| text | varchar(42) | NO | MUL | | |
+-------+-------------+------+-----+---------+-------+
“
Encodé en utf-8 donne \xe2\x80\x9c
\xe2\x80\x9c
Décodé en latin1
Puis recodé en utf-8
Donne \xc3\xa2\xc2\x80\xc2\x9c
(6 octets).…
(Utf-8: \xe2\x80\xa6
) Est codé en \xc3\xa2\xe2\x82\xac\xc2\xa6
[0]: comme les guillemets intelligents ne sont inclus dans aucun encodage 8 bits, et mon terminal affiche correctement les caractères utf-8.
De nombreux programmes/normes (y compris MySQL) supposent que "latin1" signifie "cp1252", donc l'octet 0x80 est interprété comme un symbole Euro, c'est là que ce \xe2\x82\xac
le bit (U + 20AC) vient du milieu.
Lorsque j'essaie, cela fonctionne correctement (mais notez comment je mets les données et les variables définies sur le serveur db):
mysql> set names utf8; -- http://dev.mysql.com/doc/refman/5.0/en/charset-connection.html
mysql> create table sq (c varchar(10)) character set utf8;
mysql> show create table sq\G
*************************** 1. row ***************************
Table: sq
Create Table: CREATE TABLE `sq` (
`c` varchar(10) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8
1 row in set (0.19 sec)
mysql> insert into sq values (unhex('E2809C'));
Query OK, 1 row affected (0.00 sec)
mysql> select hex(c), c from sq;
+--------+------+
| hex(c) | c |
+--------+------+
| E2809C | “ |
+--------+------+
1 row in set (0.00 sec)
mysql> select * from sq into outfile '/tmp/x.csv';
Query OK, 1 row affected (0.02 sec)
mysql> show variables like "%char%";
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.00 sec)
Et depuis le Shell:
/tmp$ hexdump -C x.csv
00000000 e2 80 9c 0a |....|
00000004
J'espère qu'il y a une friandise utile là-dedans…
Les versions plus récentes de MySQL ont une option pour définir le jeu de caractères dans la clause outfile:
SELECT col1,col2,col3
FROM table1
INTO OUTFILE '/tmp/out.txt'
CHARACTER SET utf8
FIELDS TERMINATED BY ','
J'ai trouvé que cela fonctionne bien.
SELECT convert(col_name USING latin1) FROM posts INTO OUTFILE '/tmp/x.csv' …;
Comme vous pouvez le voir, ma base de données MySQL utilise latin1
et le système est utf-8
.
mysql> SHOW VARIABLES LIKE 'character\_set\_%';
+--------------------------+--------+
| Variable_name | Value |
+--------------------------+--------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
+--------------------------+--------+
7 rows in set (0.00 sec)
Chaque fois que j'essayais d'exporter une table, j'obtenais un étrange fichier CSV encodé. Donc, je mets:
mysql_query("SET NAMES CP1252");
header('Content-Type: text/csv; charset=cp1252');
header('Content-Disposition: attachment;filename=output.csv');
comme dans mon script d'exportation .
Ensuite, j'ai une sortie UTF-8 pure.
Pour répondre spécifiquement à votre question "Qu'est-ce que c'est?", Vous y avez répondu vous-même:
Je soupçonne que cela est dû au fait que "les valeurs des colonnes sont vidées à l'aide du jeu de caractères binaires. En effet, il n'y a pas de conversion de jeu de caractères. "- dev.mysql.com/doc/refman/5.0/en/select-into.html
C'est ainsi que MySQL stocke utf8
données encodées en interne. C'est une variation terriblement inefficace du stockage Unicode, utilisant apparemment trois octets complets pour la plupart des caractères, et ne prenant pas en charge les séquences UTF-8 de quatre octets.
Quant à savoir comment le convertir en UTF-8 réel en utilisant INTO OUTFILE
... Je ne sais pas. L'utilisation d'autres méthodes mysqldump
le fera cependant.
Vous pouvez exécuter des requêtes MySQL à l'aide de l'outil CLI (je crois même avec un format de sortie pour qu'il imprime CSV) et rediriger vers un fichier. Devrait faire la conversion de jeu de caractères et vous donner toujours accès aux jointures, etc.
Essayez SET CHARACTER SET <blah>
avant votre sélection, <blah>=utf8
ou latin1
etc ... Voir: http://dev.mysql.com/doc/refman/5.6/en/charset-connection.html
Ou SET NAMES utf8;
pourrait fonctionner...
Vous devez émettre charset utf8
à l'invite MySQL avant d'exécuter le SELECT
. Cela indique au serveur sous quelle forme les résultats doivent être affichés.