zxFortunes, codificación e indexado (y III)
El primer problema que se me presentó al hacer el proyecto consistía en cómo iba a leer los ficheros de texto procedentes del mundo Linux. De entrada, una de los primeros problemas es que mientras en Windows, un final de línea viene acompañado por CR/LF, en Linux sólo lleva uno de ellos. Sabido es que con el acceso E/S estándar de C (me refiero a la familia de funciones fread/fopen/fgets), siempre que el fichero se abra en modo texto, las traducciones a cadenas se realizan automáticamente. Pero en el mundo .NET no pude encontrar algo parecido; es más, dadas las diferentes codificaciones, el .NET lleva toda una rica variedad (y complejidad) de formas de leer un fichero. Son los Encoding, que se encuentran dentro del espacio de nombre System.Text. Tenemos cinco formas de leer un fichero, a saber:
Bueno, pues tras mucho batallar leyendo los ficheros, determiné que el formato era el ASCII. Parece una perogrullada pero no lo es. Me llevó bastante tiempo averiguarlo, porque la primera vez que probé con el formato ASCII falló y leyó mal. El problema estaba en los acentos y la ñ, que no había forma de que se leyeran bien. También tuve otro problema que casi me vuelve loco. Una vez procesados y copiados al formato nativo de Windows, al leerlos de nuevo para sacar la cita aleatoriamente, la cantidad de bytes leída no se correspondía con la contada. Me explico. Iba a la posición 100 del archivo. Leía 50 caracteres. Pues realmente había ido a la 80 y había leído 30. Y no era un problema de tamaños dobles de caracteres ni nada de eso (en un editor hexadecimal, lo guardado en el disco estaba perfectamente almacenado en caracteres unicode), sino de algo interno a Windows. También, en el trabajo, tuve un problema similar con otras herramientas y otros menesteres, al almacenar relativamente grandes ficheros binarios con información más o menos codificadas. Tras mucho batallar y repetir cosas una y otra vez, de repente, la cosa comenzó a funcionar mágicamente cuando antes no lo había hecho… Porque publicaron el SP1 del NET 1.1. En fin. La lata que les di a los pobres de microsoft.public.es.vcsharp.
Indexar sin base de datos
¿Cómo obtener una cita de entre miles de ellas, quizás repartidas entre docenas de archivos diferentes, sin tener ningún motor de bases de datos? ¿Hacerme yo uno? Pues casi, pero no. La idea es generar un archivo de índice, independiente de los ficheros y con registros de tamaño fijo (¿a alguien le suenan los ntx, ndx, px?). Y desde luego no cargarlo en memoria. Ni árboles-b ni ninguna estructura de datos compleja. Un simple par de elementos para cada cita, almacenados consecutivamente, todo en disco. Un primer elemento, una cadena de ancho fijo que almacena el nombre del fichero en donde está almacenada la cita. El siguiente valor contiene la posición de comienzo de la cita dentro del fichero. Ahora sólo queda echar los dados, obtener un valor situado entre cero y el número total de citas (que se almacena al principio del fichero índice), y calcular en base a ese número la posición en el fichero índice del registro:
int posInIndex=2*iFortune*System.Runtime.InteropServices.Marshal.SizeOf(nFile);
Nos vamos a esa posición del fichero, leemos el nombre del fichero de la cita y la posición de inicio. Abrimos el fichero donde realmente se encuentra la cita, nos vamos a la posición indicada, y leemos hasta encotrar un “%” o el final del fichero. Sencillo y extremadamente rápido.
