El formato de los archivos de datos xBase

Mensajes
1,119
Oro
233,145
En una época, ahora más o menos remota, los llamados sistemas xBase de gestión de bases de datos (dBase, Foxbase, FoxPro, Clipper, etc.) dominaron la programación de sistemas de bases de datos en las PC, y eso siguió siendo así a lo largo de muchos años.

En realidad los mencionados sistemas no eran la gran cosa, es cierto, o por lo menos en cuanto a las herramientas de desarrollo utilizadas, como se sabe bastante primitivas si las comparamos con algunas de sus contrapartidas más modernas; en eso estamos de acuerdo, sin embargo, a pesar de lo dicho, sin duda alguna los sistemas como dBase fueron capaces de resolver muchos problemas comunes en su era, cuando reinaba el MSDOS en las microcomputadoras del mundo, y no existían muchas más opciones de todos modos.

Nota: El gestor de base de datos dBase II se comenzó a utilizar en la época cuando era común el sistema operativo CP/M de Digital Research en las microcomputadoras de 8 bits, en una época cuando MSDOS no soñaba con nacer todavía.

En todo caso, a pesar de su evidente antigüedad, la forma de guardar los datos utilizada por todos los sistemas mencionados de la familia xBase, o sea, su formato de archivo por lo común con la extensión ".dbf", se sigue usando a menudo, por un lado porque ahora existen sistemas herederos de los originales, y por otro debido a la simplicidad de diseño del mencionado formato interno de los archivos, la cual no sólo hace posible su interpretación por personas sin demasiada experiencia en la programación de computadoras, sino también los convierte en una solución ideal en muchos desarrollos pequeños o limitados.

En particular podemos encontrarnos con archivos propios de los sistemas comentados en muchos Sistemas de Información Geográfica y otros afines.

En cuanto a los sistemas herederos más modernos se pueden citar sistemas gestores de bases de datos mantenidos por empresas o proveedores como XBase++ de Alaska Software y FlagShip; y sería un crimen no mencionar como entre ellos existen también proyectos gratuitos (con licencia GPL) como Harbor y xHarbour, si mal no recuerdo herederos de Clipper; por su parte, en la plataforma .Net de Microsoft se dispone de XSharp, una implementación de un lenguaje xBase para los entornos Visual Studio 2015, 2017 y 2019.

Por todo esto, en este texto vamos a estudiar cómo era (o es) el formato interno de los archivos donde se guardan los datos en los sistemas xBase, como hemos dicho archivos con la extensión ".dbf" en donde está contenida una tabla de datos, puesto en dichos sistemas una base de datos se conforma con una colección de archivos con la mencionada extensión situados en una carpeta.

Por lo demás, en adición a lo comentado también compartiré una implementación de una clase de RapidQ (una implementación de BASIC con cierta limitada orientación a objetos desarrollada por William Yu) por medio de la cual podremos manipular un archivo ".dbf" por nuestra cuenta sin necesidad de otras bibliotecas de terceros; en este caso en particular la clase se hizo pensando en el formato usado en Foxbase, puesto como podremos ver a lo largo de este texto explicativo, entre las muchas versiones y variantes de los sistemas de la familia xBase se presentaron varias diferencias más o menos significativas, casi siempre en cuanto a los tipos de datos soportados.

Nota: La clase para manipular archivos ".dbf" de Foxbase con RapidQ no fue implementada del todo como me hubiera gustado hacerlo, debido en lo fundamental a las características del lenguaje de programación utilizado y a sus limitaciones, pero por lo menos sirve como demostración de la manera de manipular archivos DBF por nuestra cuenta.

En realidad las diferencias entre versiones o productos fueron más grandes cuando se trataba de los archivos índice o los archivos memo, o debido a características propias sólo de un determinado producto, sin embargo, nosotros no trataremos con dichos archivos índice o memo a pesar de ser los índices de uso obligatorio en una base de datos real para poder acelerar las operaciones cuando se buscan datos en las tablas, en especial si estas contienen muchos registros.

En fin, para comenzar consideraré todos conocen el significado de términos básicos propios del mundo de las bases de datos relacionales como podrían ser “registro” o “campo”, dado cada cual podrá preguntar si no es ese el caso.

