La batallita del agüelo:
Nota:
Si no quieres tragarte todo este rollo, puedes ir
directamente a la explicación y al código de ejemplo.
Por si te preguntas que hace el Guille a estas alturas de la vida explicando
cómo acceder a ficheros del tipo
dBase, los que
tienen la extensión .dbf, (que sin que nadie se moleste, son de una
tecnología más vieja que yo), decirte que la verdad es que NUNCA he necesitado
acceder a ese tipo de ficheros de bases de datos... hasta hace un par de días...
Fíjate si nunca los he usado que lo que tengo en mi sitio
sobre cómo acceder a
los ficheros dBase desde Visual Basic 6.0 (o anterior) está
escrito por colaboradores, y fue algo que publiqué por primera
vez en Noviembre de 1998 (¡hace más de 10 años!).
Como te digo nunca he necesitado saber cómo acceder a este
tipo de bases de datos (las de extensión .dbf), pero el otro
día, un colega de aquí de Nerja, que está usando un programa que
hice en MS-DOS hace ya más de 20 años (aunque lo sigo
manteniendo) y me dijo que necesitaba que le pasara datos con
formato de la exportación de ContaPlus a mi programa.
Cuando he tratado con este tipo de exportaciones, siempre he
preferido el formato ASCII, o sea, en texto de longitud fija,
pero resulta que esos datos que quería importar en mi programa
los había exportado a su vez de un programa de hotel y resulta
que la gente esa del programa de hotel (que por cierto le han
cobrado una pasta gansa por el programa y además tienen un
contrato mensual de mantenimiento de 60 euros mensuales), pasan
olímpicamente de este colega y solo hacen darle largas... y como
resulta que el formato de exportación del programa de hotel
(para compatibilizar con ContaPlus) es del tipo XBASE, es decir,
en ficheros con la extensión .dbf, que para más señas son del
tipo usado por Visual FoxPro (ahora te cuento porqué hago esta
aclaración), pues... tuve la necesidad de "aprender" a usarlos
para así poder leer la información y guardarla en el formato que
usa mi programa.
¿Y qué hace uno cuando necesita saber algo de programación?
(aparte de entrar en el sitio del Guille)
Pues eso, buscar en los buscadores de Internet (en mi caso, sólo
usé Google, pa que te voy a decir otra cosa).
¿Y qué fue lo que me encontré?
Pues... aparte de algún que otro link a lo que tengo en mi sitio
para VB6 o anterior, algunas cosas más... pero casi todos los
ejemplos usaban cadenas de conexión para dBase IV o dBase III y
los que había para usar con FoxPro, pues... no me funcionaban,
además probando de dos formas, es decir, con los proveedores
para usar con objetos del tipo OleDb u Odbc.
Cuando usaba los drivers para dBase, tanto desde OleDb como
desde Odbc, el error que me daba era:
External table is not in the expected format,
que traducido (casi libremente) viene a decir: La tabla externa
no está en el formato esperado
(en el mensaje de Odbc añadía ERROR [HY000] y cuatro cosas más,
pero en el fondo el error era el mismo).
Decirte que las bases de datos .DBF en realidad son tablas,
es decir, cada fichero es una tabla. Te digo esto, porque al
principio tampoco tenía ni idea de cómo acceder a la tabla (con
la cadena SELECT), pero después me di cuenta de que había que
poner en mismo nombre del fichero (con o sin la extensión, eso
no influye), ya que en la cadena de conexión, lo que hay
que indicar es el directorio en el que están las "tablas" a las
que queremos acceder.
Aclarado esto, te voy a mostrar el código (para Visual Basic
.NET) que usé para acceder a las bases de datos usando OleDb y
Odbc (recuerda que esto solo es válido para bases de datos del
tipo dBase).
Comentar que sBase es una variable de tipo String en la que está el
nombre completo de uno de los ficheros .dbf a los que quiero
acceder, por eso se extrae el directorio a la hora de crear la
cadena de conexión. Por otro lado, la variable sConn es una
variable de tipo String.
Usando objetos de System.Data.OleDb:
sConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & _
System.IO.Path.GetDirectoryName(sBase) & _
";Extended Properties=dBASE IV;"
Using dbConn As New System.Data.OleDb.OleDbConnection(sConn)
Usando objetos de System.Data.Odbc:
sConn = "Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=" & _
System.IO.Path.GetDirectoryName(sBase) & ";"
Using dbConn As New System.Data.Odbc.OdbcConnection(sConn)
Recuerda que estas dos formas solamente funcionan con bases de datos más
antiguas, es decir, las creadas con dBase (y puede que con Clipper, pero no te
lo puedo asegurar). Es decir, NO serviría para abrir bases de datos creadas con
FoxPro (o compatibles con el formato usado por ese lenguaje).
Sigo con mi batallita.
La cuestión es que los ficheros de prueba que yo tenía no los abría con
esas cadenas de conexión y probando con otras específicas de FoxPro,
tampoco, ya que me decía que no tenía nada que hacer...
Si probaba OleDb con la cadena:
Provider=vfpoledb.1;Data Source=<el directorio>
El error era: The 'vfpoledb.1' provider is not registered on the local
machine, que traducido sería algo así como que el proveedor ese no está
registrado en la máquina local (también probé con vfpoledb).
Si probaba con Odbc usando la cadena (que finalmente es la correcta):
Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=<el
directorio>
El error era: Driver does not support this function, que traducido
sería algo así como: El driver no soporta esta función.
El código de OleDb lo probé mucho después que el del Odbc, ya que si me
llega a dar un error como ese de OleDb, en el que se indica "claramente" que
el proveedor usado no está registrado, me hubiera dado cuenta de que me
faltaba algo en mi equipo para que esto funcionara, ya que el error ese de
que el driver no soporta la función, lo que me viene a decir, es que el
driver SÍ está presente, pero que alguno de los parámetros está mal.
En las pruebas que hice la cadena de conexión tenía más opciones que después
me he dado cuenta que no son necesarias, al menos si se quieren usar los
valores predeterminados, ya que la cadena de conexión era algo así:
"Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=" &
System.IO.Path.GetDirectoryName(sBase) & ";Exclusive=No; Collate=Machine;NULL=NO;DELETED=NO;BACKGROUNDFETCH=NO;"
Y como en mi búsqueda me topé con programas que abrían esos tipos de
ficheros e incluso me permitían exportarlos a otros formatos, y algunos de
ellos los abrían todos, lo que pensé es que el Guille es más torpe de la
cuenta y no es capaz de abrir un fichero de tipo .DBF.
Pero no soy tan torpe, ya que finalmente conseguí lo que me proponía
(bueno, en realidad, algo torpe si que soy, pero no demasiado, al menos eso
es lo que puedo aparentar, particularmente después de haber dado con la
clave de mis quebraderos de cabeza con los dichosos ficheros .dbf).
La cuestión es que uno de estos programas te permitían exportar los datos
a formato dBase IV, dBase III, FoxPro, etc. Así que, los exporté. Y pude
abrir (usando el código de OleDb) los que tenían formato dBase, pero no los
de FosPro ni los que tenía originales.
Y como soy muy morruo (además de torpe), pensé que
si ese programa (y otros dos más que probé) son capaces de abrir ese tipo de
ficheros... pues... eso, que ¿por qué yo no iba a ser capaz?
La cosa es que me busqué la estructura de este tipo de ficheros, lo abrí
con un editor de texto (en modo binario y normal a ver si...) y lo único que
saqué en claro es lo que después vi en
la página de explicación del formato .dbf y en la que explica los
formatos según el primer byte del fichero, que para el fichero con formato
dBase III era 03, para dBase IV era 43 y para FoxPro era 30 (todo en
hexadecimal).
Y como resulta que los ficheros que yo tenía también tenían un 30, fue fácil
deducir que estaban en formato de Visual FoxPro, por tanto... tenía que usar
las cadenas de conexión de FoxPro, pero... si no me funcionaban... (recuerda
que me daba error de que el driver no soportaba la función) ¿qué hacer?
Pues... lo que tendría que haber hecho unas cuantas horas antes: ir al
sitio de Microsoft y bajarme los drivers para Visual FoxPro (si es que es de
cajón, pero... en fin...).
Y eso hice, busqué dónde estaban esos drivers, los bajé, los instalé y
todo funcionando, además, con esa cadena de conexión me permitía abrir los
tres tipos de ficheros .dbf... en fin...
Usa este link si necesitas
descargar los drivers ODBC de Microsoft Visual FoxPro para abrir
cualquier tipo de ficheros .dbf (al menos los compatibles) y con toda
seguridad, algunos más (los usados por ese lenguaje).
Y este otro link es para
descargar los drivers OLEDB de Microsoft Visual FoxPro 9.0 (aunque este
ni lo he bajado, ni por supuesto, lo he instalado ni probado).
Y ahora te explicaré algo del formato .dbf y te mostraré el código para
abrir y acceder a bases de datos que están en estos tipos de ficheros
(usando ODBC), y como dice el título de este artículo, usando Visual Basic
.NET y Visual C#.
Si no quieres saber "un poco" de cómo es el formato de los ficheros .dbf
puedes pasar directamente al código de cómo acceder
a los ficheros .dbf usando ODBC (por supuesto con código para Visual Basic .NET
y para el de
Visual C#).
Explicación (rápida) de qué es un fichero de tipo .dbf
Sin enrollarme demasiado, comentarte que un fichero .dbf (al menos los
compatibles con el original de dBase o los usados por los lenguajes
conocidos como
xBase: dBase,
Clipper,
FoxPro
/ Visual Fox Pro, etc.) en realidad es el equivalente a una
tabla de una base de datos. Ese fichero incluye la información de los campos
que tiene esa tabla, de qué tipos son, cuántos registros hay y cuáles son
esos registros (si es que hay alguno).
Nota de los cuelgues del Expression Web 2:
Al ir a añadir el link del texto de abajo, se me colgó y como ese texto lo
escribí y no guardé antes de ir a poner el link, pues... lo perdí... en
fin... por suerte, se cuelga menos que antes...
Y otra vez se ha colgado, esta vez, al poner los links del párrafo de
arriba... en fin... tendré que reiniciar el equipo a ver si... ¡zeñó, zeñó,
que crú con el e-precion güé!
Si quieres saber más sobre la estructura de los ficheros xBase (extensión
.dbf), puedes verlo desde este link:
Estructura de los ficheros .dbf.
Como el título dice, vamos a usar las clases del espacio de nombres
System.Data.Odbc para acceder a los ficheros con extensión .dbf, que tal
como te he comentado antes, lo que contienen es una tabla.
Para crear el ejemplo, tendrás que crear una aplicación de Windows,
añadir dos cajas de texto, dos botones y un DataGridView (todo esto si
tienes el Visual Studio 2005 o superior).
Una de las cajas de texto (txtFic) tendrá el path a un fichero con la
extensión .dbf (admite drag & drop), de lo que haya en esa caja de textos se
usará solo el nombre del directorio.
La otra caja de texto (txtSelect) tendrá el nombre del fichero al que
queremos acceder, y tal como ya te he comentado, ese nombre de fichero en
realidad lo usaremos como nombre de la tabla. Y tal como he comentado antes,
se puede indicar tanto con la extensión .dbf como sin ella. En las
operaciones de abrir o arrastrar y soltar se toma el nombre del fichero
soltado (o abierto) sin la extensión y es lo que se asigna en esa caja de
textos.
Los botones sirven para seleccionar un fichero (btnExaminar) y el otro para
abrir la tabla (btnAbrir) y mostrarla en el grid (dgvDiarios).
Este es el código de Visual Basic para abrir la "base de datos"
(más
abajo tienes el código completo de VB del formulario):
Dim sBase As String = txtFic.Text
Dim sSelect As String = "SELECT * FROM " & txtSelect.Text
Dim sConn As String
sConn = "Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=" & _
System.IO.Path.GetDirectoryName(sBase) & ";"
Using dbConn As New System.Data.Odbc.OdbcConnection(sConn)
Try
dbConn.Open()
Dim da As New System.Data.Odbc.OdbcDataAdapter(sSelect, dbConn)
Dim dt As New DataTable
da.Fill(dt)
dgvDiarios.DataSource = dt
dbConn.Close()
Catch ex As Exception
MessageBox.Show("Error al abrir la base de datos" & vbCrLf & ex.Message)
Exit Sub
End Try
End Using
Este es el código de Visual C# para abrir la "base de datos"
(más abajo
tienes el código completo de C# del formulario):
string sBase = txtFic.Text;
string sSelect = "SELECT * FROM " + txtSelect.Text;
string sConn;
sConn = "Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=" +
System.IO.Path.GetDirectoryName(sBase) + ";";
using (System.Data.Odbc.OdbcConnection dbConn = new System.Data.Odbc.OdbcConnection(sConn))
{
try
{
dbConn.Open();
System.Data.Odbc.OdbcDataAdapter da = new System.Data.Odbc.OdbcDataAdapter(sSelect, dbConn);
DataTable dt = new DataTable();
da.Fill(dt);
dgvDiarios.DataSource = dt;
dbConn.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error al abrir la base de datos\n" + ex.Message);
return;
}
}
Y esto es todo... solo comentarte que te fijes en la cadena SELECT, en la que
se indica el nombre del fichero al que queremos acceder (y en la cadena de
conexión hay que indicar el directorio en el que está ese fichero), y que puede
estar con o sin la extensión .dbf.
Espero que te sea de utilidad.
Nos vemos.
Guillermo
Resto del código del formulario de prueba
Imports System
Imports System.Windows.Forms
Imports System.Data
Public Class Form1
Private Sub btnExaminar_Click(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles btnExaminar.Click
Dim oFD As New OpenFileDialog
With oFD
.Filter = "Ficheros DBF (*.dbf)|*.dbf|Todos (*.*)|*.*"
.FileName = txtFic.Text
If .ShowDialog = DialogResult.OK Then
txtFic.Text = .FileName
' El nombre del fichero
txtSelect.Text = System.IO.Path.GetFileNameWithoutExtension(txtFic.Text)
btnAbrir_Click(Nothing, Nothing)
End If
End With
End Sub
Private Sub btnAbrir_Click(ByVal sender As Object, _
ByVal e As EventArgs) _
Handles btnAbrir.Click
Dim sBase As String = txtFic.Text
Dim sSelect As String = "SELECT * FROM " & txtSelect.Text
Dim sConn As String
sConn = "Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=" & _
System.IO.Path.GetDirectoryName(sBase) & ";"
Using dbConn As New System.Data.Odbc.OdbcConnection(sConn)
Try
dbConn.Open()
Dim da As New System.Data.Odbc.OdbcDataAdapter(sSelect, dbConn)
Dim dt As New DataTable
da.Fill(dt)
dgvDiarios.DataSource = dt
dbConn.Close()
Catch ex As Exception
MessageBox.Show("Error al abrir la base de datos" & vbCrLf & ex.Message)
Exit Sub
End Try
End Using
End Sub
Private Sub Form1_DragDrop(ByVal sender As Object, _
ByVal e As DragEventArgs) _
Handles Me.DragDrop, txtFic.DragDrop
' Drag & Drop, aceptar el primer fichero
If e.Data.GetDataPresent("FileDrop") Then
txtFic.Text = CType(e.Data.GetData("FileDrop", True), String())(0)
txtSelect.Text = System.IO.Path.GetFileNameWithoutExtension(txtFic.Text)
End If
End Sub
Private Sub Form1_DragEnter(ByVal sender As Object, _
ByVal e As DragEventArgs) _
Handles Me.DragEnter, txtFic.DragEnter
' Drag & Drop, comprobar con DataFormats
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
e.Effect = DragDropEffects.Copy
End If
End Sub
End Class
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_DragEnter(object sender, DragEventArgs e)
{
// Drag & Drop, comprobar con DataFormats
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effect = DragDropEffects.Copy;
}
}
private void Form1_DragDrop(object sender, DragEventArgs e)
{
// Drag & Drop, aceptar el primer fichero
if (e.Data.GetDataPresent("FileDrop"))
{
txtFic.Text = ((String[])e.Data.GetData("FileDrop", true))[0];
txtSelect.Text = System.IO.Path.GetFileNameWithoutExtension(txtFic.Text);
}
}
private void btnExaminar_Click(object sender, EventArgs e)
{
OpenFileDialog oFD = new OpenFileDialog();
oFD.Filter = "Ficheros DBF (*.dbf)|*.dbf|Todos (*.*)|*.*";
oFD.FileName = txtFic.Text;
if (oFD.ShowDialog() == DialogResult.OK)
{
txtFic.Text = oFD.FileName;
// El nombre del fichero
txtSelect.Text = System.IO.Path.GetFileNameWithoutExtension(txtFic.Text);
btnAbrir_Click(null, null);
}
}
private void btnAbrir_Click(object sender, EventArgs e)
{
string sBase = txtFic.Text;
string sSelect = "SELECT * FROM " + txtSelect.Text;
string sConn;
sConn = "Driver={Microsoft Visual FoxPro Driver};SourceType=DBF;SourceDB=" +
System.IO.Path.GetDirectoryName(sBase) + ";";
using (System.Data.Odbc.OdbcConnection dbConn = new System.Data.Odbc.OdbcConnection(sConn))
{
try
{
dbConn.Open();
System.Data.Odbc.OdbcDataAdapter da = new System.Data.Odbc.OdbcDataAdapter(sSelect, dbConn);
DataTable dt = new DataTable();
da.Fill(dt);
dgvDiarios.DataSource = dt;
dbConn.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error al abrir la base de datos\n" + ex.Message);
return;
}
}
}
}
Glosario:
Morruo: Tozudo, obstinado, cabezón,
en el sentido de cabezota que cuando se le mete algo en la cabeza no hay
quién lo convenza de otra cosa.
Espacios de nombres usados en el código de este artículo:
System.Data
System.Data.Odbc
System.IO