 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 frillo 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 dibujas 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 era 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 al jefe, en el momento que se lo mostramos al 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, ¿sí? 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, hasta un montón, un montón de información. La verdad que es un estándar súper grande, pero estaba ahí publicado y ustedes pueden profundizar siempre ahí, digamos. Entonces, si quieren ir a ese estándar, digamos, ¿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, ¿sí? Son PDFs, se pueden bajar por separados, 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, los caracteres, un numerito que te identifica cada carácter, ¿sí? El de latin 1 será este, el de griego será este, el de rúnico será este y, así como eso, se 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, ¿sí? 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, ¿sí? 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, ¿sí? Dice que el pi, por ejemplo, es este. 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, ¿sí? También da información aledaña que puede ser útil. En el caso del pi, por ejemplo, el número es 030. 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 como 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, ¿qué sé yo? Sí. El nombre es este. El nombre no es... Está en inglés en mayúscula. Sí, sí, sí, sí. Claro. El pi se llama así. Bien. Entonces, ¿cómo vemos esto en Python? Bueno, nosotros siempre en Python podemos escribir los carácteres 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, ¿sí? 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 directamente en 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 tienen el teclado que tiene ese carácter, siempre 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 V larga antes, ahí están especificando que la cadena es de bytes, ¿sí? 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. Python 2 debe morir, Python 3 si lo usamos todos, ¿no? Entonces, esta presentación está en Python 3. Pero, bueno, nada, tengan este cuidado porque si alguien les dice, esto me falla y le está mostrando que está armando un ejemplo con una cadena sin especificar ni V ni U, una de las primeras cosas que hay que preguntarles es, ¿para vos qué estás usando? Python 2 o Python 3? No sé 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, podemos meterlo directamente si tenemos los caracteres para escribirlo 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 cosas. Bien, 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 puerto serie, cualquier cosa que necesite salir de la computadora o salir de la memoria, por así decirlo, aunque ya vamos a ver 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 unicodes. Yo en el disco el ú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 qué es lo que viaja por el socket? Por el socket viajan bytes o bits. No viajan otras cosas conceptualmente más complejas. Viajan 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, 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 encode o codificar, pasan a bytes. Y si ustedes tienen bytes con la operación decode o decodificar, 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 unicodes, lo que leen por el socket? Lo tienen que decodificar para obtener unicode. Es sencillo. Primero se preguntan qué tengo y saben qué se 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, ¿sí? 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 traducirlos a 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 si tienen un objeto, que ya no hay formas estándar y, por ejemplo, se puede picar un montón de cosas. Pero la complejidad de pasar mi objeto y codificarla y pasarla a bytes no es nueva, ¿sí? No 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í? ¿Por qué? 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 1 es una, las diferentes UTF son otras. Hay un montón de formas de codificar. Un carácter a bytes, ¿sí? Una vez 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 bytes, daba del 0 al 127. Los primeros 16 bytes 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 caracteres a números, ¿sí? Al número al número a la secuencia de bytes. 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. Yo arranco con una enye. Si yo codifico esa enye utilizando Latin 1 fíjense que para codificarle qué operación estoy llamando. El encode. Y fíjense también ese detalle. La cadena de la enye tiene alguna letrita antes? No, entonces estos Python 3 ¿qué es? Un encode. Cuando yo estoy agarrando la enye que es un carácter un encode que me deja eso. Bites. Fíjense que las cadenas de abajo tienen la b antes porque son bytes ¿tá 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 y el otro del byte ¿sí? Entonces si yo codifico la enye en Latin 1 me va a quedar un byte que vale f1 en exa ¿sí? Si yo codifico a la enye en utf8 me van a quedar dos bytes y si yo que vale el primero vale c3 y el otro vale b1 y si yo codifico la enye en utf16 me van a quedar en este caso 4 bytes entonces como cuando yo codifico un carácter unicode me quedan distintas secuencias de byte en función de la codificación que utilicé obviamente si o si para decodificar ese carácter unicode yo necesito utilizar la misma codificación el tema es volver a utilizar a decidir cuál uso Latin 1 por ejemplo es uno muy utilizado básicamente porque es como el default en windows pero tiene un problema uno es que solamente te maneja 256 carácteres no está mantenido desde hace un montón de tiempo porque ya que oído como fijo tiene 256 carácteres tampoco lo van a ir cambiando demasiado y como tiene 256 carácteres no entra todo el estandar unicode tiene 100 y pico de miles de carácteres obviamente 100 y 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 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 0,3,0 eso yo lo habíamos visto barra u es la forma de yo especificar un carácter unicode barra x es la forma de poner el número para un byte es muy utilizado latin 1 porque es uno de los primeros que nacieron porque básicamente los primeros 127 son ASCII 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 n y ese tipo de cosas obviamente si el pi no entra obviamente un carácter chino ni 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 hubieron hablar de el indio en castellano graciosamente se dice el indio pequeño el indio mayor big indian little indian UTF-16 maneja dos bytes es expandible a 4 pero maneja priori dos bytes con dos bytes ya pueden codificar 65536 carácteres si necesitan codificar más de 65530 si necesitan codificar un carácter que está en un número mayor a 65536 UTF-16 utiliza 4 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 entonces para solucionar eso cuando uno comunica computadoras entre computadoras 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 mi me dice si los bytes vienen así o así se llama BOM y el BOM que utiliza UTF-16 es ese FFF si ustedes agarran algo que está codificado en UTF-16 y ven que al principio arranca FFF pero si ven que arranca con FFF saben que tienen que ir dando vuelta cada uno de los dos pares que esto lo hace UTF-16 a mano, solo estos te los digo porque ustedes tendrían que laborarlo a mano pero no se tiene que meter en esa complejidad si ustedes llaman UTF-16 para que lo decodifique va a saber qué hacer está bueno para todos los carácteres que yo escriba me va a utilizar dos bytes si entonces es una macana porque la mayoría de los carácteres que nosotros usamos utilizan un byte si? me entra en el primer byte todas las letras ASCII COMUN las letras A, B, C, D, F, G es más la CENI nosotros lo raro que escribamos con un carácter que se escapa del 256 entonces como que es el doble despacio y después tenemos UTF-8 que es el más recomendado porque 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 si yo necesito un byte para codificarlo me va a utilizar un byte si necesito dos bytes para codificarlo me va a utilizar dos bytes si tengo un carácter bastante extraño para utilizar cuatro bytes es expandible pero también es eficiente si? 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á que lo vais a explicar en la presentación pero si ustedes se bajan 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 nada es una nerdiada 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 en la sección de arriba eso es bytes o unicode unicode porque qué tipo de cadena tengo ahí tengo algún prefijo no si no tengo un prefijo en Python 3 que es vamos hola hola unicode entonces si yo codifico SP qué me da bytes si yo codifico esa cadena de bytes utilizando la misma codificación qué vuelvo a tener ese es slide eso se lo tiene que saber de memoria 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 si? ese es un problema no siempre es tan fácil ustedes están trabajando con alguien tengo un problema de unicode te dice me explota acá qué qué estás teniendo ahí el tipo vayas un print el print te va a estar escondiendo si tenes bytes o si tenes unicode lo primero que tenes que hacer es un print wrapper de lo que vos tenes 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 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 lo que tiene son bytes 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 las reglas de oro un detalle con respecto a Python mismo Python mismo obviamente uno dice, bueno, tiene caracteres unicodes pero Python ¿dónde guardan esos caracteres unicodes? 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 carácter 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 unicodes? esto es un detalle que se los 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, si o si utiliza los 2 bytes o 4 bytes no era como usted F16 que utilizaba normalmente 2, pero si necesitaba 4 los usaba es muy importante que internamente Python 2 se utilizaba UCS porque es una cosa fisco ustedes piensen que se hacen LEN para saber la cantidad de caracteres unicodes y el tipo sabe que tiene una cadena unicode en UCS4 y a lo largo sabe que tiene 4 caracteres unicode porque divide por 4 las cuentas internas que hace Python pero al compilar Python 2 ustedes tenían 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 del estándar 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 te puede 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 internamente en el programa de ustedes siempre unicode si no manejan unicode internamente se pueden empezar a tener mocos por todos lados a tener pequeños errores que no saben de donde viene por ejemplo, yo ahí recibo algo que es la palabra máscara codificada en UTF-8 fíjense y yo salgo en largo de ese dato me dice que tiene 8 pero máscara tiene 7 letras ahí ya me empecé a confundir 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 de codificada me muestra algo que está mal 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 de codificar en los bordes lo más cerca posible de donde obtiene los dos lo primero que tienen que hacer es codificar y de codificar pueden utilizar en code y de code o si van a trabajar con archivos es muy útil cuando 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 si ustedes leen el archivo que están bytes en disc obviamente pero si acá ponen yo 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 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 yo sé que la base de datos está en latino 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 fíjense acá lo que estoy dando vuelta poniéndola como 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 se lo 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 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 piolas hay un módulo unicode data al cual le pueden preguntar el nombre que veíamos antes o incluso por el nombre del código no tengan miedo de utilizar todo lo que es procesamiento de texto como app, malware, etcétera en Python porque maneja unicodes sin problemas e incluso tengo algunas cosas bastante descomplicadas unicode disponibles en el módulo unicode data como es descomposició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 con otra gente de otra charla seguro cualquier duda que tengan unicode ese es mi twitter y yo a la próxima charla no voy así que charlamos en el pasillo lo que quieran buenísimo muchas gracias