Por lo general un archivo propio de xBase consiste, como pasa con muchos otros formatos en el mundo de la informática, en una cabecera en donde se guarda información importante a la hora de interpretar el resto de la estructura del archivo; y a continuación de la cabecera se encuentran los datos de la tabla propiamente dichos en un formato ASCII, hasta llegar al final del archivo en donde, opcionalmente, nos podemos encontrar con el código 0x1A para indicárnoslo.

Nota: Los números con un “0x” delante están en un sistema de numeración hexadecimal; éste es el símbolo comúnmente usado en C para denotar los números en dicha base numérica.

La Tabla 1 mostrada a continuación lista los componentes de la cabecera de uno de estos archivos ".dbf" (la parte final de la cabecera puede variar mucho entre versiones):

Tabla 1: Formato de la cabecera de un archivo “.dbf”
ByteTamaño (bytes)ContenidoDescripción
0010x03DBF simple (FS, D3, D4, D5, Fb, Fp, CL)
0x04DBF simple (FS, D4, D5)
0x05DBF simple (FS, D5, Fp)
0x43DBF con memo “.dbv” de tamaño variable (FS)
0xB3DBF con memo “.dbv“ y “.dbt” (FS)
0x83DBF con memo “.dbt” (FS, D3, D4, D5, Fb, Fp, CL)
0x8BDBF con memo “.dbt” en formato D4 (D4, D5)
0x8EDBF con table SQL (D4, D5)
0xF5DBF con memo “.fmp” (Fp)
013YYMMDDFecha de última actualización
044Entero largoNúmero de registros en el archivo
082Entero cortoTamaño en bytes de la cabecera
102Entero cortoTamaño en bytes del registro
1220x00Reservado
1410x01Transacción iniciada (D4, D5)
0x00Transacción finalizada (D4, D5), ignorado (FS, D3, Fb, Fp, CL)
1510x01Encriptado (D4, D5)
0x00Normal (visible)
16120x00Usado en un entorno multiusuario (D4, D5)
2810x01Existe índice de producción (Fp, D4, D5)
0x00Índice sobre demanda
291nId de manipulador de lenguaje (D4, D5)
0x01Código de página 437 DOS USA (Fp)
0x02Código de página 850 DOS Multilenguaje (Fp)
0x03Código de página 1251 Windows ANSI (Fp)
0xC8Código de página 1250 Windows EE (Fp)
0x00Ignorado (FS, D3, Fb, Fp, CL)
3020x00Reservado
32n*32---------------Descriptor de campo (n indica el número de campos)
n*32+110x0DCarácter de terminación de cabecera

En la Tabla 1 mostrada anteriormente, los símbolos (FS, D3, D4, D5, Fb, Fp, CL) denotan uno de los productos más específicos de la familia xBase como sigue:

FS = FlagShip D3 = dBase III+

Fb = Foxbase D4 = dBase IV

Fp = FoxPro D5 = dBase V

CL = Clipper

Nota: En donde no se especifica uno de estos símbolos se supone ese valor en particular dentro de la cabecera será interpretado igual con independencia del sistema xBase involucrado.

Por lo demás, en la tabla también podemos observar las inevitables diferencias, no tanto en cuanto a la estructura propiamente dicha como en cuanto a los valores almacenados en las distintas partes de la cabecera de un archivo “.dbf”, producto esto como se comentó de la variedad de entornos de desarrollo donde se los utiliza para almacenar la información, por las respectivas necesidades particulares de cada uno de ellos para poder implementar sus propias características o extensiones.

Por último, en la posición 32 dentro de la cabecera del ".dbf" se encuentra situado un arreglo de descriptores de campo cuyo tamaño, como era de esperarse, está definido por la cantidad de los mismos existentes en una tabla en particular, y por tanto no podemos conocer ese datos antes de toparnos con una tabla real, incluso si se sabe cada uno de dichos descriptores está constituido por 32 bytes de datos.

La estructura de uno de dichos descriptores de campos podemos verla representada en la Tabla 2 mostrada a continuación:

