 Bueno, bienvenidos, mi nombre es Facundo Batista, les voy a hablar un poquitito de unicode. Esta charla nace básicamente por una complejidad que yo vi, primero su frío y que después vi que pasan todos. Que es que la curva de aprendizaje de unicode es así. No la entendés, no la entendés, no la entendés, no la entendés. ¿La entendiste? Ya la entendiste, ya la entendiste, ya la entendiste. Es un escalón, si por la dibujarse es un escalón. Y pasa mucho que cuesta pasar ese escalón. Entonces cuando uno programa, ¿qué hacemos cuando programamos? Hacemos el programa, lo probamos, nos da error de unicode, decimos. ¿Qué carajo? Porque hubiera un error de unicode acá. Empezamos a cambiar cosas al azar hasta que de repente funciona. Entonces lo dejamos ahí bien lejitos, hasta que se lo mostramos a jefe. El momento que se lo mostramos a jefe se rompe obviamente porque no es que entendimos cuál era el problema y lo arreglamos. No. Cambiamos cosas al azar. ¿Por qué? Porque no entendimos cuál era el problema. Entonces la idea de esta charla es que se vayan con la capacidad de razonar el problema cuando sucede de que entiendan unicode, de que entiendan que es codificar, que entiendan que es decodificar, que entiendan por qué existe unicode y además les voy a mostrar unas reglas de oro que son como unas buenas prácticas para seguir cuando trabajan para manejar unicode. Cualquier pregunta me pueden interrumpir en cualquier momento y listo. Entonces ¿qué es unicode? Antes que nada unicode es un estándar. Es un montón de gente que se puso de acuerdo en algo y dijo bueno, unicode es esto y lo publican. Hay un unicode.org que si ustedes van están todo el estándar ahí especificado. Es un montón de información. Un conjunto de PDFs que les explican millones y millones de cosas. Desde qué caracteres existen, hasta cuál es la forma de ordenar esos caracteres, hasta cuál es la forma de denominarlos, un montón de información. La verdad que es un estándar supergrande, pero está ahí publicado y ustedes pueden profundizar siempre ahí, digamos. Entonces, si quieren ir a ese estándar, ¿cómo empezamos a procesar la información de ese estándar? Bueno, lo más fácil es los code charts. El punto de entrada al estándar que nosotros vamos a utilizar normalmente son los code charts. ¿Qué son los code charts? Básicamente son planillas, son PDFs, se pueden bajar por separado, se pueden bajar todo junto y que son grandes tablas con información sobre esos caracteres. Por ejemplo, el code chart de Basic Letting, es una tabla mucho más grande que eso, obviamente. X y griega, los caracteres, un numerito que te identifica cada carácter. El de Latin 1 será este, el de griego será este, el de rúnico será este y así como eso, hay un montón de code charts. Hay code charts para dibujos matemáticos, hay code charts para idiomas, que yo, Klingon, por ejemplo, hay code charts para los que se les ocurra. Todo está especificado en el estándar. Cada una de esas cajitas en el code chart es un carácter unicode. ¿Qué es un carácter unicode o qué está especificado en cada una de esas cajitas? Bueno, hay varias informaciones. Los dos más importantes para nosotros es el glifo, que el glifo es como el dibujito del carácter. Que el estándar no es que dice que el carácter se tiene que dibujar exactamente así, porque la forma exacta de dibujar cada carácter dependerá de la tipografía. Pero es un dibujito de referencia. Dice que el pi, por ejemplo, es éste. Y no es que todas las tipografías tienen que dibujar el pi de este igual. Pueden dibujar así con un palito horizontal y dos palitos verticales. Pero el glifo de referencia es ese. También especifica un nombre, también especifica un número y también da información aledaña que puede ser útil. En el caso del pi, por ejemplo, el número es 0,3,0. Es un número en exa. Es el número del carácter unicode. Es el número que identifico univocamente ese carácter en todo el estándar. Dice cómo se llama letra pi pequeña en minúscula griega. Y del pi, por ejemplo, nos dice que también es una constante matemática que vale 3,14, que sé yo. El nombre es éste. El nombre no es... Está en inglés en mayúscula. Sí, sí, sí, sí, sí. El pi se llama así. Bien. Entonces, ¿cómo vemos esto en Python? No. Nosotros siempre en Python podemos escribir los caracteres unicode con el número correspondiente en el estándar. Si arrancamos el string con barra u como en ese caso, ponemos barra u, ponemos el numerito. Y fíjense que la variable pi que yo escribí ahí, que tiene de largo 1, porque es un solo carácter, barra u y un solo número, vale pi. Si lo muestro, me lo muestra pi. Sí, print. Ahí lo veo con directamente el intérprete interactivo. Y obviamente yo también lo podría escribir así. Pero yo tengo un teclado latinoamericano. Mi teclado no tiene... Supongo que para un griego tendrá una teclita que dice pi, pondrá pi y ya está, digamos. Pero nosotros, si un teclado que no soporta, ustedes quieren poner un carácter y no tiene el teclado que tiene ese carácter, pueden escribir el numerito de esa manera y funciona. Bien, ya que arrancamos con Python, vamos a hacer una aclaración bastante importante. En Python 2 y en Python 3, las cadenas hay un detalle con respecto a unicode. Tanto en Python 2 como en Python 3, si a la cadena le ponen una B larga antes, ahí están especificando que la cadena es de bytes. No de unicode, sino bytes. Cada elemento va a ser un byte. Tanto en Python 2 como en Python 3, si antes de la cadena ponen una U, eso va a ser una cadena unicode. O sea que cada elemento de esa cadena va a ser un carácter unicode. Ahora, si ustedes no ponen nada, el default de esa cadena es lo que cambia entre Python 2 y Python 3. El default de esa cadena, cuando ustedes no ponen nada, son bytes en Python 2 y en Python 3 es unicode. Esta presentación la hice toda como si fuese Python 3, porque Python 2 debe morir, Python 3 se los usamos todos, ¿no? Entonces, esta presentación está en Python 3. Pero bueno, tengale este cuidado porque si alguien le dice, esto me falla y le está mostrando que está armando un ejemplo con una cadena sin especificar mi BNU, una de las primeras cosas que hay que preguntarle es... ¿Vos qué estás usando? Python 2 o Python 3? Porque tengo que entender si lo que vos estás escribiendo ahí son bytes o lo que estás escribiendo ahí es unicode. Eso es siempre lo primero que hay que entender, el dato de entrada. Ahora, hasta ahora qué sabemos? Hasta ahora sabemos que unicode es un estándar, nada, no, no, no. Técnicamente no hay nada raro, sabemos cómo meterlo en Python, exactamente si tenemos los caracteres para escribirlo o podemos... o podemos especificar el número, etcétera. ¿Dónde empieza la complejidad de Python? Perdón, ¿dónde empieza la complejidad de unicode más allá de Python? Cualquier lenguaje. Esto es muy lindo, pero se me apaga, le tengo que cambiar de cosa. Bien. Mal, estamos. El problema es que yo tengo esos caracteres unicode en mi cabeza, en el programa y los tengo que persistir o los tengo que mandar por la red. O sea, los tengo que grabar a disco, los tengo que mandar por un socket, los tengo que hacer viajar a mandar por un recuesta HTTP, los tengo que mandarlos por el porto serie, cualquier cosa que necesite salir de la computadora o salir de la memoria, por así decirlo, aunque ya vamos a saber que no es así, pero la idea es que lo tengo que bajar a bytes. ¿Por qué lo tengo que bajar a bytes? Porque yo en el disco no puedo escribir caracteres unicode. Yo en el disco lo único que puedo escribir son bytes. Yo tengo un objeto que es el caracter unicode y para poder grabar ese caracter unicode en el disco lo tengo que convertir a bytes. La misma manera que para mandarlo por un socket viajan bytes o bits. No viajan otras cosas conceptualmente más complejas. ¿Vejan bytes o bits? Entonces, cualquier cosa para mandarlo por un socket lo tengo que convertir a bytes o bits. ¿Cómo convierto los caracteres unicodes a bytes o bits? Bueno, la traducción esa se llama codificar cuando pasamos de un caracter unicode a bits, lo codificamos y lo decodificamos cuando pasamos bits o bytes al caracter unicode. Vamos de nuevo porque esto es la operación más importante que ustedes necesitan para entender unicode. Si ustedes se acuerdan de este dibujo y yo recomiendo que agarren la presentación, sacen esta cosita, esta slide, lo impriman y lo peguen al lado del escritorio, ¿sí? Porque si ustedes entienden esto solucionan en la mitad de los problemas de que tienen unicode. Si ustedes tienen un caracter unicode con la operación en code o codificar pasan a bytes. Y si ustedes tienen bytes con la operación de code o de codificar lo pasan a caracteres unicode. Entonces, si ustedes, por ejemplo, están leyendo un socket, ¿qué viene por el socket? Bites. ¿Qué van a hacer con esos? ¿Eso es unicode lo que leen por el socket? Lo tienen que decodificar para obtener unicode. Es sencillo. Primero se preguntan qué tengo y después saben qué pueden hacer. Bien. Ahora, ¿cuál es la complejidad de eso? ¿No es directo? ¿Por qué es difícil? Bueno, el problema es que no hay una sola forma de codificar los caracteres unicode para pasarlos a bytes. Tengo un montón de formas de codificar un caracter unicode que me va a dar un montón de formas de bytes distintas. No es una complejidad nueva. O sea, lo único que se codifica, el único objeto conceptual que se codifica a bytes de una única manera son los números enteros positivos. Cualquier cosa que no sea un número entero positivo tiene una forma de traducirlo a bytes. Acá yo pongo dos ejemplos, que es, por ejemplo, los números negativos. Hay dos formas de traducir los bytes. Complemento a uno, complemento a dos. Oh, incluso manteniendo los números positivos. Si es con coma, si es con punto flotante, puede ser, por ejemplo, precisión simple, precisión doble, precisión quadruple. Y estamos hablando de números recién. Cualquier objeto conceptual, ni hablar si hacen una clase a usted que para ya no hay formas estándar y, por ejemplo, se puede piclear un montón de cosas. Pero la complejidad de pasar mi objeto y codificarla y pasar la bytes no es nueva, ¿sí? Nosotros nos chocamos con esto en un icode, pero es algo normal. Entonces, ¿cómo representamos a bytes o cómo llevamos a bytes un objeto un icode? Bueno, básicamente tenemos que escribir reglas para traducir los distintos caracteres un icode a distintas secuencias de bytes. Tampoco es que las tenemos que pensar nosotros, ¿sí? Porque ya tenemos en el estándar de un icode un montón de reglas que pensaron, un montón de gente, y que podemos utilizar. Es más, no tiene sentido pensar unas nosotras. Vamos a utilizar siempre algunas del estándar. ASCII es una, Latin Uno es una, las diferentes UTF son otras. Hay un montón de formas de codificar un carácter a bytes, ¿sí? Una vez es, usa ASCII como sinónimo de no carácter un icode, pero realmente ASCII es una tabla también que dice que la A es el 64, ¿sí? Pero eso es una tabla. El ASCII original era de 7 bits, daba del 0 al 127. Los primeros 16 bits eran de carácter de control, van a 15, sin control del 0, o 14, sin control del 0. Pero también es una tabla. También es una tabla que me traduce carácteres a números, al número a la secuencia de bits. Ahora, necesitamos una regla para traducir, necesitamos una regla para codificar. No la tenemos que pensar nosotros, hay un montón. ¿Cuál usamos? No importa cuál usemos, lo más importante es que tenemos que utilizar la misma para codificar y la misma para decodificar. ¿Por qué? Porque cuando nosotros utilizamos distintas codificaciones las secuencias de bytes que nos quedan son distintas. Por ejemplo, veamos ahí en el ejemplo. Yo arranco con una eñe. Si yo codifico esa eñe utilizando Latin 1, fíjense que para codificarle que operación estoy llamando el encode. Y fíjense también ese detalle. La cadena de la eñe tiene alguna letrita antes? No. Entonces, esto es Python 3 que es un encode. Cuando yo estoy agarrando la eñe, que es un carácter un encode y le estoy llamando al punto encode, ¿qué me deja eso? Bites. Fíjense que las cadenas de abajo tienen la B antes, porque son bytes. ¿Está bien? Entonces, fíjense, no sé si están acostumbrados a esta forma de expresar los bytes, pero si no, el barra x es una forma que tiene Python para especificar un byte. Después del barra x, del barra invertida x, hay dos caracteres en exa que me dicen el número del byte. Entonces, si yo codifico la eñe en Latin 1, me va a quedar un byte que vale f1 en exa. Si yo codifico a la eñe en UTF8, me van a quedar dos bytes. Y si yo... que vale el primero... el primero vale c3 y el otro vale b1. Y si yo codifico la eñe en UTF16, me van a quedar en este caso cuatro bytes. Entonces, como... cuando yo codifico un carácter un encode, me quedan distintas secuencias de bytes en función de la codificación que utilicé, obviamente, sí o sí para decodificar ese carácter un encode, yo necesito utilizar la misma codificación. El tema es volver a utilizar... volver a decidir cuál uso. Latin 1, por ejemplo, es uno muy utilizado. Es muy conocido, básicamente, porque es como el default en Windows, pero tiene un problema. Uno es que solamente te maneja 256 caracteres. No está mantenido desde hace un montón de tiempo porque ya ha cogido como fijo, 256 caracteres. Tampoco lo van a ir cambiando demasiado. Y como tiene 256 caracteres, no entra todo el estándar único del encode. El estándar único del encode tiene 100 pico de miles de caracteres. Entonces, obviamente, 100 pico de miles mayor que 256. Por ejemplo, si yo quiero agarrar p y codificarlo en Latin 1, voy a tener un error. Fíjense cuál es el error. Latin 1, el codec, Latin 1, codec, codificación, codec, codificación. La codificación en Latin 1 no puede codificar el carácter. Fíjense que acá me muestra el detalle. El carácter del número es barraus030, eso yo lo habíamos visto. Barraus, la forma de yo especificar es un carácter unicode. Barrax es la forma de poner el número para un byte. Es muy utilizado en Latin 1 porque es uno de los primeros 127 zonaski, como la mayoría de las codificaciones. Y después los segundo 127 utilizaban todos los carácteres más comunes en el resto de las culturas occidentales. Los acentos nuestros, la E, ese tipo de cosas. Obviamente, si el P no entra, obviamente un carácter chinón y pensarlo. UTF-16 es otro bastante utilizado también. UTF-16 en el UTF-16 se nos presenta el primer problema que se los menciono acá, no vamos a meternos mucho en detalle con eso, pero es para que lo tengan en cuenta. No sé si hubieran escuchado hablar de el Indio, lo que sé en castellano graciosamente. Se dice el Indio pequeño, el Indio mayor. Big Indian, Little Indian. Ustedes, el UTF-16 maneja dos bytes. Es expandible a cuatro, pero maneja priori dos bytes. Con dos bytes ya pueden codificar 65536 carácteres. Si necesitan codificar más de 655... Perdón, si necesitan codificar un carácter que está en un número mayor a 65536, UTF-16 utiliza cuatro bytes. El problema es que tienen dos bytes y no todas las computadoras ponen el byte de mayor orden a la izquierda y el byte de menor orden a la derecha. Algunas arquitecturas de computadora lo ponen de una manera otras de otra. Para solucionar eso, cuando uno comunica computadoras entre computadoras lo que hace es poner dos carácteres fijos al principio. Entonces la persona que recibe ese stream de bytes sabe si el par viene así o así básicamente. Ese par de carácteres que a mí me dice si los bytes vienen así o así se llama BOM. Y el BOM que utiliza UTF-16 es ese FFFE. Si ustedes agarran algo que está codificado en UTF-16 y ven que al principio arranca con FFFE, ya está. Pero si ven que arranca con FFFE saben que tienen que ir dando vuelta a cada uno de los dos pares. Que esto lo hace UTF-16 a mano, solo. Esto te los digo porque si ustedes tendrían que laborarlo a mano. Pero no se tienen que meter en esa complejidad, si ustedes llaman UTF-16 para que lo decodifiquen, va a saber que hacer. Está bueno me sirve, pero ¿cuál es el problema? Para todos los carácteres que yo escriba me va a utilizar dos bytes. Entonces es una macana. ¿Por qué? Porque la mayoría de los carácteres que nosotros usamos utilizan un byte. Mientras en el primer byte, todas las letras ASCII común, las letras A, B, C, D, F, G, es más, la C, ni en los acento, nosotros es raro que escribamos con un carácter que se escapa del 256. Entonces, como que esto me ocupa el doble despacio. Y después tenemos UTF-8 que es el más recomendado. ¿Por qué UTF-8 es el más recomendado? Primero porque es el default en Linux y en plataformas similares. Segundo porque es bastante eficiente en el sentido que yo necesito un byte para codificarlo. Me voy a utilizar un byte. Si necesito dos bytes para codificarlo me voy a utilizar dos bytes. Si tengo un carácter bastante extraño, me voy a utilizar tres bytes. Si tienen un carácter aún más extraño me voy a utilizar cuatro bytes. Es expandible, pero también es eficiente. ¿Sí? UTF-8 es la referencia hoy por hoy. Y es raro que se utilice otra cosa que no es UTF-8 de nuevo en las culturas occidentales. Los chinos, por ejemplo, es normal que utilicen UTF-16 porque en general siempre van a estar en los carácteres que necesitan dos bytes. Este slide lo dejo acá, siempre, no porque lo vais a explicar en la presentación pero si usted se baja en la charla después es interesante ver cómo UTF-8 va armando las cadenas en función de cuánto, de qué carácter necesito ubicar. Si usted está perdiada, si les gusta jugar con los bititos este slide está bueno. Entonces, volviendo a la problemática que tenía antes. Yo tengo un carácter, ese carácter de arriba de todos. Yo tengo tres secciones ahí, la sección de arriba. ¿Qué carácter tengo? ¿Eso es byte o unicode? ¿Unicode? ¿Por qué? ¿Qué tipo de cadena tengo ahí? ¿Tengo algún prefijo? No. ¿Qué tipo de cadena tengo en Python 3? ¿Qué es? Vamos, hola, hola. Unicode. Entonces, si yo codifico SP ¿Qué me da? bytes Si yo decodifico esa cadena de bytes utilizando la misma codificación ¿Qué vuelvo a tener? Unicode. ¿Sí? Es ese slide. Es el razonamiento que siempre tienen que hacer. Ustedes primero tienen que preguntar qué es lo que tienen. ¿Qué estoy viendo acá? ¿Son bytes o es unicode? ¿Sí? Eso es un problema. No siempre es tan fácil. Ustedes están trabajando con alguien. Tengo un problema de unicode. Te dicen, ¿me explota acá? ¿Qué estás teniendo ahí? El tipo byte es un print. El print te va a estar escondiendo si tenés byte o si tenés unicode. Lo primero que tenés que hacer es un print wrapper de lo que vos tenés. Si vos haces un print wrapper ahí te va a estar especificando que es una cadena y si es byte te va a poner una b al principio en Python 3. Si es Python 2 te va a mostrar byte por default y te va a poner una u si estás teniendo unicode. Siempre hay que tener cuidado cuando uno mira lo que tiene porque puede prestar la confusión. Una vez que uno está seguro que tiene son byte o es unicode ya tiene la mitad de la pelea ganada. Después hay que saber si uno está codificando o decodificando. Si hay que codificarla o decodificarla. Bien. Antes de pasar a las reglas de oro un detalle con respecto a Python mismo. Python mismo obviamente uno dice, bueno, tiene caracteres unicode pero Python ¿dónde guardas esos caracteres unicode? En memoria. Y de la misma manera que los discos regidos guardan bytes y que los sockets por los sockets pasan bytes las memorias que guardan bytes. Entonces Python internamente tampoco puede poner un caracter unicode ahí arriba de la misma manera que no puede poner un código objeto. Tiene formas de codificar eso. ¿Cómo codifica Python internamente los caracteres unicode? Esto es un detalle que se lo menciono solamente porque por ahí se pueden cruzar. En Python 2 la codificación que se utiliza se decide en el momento de compilar Python para esa plataforma. A veces utiliza UCS2 o UCS4. ¿Qué es UCS que no lo había mencionado antes? UCS2 y 4 es una codificación que siempre sí o sí utiliza los 2 bytes o 4 bytes. No era como usted F16 que utilizaba normalmente y necesitaba 4 en su sábado. Es muy importante que internamente Python 2 utilizaba UCS porque es una cosa fija. Ustedes piensen que se hacen LEN para saber la cantidad de caracteres unicode y el tipo sabe que tiene una cadena unicode en UCS4 de 16 bytes de largo sabe que tiene 4 caracteres unicode porque divide por 4. Esas son las cuentas internas que hace Python. Pero al compilar Python 2 si usted tiene que tomar esa decisión lo compilan con UCS2 y tienen solamente 65.536 caracteres o lo compilan en UCS4 donde pueden tener todos los caracteres de la estandar unicode pero al mismo tiempo cada caracter unicode le va a ocupar 4 bytes en memoria. Es algo que tenían que decidir en el momento de compilar. En Python 3 hay una representación más flexible que va a ocupar 1, 2 o 4 bytes y funciona. Hace todo internamente me parece que me estoy quedando sin tiempo pero como no tengo nadie que me marque. Las reglas de oro internamente en el programa de ustedes siempre tienen que manejar unicode. Si no manejan unicode internamente se pueden empezar a tener mocos por todos lados a tener pequeños errores que no saben de dónde bien. Por ejemplo, yo ahí recibo algo que es la palabra máscara codificada en UTF8. Fíjense y yo salgo en largo de ese dato y me dice que tiene 8 pero máscara tiene 7 letras. Ahí ya me empecé a confundir por qué máscara me dice que tiene 8 si tiene 7 letras. O le pido que me dé la mayúscula y me da esta porquería que si yo después la muestro lo voy a hacer. ¿Por qué? Porque yo estoy procesando lo que tengo pensando que son letras le hago a per, quiero contarlas pero realmente tengo bytes entonces siempre tienen que codificar y decodificar en los bordes lo más cerca posible donde obtiene y donde dejan los datos. Lo primero que tienen que hacer es codificar y decodificar. Pueden utilizar en code y decode o si van a trabajar con archivos especifican un open y RT por ejemplo para leer el archivo o WT para escribir el archivo acá mismo le pueden pasar la codificación entonces ustedes leen el archivo que están bytes en disc obviamente pero si acá ponen en code el archivo está en UTF-8 acá ponen en code UTF-8 cuando ustedes leen ya tienen unicode ya hicieron la decodificación la codificación en esa capa. Vamos con un ejemplo donde yo muestro donde siempre tienen que trabajar en los bordes Fíjense en ese caso yo voy a una base de datos, me conecto de alguna manera y traigo un nombre y un apellido de la base de datos que con mi conector es el loco que yo puse ahí son bytes, si? Fíjense que ustedes ahí tienen bytes empieza con B lo primero que tienen que hacer es decodificarlos antes de empezar a trabajar de la base de datos así que la base de datos está en Latin 1 la decodifico y tengo el nombre completo que es ese nombre que es unicode una vez que lo tengo unicode hago todo el procesamiento que tengo que hacer sin tener miedo a romper esa cadena porque ya sé que es unicode y puedo procesarla normalmente si? Fíjense acá lo que estoy dando vuelta poniendo la coma estoy armando el nombre y el apellido de forma distinta antes de sacar mis datos lo que tengo que hacer es codificarlo en este caso lo estoy codificando en UTF-8 porque yo alguien más le voy a dar en UTF-8 y vuelvo a tener bytes y después ahí sí puedo dejar el resultado donde lo tengo que dejar mandándolo como bytes pero lo importante lo que quiero resaltar es que decodifiquen en los bordes y codifiquen en los bordes e internamente siempre hagan todo el procesamiento en unicode bueno y para terminar esto nos vamos a ver porque nos quedamos sin tiempo un par de cositas violas hay un módulo unicode data al cual le pueden preguntar el nombre que veíamos antes o incluso por el nombre nos puede dar el código no tengan miedo de utilizar todo lo que es procesamiento de texto como upper, lower etcétera en Python porque maneja unicode sin problemas e incluso tengo algunas cosas bastante complicadas unicode disponibles en el módulo unicode data como es composición de caracteres composición de caracteres etcétera nos quedamos sin tiempo tienen otra charla arrancando en otro lado en este momento si quieren pero están entrando otra gente de otra charla seguro cualquier duda que tengan unicode es en mi twitter la próxima charla no voy así que la charlamos en el pasillo lo que quieran buenísimo muchas gracias