Confusiones épicas: cadenas de texto

La informática es maravillosa. Permite coger un proceso tedioso y automatizarlo, dejando las tareas creativas a las personas.
Pero tiene un lado oscuro: cuando esas mismas personas pretenden reinventar la realidad o entienden mal un problema. Vamos, lo que se conoce como "pajas mentales".

Advierto que este "artículo" no es para todos los públicos y puede ser soporífero para quienes no han programado.

Cadenas de texto
La informática lleva dando tumbos desde sus inicios a cómo representar lo que en cualquier idioma es un sencillo texto.
Comprendo que inicialmente los caracteres se representasen en 7 bits (ASCII) y que luego lo ampliasen a 8 bits, que luego se quedase corto e inventasen UFT-8 y que luego se internacionalizase con UNICODE.
Lo que no puedo entender es cómo puede llegarse a complicar tanto la representación de un texto. Me refiero a un trozo de texto, una frase, no a un texto completo.

La historia de terror comienza cuando alguien decide que para representar un texto le basta con simplemente una sucesión de caracteres:
Mi bonito gato.

Tres palabras y un punto. Conceptualmente sencillo. Pongamos dos textos seguidos:
Mi bonito gato. Mi fiel perro.
Y ya tenemos un problemilla: ¿dónde acaba un texto y comienza el otro? Cualquier persona sabría decirlo, pero informáticamente debe ser algo que no se pueda confundir. Y no, no podemos utilizar el "." como indicador del final de un texto porque un texto podría contener un punto, por ejemplo para escribir "1.973".

Así que en el lenguaje C se decidió indicar el final de un texto no con un punto, sino con el caracter 0x00, que no forma parte del texto y que no aparece en los textos normales.
Mi bonito gato.0x00 Mi fiel perro.0x00
Esto ha sido la causa de numerosos fallos de seguridad. Al copiar un texto de un sitio a otro, muchas funciones de C copiaban el texto original en el destino, terminando la copia al encontrar el caracter 0x00 y sin tener en cuenta que el espacio de memoria en el destino podía ser menor que el necesario para copiar el texto. Esto hace que la copia escriba fuera del espacio de memoria de destino, pudiendo llegar a escribir partes del texto en zonas de memoria que contienen un programa. Es decir, que copiando un texto terminamos modificando un programa en memoria. Glorioso.
 
Todo esto a parte del hecho de que no es muy seguro utilizar un carácter especial para indicar el final. ¿Y si queremos incluir ese carácter dentro de nuestro texto? Y ahí se lia todo con carcteres de escape.
Entonces llegó otro lumbreras y dijo "En Pascal guardaremos la longitud al inicio del texto." con lo que un texto quedaba:
(15)Mi bonito gato. (14)Mi fiel perro.

Y comenzamos con los cambios innecesarios. En C, el primer carácter de un texto es miVariable[0]. En Pascal ahora era miVariable[1], porque en miVariable[0] estaba guardada la longitud.
Y aquí es cuando los programadores, cuando leen, por ejemplo, "el quinto carácter" comienzan a preguntarse "¿el primero es el cero o el uno"?
Aquí debo levantar la mano y estamparla en la mesa para aclarar algunas cosas que són básicas y no veo que en general los programadores tengan claro cuando hablan.
Respecto a una lista de elementos de longitud n:
     Posición: de 1 a n
     Índice: de 1 a n
     Desplazamiento: de 0 a n-1
Las cosas no se numeran "0, 1, 2 ...", no. Se numeran de 1 en adelante.

¿Entonces por qué el "desplazamiento" o la insistencia en numerarlas desde cero? La respuesta es técnica y se debe a que cuando se reserva memoria para guardar un dato, luego se accede a la memoria a partir de la dirección de memoria obtenida. El primer byte está precisamente en esa misma dirección, el segundo en esa dirección más 1 byte, de desplazamiento respecto a la dirección de inicio. Pero es un motivo técnico que no tiene nada que ver con cómo se numeran las cosas.
Siguiendo con eso de Pascal de guardar la longitud de un texto en su inicio y en un byte.
Problemón: como la longitud se guardaba en el primer carácter, la cadena sólo podía ser de hasta 255, que es lo que se puede representar en un byte. Así que solo podemos usar cadenas cortas: estupendo.