Tabla 2: Formato del descriptor de campo de un DBF
ByteTamaño (bytes)ContenidoDescripción
011Caracteres ASCIINombre del campo terminado con 0x00
111C, D, F, N, L, M, V, P, B, G, 2, 4, 8Id de tipo de campo (se explica más adelante)
124nDirección del campo en memoria (D3)
nDesplazamiento desde el comienzo del registro (Fp)
0x00Ignorado (FS, D4, D5, Fb, CL)
161nLongitud del campo en bytes
171nPosiciones decimales del campo
1820x00Reservado
201nId de área de trabajo (D4, D5)
0x00No usado (FS, D3, Fb, Fp, CL)
212nMultiuso dBase (D3, D4, D5)
0x00Ignorado (FS, Fb, Fp, CL)
2310x01Campos conjunto (D3, D4, D5)
0x00Ignorado (FS, Fb, Fp, CL)
2470x00Reservado
3110x01Campo en índice “.mdx” (D4, D5)
0x00Ignorado (FS, D3, Fb, Fp, CL)

En la Tabla 2 mostrada arriba también nos encontramos con las mismas diferencias debidas a los variados entornos de desarrollo de la familia xBase y a sus necesidades o características propias. En particular, el tipo de campo denotado por uno de los caracteres ASCII “C, D, F, N, L, M, V, P, B, G, 2, 4, 8”, es uno de los valores más afectados por esto, puesto como mencioné en un principio, se pueden encontrar tipos de datos soportados por un entorno y no soportados por otro. En este texto vamos a comentar todos los tipos de datos, no obstante, vamos a implementar nada más los tipos de datos más comunes, para no extendernos demasiado, puesto me sería imposible tratar todas las características.

En la Tabla 3 podemos ver el significado de cada símbolo usado para denotar un tipo de datos de un campo de una base de datos de la familia xBase:

Tabla 3: La simbología usada para representar los tipos de datos de un campo
SímboloDescripción
CCadena (Char): Es una cadena de caracteres ASCII rellenada con espacios hasta alcanzar su longitud declarada.
La longitud máxima soportada (n) depende del entorno xBase y es como sigue:
n = 1..64 Kb (FS)
n = 1..32 Kb (Fp, CL)
n = 1..254 (Resto de los sistemas)
DFecha (Date): Es una cadena de caracteres consistente en 8 caracteres ASCII con los dígitos (0..9) representando una fecha en formato YYYYMMDD.
FFlotante (Float): Es una cadena de caracteres ASCII con los dígitos (-.0123456789) necesarios para representar un número en punto flotante (FS, D4, D5, Fp).
La longitud máxima de la cadena (n) puede ir desde 1 hasta 20.
NNumérico (Numeric): Es una cadena de caracteres ASCII con los dígitos (-.0123456789) necesarios para representar un número en punto flotante.
En este caso la longitud máxima (n) depende del entorno y puede ser:
n = 1..20 (FS, Fp, CL)
n = 1..18 (D3, D4, D5, Fb)
LLógico (Logic): Es una cadena de caracteres ASCII de longitud 1 conteniendo uno de los caracteres (YyNnTtFf Space) en FS, D3, Fb, Fp, y CL, o los caracteres (YyNnTtFf ?) en D4 o D5, para representar el contenido de un campo lógico (True o False).
MNota (Memo): Es una cadena de 10 dígitos para representar el número de la posición inicial de los datos reales en el correspondiente archivo “.dbt”; o una cadena vacía de 10 espacios si no existe una entrada en dicho archivo.
VVariable: Es una cadena de datos binarios o ASCII guardada en un archivo “.dbv” (FS).
En el campo del “.dbf” se guarda lo siguiente:
4 bytes binarios = posición inicial de los datos en archivo “.dbv”
4 bytes binarios = tamaño de datos
1 byte = subtipo
1 byte = reservado (0x1A)
En caso de no existir los datos en el archivo “.dbv” el campo almacena 10 espacios en blanco como pasa con los campos memo.
PImagen (Picture): Los datos reales se guardan en formato binario en un archivo “.ftp” (Fp).
El campo se trata de forma similar a un memo (n = 10).
BBinario (Binary): Los datos reales se guardan en formato binario en un archivo “.dbt” (D5).
El campo se trata de forma similar a un memo (n = 10).
GGeneral: Es un objeto OLE (D5, Fp).
El campo se trata de forma similar a un memo (n = 10).
2Entero corto (Short int): Es un entero corto con signo en binario +/- 32767 (FS).
4Entero largo (Long int): Es un entero largo con signo en binario +/- 2147483647 (FS).
8Doble (Double): Es un número en punto flotante tipo double con signo estándar IEEE (FS).

