{"id":15,"date":"2006-12-10T23:17:09","date_gmt":"2006-12-10T21:17:09","guid":{"rendered":"http:\/\/blog.rastersoft.com\/index.php\/2006\/12\/10\/python-mysql-utf-8-y-la-madre-que-los-pario\/"},"modified":"2015-08-15T18:15:44","modified_gmt":"2015-08-15T18:15:44","slug":"python-mysql-utf-8-y-la-madre-que-los-pario","status":"publish","type":"post","link":"https:\/\/blog.rastersoft.com\/?p=15","title":{"rendered":"Python, MySQL, UTF-8 y la madre que los pari\u00f3"},"content":{"rendered":"<p>Errar es humano, pero para liar las cosas de verdad se necesita un ordenador&#8230; que trabaje con UTF-8.<\/p>\n<p>Para los que no lo conozcan, <a href=\"http:\/\/en.wikipedia.org\/wiki\/UTF-8\" target=\"_blank\">UTF-8<\/a> es una codificaci\u00f3n multibyte para la tabla de caracteres UNICODE. Un caracter UNICODE est\u00e1 representado por un n\u00famero entre 0 y 1,114,111, y UTF-8 es un sistema para representar dicho n\u00famero mediante una secuencia de bytes. Su caracter\u00edstica m\u00e1s atractiva es que los primeros 128 caracteres se corresponden con los de la tabla ASCII y, adem\u00e1s, ocupan un solo byte, lo que significa que un texto en ASCII est\u00e1ndar (de 7 bits) es, a la vez, un texto UTF-8; sin embargo, tan pronto comenzamos a usar otros caracteres m\u00e1s raros (como nuestra querida letra \u00d1, o nuestras vocales acentuadas), ocuparemos dos, tres o hasta cuatro bytes por caracter.<\/p>\n<p>En efecto, hagamos una peque\u00f1a prueba: abramos nuestro interprete de Python y escribamos:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">Python 2.4.4c1 (#2, Oct 11 2006, 21:51:02)\r\n[GCC 4.1.2 (Ubuntu 4.1.1-13ubuntu5)] on linux2\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n&gt;&gt;&gt; cadena = u\"Pap\u00e1\"\r\n&gt;&gt;&gt; print len(cadena)\r\n4<\/pre>\n<\/div>\n<p>Hasta aqu\u00ed nada raro: hemos definido una cadena de tipo UTF-8 (la letra <em>u<\/em> antes de las comillas sirve para eso) y le hemos pedido al interprete que nos diga su longitud, que, efectivamente, es cuatro. Pero si cambiamos un poco el experimento llegan las cosas \u00abraras\u00bb:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">&gt;&gt;&gt; cadena2 = \"Pap\u00e1\"\r\n&gt;&gt;&gt; print len(cadena2)\r\n5<\/pre>\n<\/div>\n<p>Ahora la longitud es cinco, lo que no supone ning\u00fan misterio si nos damos cuenta de que la cadena la estamos definiendo como cadena normal, no cadena UTF-8, y por tanto nos devuelve el n\u00famero de bytes que ocupa; y como la letra <em>\u00e1<\/em> (con tilde) necesita dos bytes (pues mi sistema utiliza UTF-8), el misterio queda resuelto.<\/p>\n<p>Por desgracia las cosas se complican cuando uno tiene que usar Python para transmitir y recibir cadenas en UTF-8, byte a byte, por la red; cadenas de las que tomar\u00e1 trozos y compondr\u00e1 con ellos peticiones para MySQL. Entonces es cuando llega la pesadilla&#8230; \u00bfY por qu\u00e9? Pues porque por defecto MySQL espera y entrega sus datos en Latin-1, y tambi\u00e9n los almacena en dicho formato; pero si yo meto una cadena UTF-8 sin decirle que es UTF-8, el la considera una secuencia el Latin-1, con lo que los datos que me entrega llegan bien, y los que yo le paso tambi\u00e9n&#8230; si no fuese porque el m\u00f3dulo de Python para trabajar con MySQL sabe que \u00e9ste espera los datos en Latin-1 en intenta convertirlos, soltando a veces una excepci\u00f3n, o metiendo basura en la base de datos el resto.<\/p>\n<p>Por todo ello debemos ser muy cuidadosos a la hora de trabajar con MySQL y Python en UTF-8, y aqu\u00ed van las pistas que he ido recolectando despu\u00e9s de varios d\u00edas de sufrimientos varios:<\/p>\n<p><strong>Convertir las tablas a UTF-8<\/strong> Es el primer paso: si creaste tus tablas en formato Latin-1, lo mejor es pasarlas a UTF-8 para evitarse problemas. Para ello usaremos<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">mysqldump --opt --password=miclave --user=miuser mibasededatos &gt; archivo.sql<\/pre>\n<\/div>\n<p>A continuaci\u00f3n editaremos el c\u00f3digo y cambiaremos la palabra <em>latin1<\/em> por <em>utf8<\/em>, con lo que al recuperar los datos, las tablas se crear\u00e1n en el nuevo formato. Ahora s\u00f3lo queda volver a grabarlas con<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">mysql -u miuser -p miclave mibasededatos &lt; archivo.sql<\/pre>\n<\/div>\n<p>y listo, ya tenemos nuestras tablas en UTF-8.<\/p>\n<p><strong>Acceder a MySQL mediante UTF-8<\/strong> Esta parte es sencilla tambi\u00e9n, pero la documentaci\u00f3n es escasa y s\u00f3lo la encontr\u00e9 despu\u00e9s de bucear por muchas p\u00e1ginas de Internet y juntar resultados. Tenemos que usar la l\u00ednea en python:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">mibase=MySQLdb.connect(host=\"mihost\", user=\"usuario\", passwd=\"clave\", db=\"basedatos\", <strong>charset=\"utf8\", init_command=\"set names utf8\"<\/strong>)<\/pre>\n<\/div>\n<p>(atenci\u00f3n a la negrita). Justo a continuaci\u00f3n tenemos que a\u00f1adir:<\/p>\n<div class=\"mycode\">\n<pre class=\"mycode\">mibase.names=\"utf8\"<\/pre>\n<\/div>\n<p>y con \u00e9sto ya tendremos garantizado el acceso a la base de datos mediante UTF-8.<\/p>\n<p>En principio con esto ya tendr\u00edamos todo listo para poder trabajar c\u00f3modamente con MySQL, Python y UTF-8, pero a\u00fan falta algo, y es que la cadena que contenga las peticiones no basta con que contenga secuencias UTF-8, sino que tiene que ser un string de tipo UTF-8. Sin embargo, puede ocurrir que tengamos las secuencias UTF-8 almacenadas en cadenas normales. En estos casos, el m\u00e9todo <strong>encode(\u00abutf-8\u00bb)<\/strong> nos sacar\u00e1 del apuro. Tambi\u00e9n nos ser\u00e1 util si necesitamos mezclar cadenas normales con los resultados que nos devuelva, en UTF-8, la base de datos.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Errar es humano, pero para liar las cosas de verdad se necesita un ordenador&#8230; que trabaje con UTF-8. Para los que no lo conozcan, UTF-8 es una codificaci\u00f3n multibyte para la tabla de caracteres UNICODE. Un caracter UNICODE est\u00e1 representado por un n\u00famero entre 0 y 1,114,111, y UTF-8 es un sistema para representar dicho &hellip; <a href=\"https:\/\/blog.rastersoft.com\/?p=15\" class=\"more-link\">Seguir leyendo <span class=\"screen-reader-text\">Python, MySQL, UTF-8 y la madre que los pari\u00f3<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,7],"tags":[],"class_list":["post-15","post","type-post","status-publish","format-standard","hentry","category-programacion","category-tutoriales"],"_links":{"self":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/15","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=15"}],"version-history":[{"count":4,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/15\/revisions"}],"predecessor-version":[{"id":1910,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=\/wp\/v2\/posts\/15\/revisions\/1910"}],"wp:attachment":[{"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=15"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=15"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.rastersoft.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=15"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}