Entonces los de Java deciden cortar por lo sano y dicen que "Los textos son inmutables.". O dicho de otra forma: no se pueden modificar una vez creados; sólo se pueden construir nuevos textos.
Se puede estar más o menos contento con esto, pero que se tomase esta decisión en un lenguje que es practicamente interpretado y no especialmente brillante por su rapidez, es un error. Eso sí, para no marear mucho a los programadores, permiten hacer operaciones con cadenas de texto como si fueran tipos de datos nativos del lenguaje: otro error.
De hecho Java tiene un pie en la programación orientada a objetos y el otro no acaba de sacarlo del cubo de los tipos de datos tradicionales, por lo que los programadores deben ir con cuidado para evitar hacer cosas que permite Java pero que son claramente errores, como comparar cadenas de texto con el doble igual (==). Son esas cosas conocidas como pitfalls.
Recordemos además que en Java no se destruyen explícitamente los objetos creados y que ya no se usan, porque quien se encarga de destruirlos es el "garbage collector" y lo hace de forma automática.
Esto termina siendo otro problemón, ya que una simple operación de concatenación de cadenas de texto termina creando múltiples de ellas que luego se tienen que "reciclar" con el "garbage collector". La solución que eligieron fue algo que es engorroso y poco natural de utilizar, pero que sí es orientado a objetos: StringBuilder.

Pueden incluir en Java 6 que un switch-case pueda utilizar Strings pero no pueden utilizar estas ultimas como objetos que son ni hacerlas modificables. Es más fácil hacerlas "inmutables" contra toda lógica y sentido común.

Mientras tanto, Oracle, cuando aún no había comprado SUN, tenía su guerra particular con los tipos de datos de texto en su base de datos del mismo nombre: CHAR y VARCHAR.

CHAR es sencillo de entender: un campo de este tipo tiene una longitud fija de caracteres y si se intenta guardar una cadena de texto de menor longitud, Oracle la alarga añadiendo espacios al final hasta que sea del tamaño que tiene definido el campo.
Este tipo de datos tiene su origen en los primeros tiempos de las bases de datos, cuando hacer que un registro tuviera un tamaño fijo facilitaba y agilizaba enormemente moverse entre ellos. Hoy en día esto no tiene sentido y sí es una fuente de problemas, como concatenar un nombre y un apellido y que el resultado tenga un churro de espacios en medio. Esto provoca que las sentencias SQL y los programas que usan bases de datos estén llenitos de intrucciones TRIM (quitar espacios de la derecha).

VARCHAR es más lógico: una cadena de texto con una longitud máxima, pero que si se guarda una cadena de menor longitud, se guarda fielmente esa cadena, sin espacios adicionales detrás. Pero tiene un detalle que es la monda: Oracle decidió que, para mantener la compatibilidad con el código ya existente consideraría que una cadena vacía (una cadena de cero caracteres) sería lo mismo que una cadena nula (NULL, la ausencia de una cadena). Totalmente incoherente con lo que representa NULL, pues es muy distinto decir que no tienes una cadena de texto a decir que la tienes pero que no contiene caracteres.

Si en un campo guardase los nombre de los genios que conozco en Oracle, una cadena vacía sería que no conozco a ninguno y una cadena nula que no he contestado a la pregunta, ambas cosas muy distintas.

Para colmo, si en un campo VARCHAR se guarda un espacio, Oracle guarda una cadena vacía, que para ellos es NULL, y es esto último precisamente lo que guardan. Estaban de pitorreo el día que lo hicieron en Oracle.

Y VARCHAR2 es sinónimo de VARCHAR. Oracle quiere dar "color" al código poniendo dos nombres al mismo tipo de datos, aunque sea sólo para liar.

Al mismo tiempo, en Oracle decidieron disparase en el pie al afirmar que si la variable a contiene NULL, el resultado de comparar a y NULL (a=NULL) es... UNKNOWN. !Bravo¡. Alguien en Oracle cree que NULL equivale a infinito, o un concepto igual de extraño. Pero eso ya es el tema de otro texto que pronto estará aquí.

Todos estos problemas surgen de las limitaciones tecnologicas del momento, pero que arrastremos estas decisiones hasta hoy es algo engorroso y lamentable. En definitiva, una pena.

No hay comentarios :

Publicar un comentario