En nuestro caso, de todos los tipos de datos mostrados en la Tabla 3, nada más implementé los correspondientes a los símbolos C, D, N, y L como lo hace Foxbase, puesto son los más comunes y los soportan todos los productos xBase (además de los mencionados Foxbase sólo soporta los campos memo, y como manifesté antes, esos no los trataremos porque no estamos desarrollando un sistema real).

Por último, detrás de la cabecera del archivo “.dbf” comentada hasta este momento, se encuentran situados los registros almacenados dentro de la base de datos o la tabla representada por el mismo, y en este caso cada uno de ellos consiste en una cadena ASCII del largo especificado por la suma de la cantidad de bytes de cada campo existente por separado, más un carácter extra.

Nota: El número para representar la longitud de un registro se guarda en la cabecera del DBF en la posición descrita como el tamaño en bytes del registro.

En particular, la cadena comentada es iniciada con un carácter asterisco (“*”) si ese registro fue eliminado en un momento previo, o con un carácter espacio, si el registro continúa activo.

Nota: En un archivo “.dbf” los registros eliminados en realidad no desaparecen, y por eso pueden ser recuperados con una instrucción Recall propia de dBase, dado sólo se marcan con un asterisco al inicio (código de carácter 0x2A) hasta una llamada a Pack sobre dicho archivo de base de datos, momento cuando todos los registros marcados como eliminados si son removidos de la tabla sin posibilidad de ser recuperados de nuevo.

Es todo, he descrito por completo el formato de un archivo “.dbf” y ahora disponemos de toda la información necesaria para implementar las subrutinas necesarias para poder manipularlo como mismo lo haría un gestor de bases de datos (o casi); como pueden ver, se trata de un formato para una base de datos relacional bastante simple en comparación con otros, como les había comentado en un principio, y por eso no deberemos encontrarnos con grandes problemas; y si a primera vista esto les pareció complicado, no se preocupen, su impresión se debe a haberse descrito todas las características soportadas por los variados entornos, y de este punto en adelante nos será más fácil puesto como he dicho nos centraremos en un archivo propio de Foxbase.

Por supuesto, la información mostrada en este texto fue tomada de distintas fuentes, y por eso en caso de encontrar un error, sería bueno lo comentaran para corregirlo.

Por otro lado, si conocen un sitio web donde se describan más formatos internos de archivos me gustaría pudieran comentarlo; en un época existió uno bastante bueno de nombre wotsit o algo parecido, sin embargo, por lo visto terminó desapareciendo.

Los interesados en ver la implementación práctica de lo comentado pueden hacerlo descargando el código de la clase de RapidQ con el enlace (es necesario Internet): DBFExample.zip.

En este caso dispondrán de una interfaz gráfica para mostrar el contenido de un archivo ".dbf", y también de un par de interfaces de comando destinadas a ver otros datos internos del mismo (los códigos fuente están incluidos).

En todo caso, cada cual puede utilizar el lenguaje de programación de su preferencia en caso de necesitar manipular un archivo DBF por su cuenta, porque para eso sólo necesita la información ofrecida en este texto y un poco de esfuerzo.
 
Por cierto, por lo visto me olvidé de poner este comentario después de publicar lo del otro programa, y creo eso completa un poco más lo tratado en este texto sobre el formato interno de los archivos DBF.

En caso de interesarle a alguien, en la dirección https://cubansolutions.cubava.cu/2023/01/03/accounts/ hay un sencillo programa para llevar las cuentas (gastos e ingresos) hecho con RapidQ usando la misma clase elaborada para este artículo.

En esta ocasión la clase tiene algunos arreglos y agregados, porque la ofrecida para la descarga en este texto no estaba tan completa y tenía algunos errores menores y omisiones, aun cuando es cierto si se viene a ver eso no afectaba en casi nada la exposición sobre el formato del archivo.

En fin, el código fuente del programa conteniendo la clase modificada está también disponible para la descarga, y como en dicho programa se la usa en un proceso más o menos real como lo es un programa para llevar los gastos y los ingresos de una forma sencilla, por eso creo podría resultar útil para enteder mejor todo eso del formato y su funcionamiento interno, y así es posible sirva como complemento a lo tratado antes.
 
Atrás
Arriba