Как читать файл dbf/dbt напрямую?

Кто-нибудь знает, как читать данные напрямую из набора файлов dBase .DBF/.DBT?

Подробности:

Я пытаюсь написать парсер в соответствии со спецификациями dBase для файлов dbf/dbt.

Файл DBF относительно прост, при этом значение в поле MEMO представляет собой порядковый номер блока, с которого предположительно начинаются данные для этого поля в файле dbt.

Файл DBT не определен глубоко в спецификациях. Когда я анализирую файл DBT (который в соответствии со спецификациями состоит из последовательных блоков [размером 512 байт каждый] с блоком 0, являющимся блоком заголовка), я вижу дополнительные байтовые данные, разбросанные между данными записи (некоторые выглядят как "мусорный" двоичный файл данные, некоторые выглядят как другие имена таблиц в БД). С некоторыми дополнительными данными, содержащими буквы / цифры, это делает невозможным чтение только данных записи блока. Я не вижу четкого определения этих странных данных в спецификациях. Я предполагаю, что это могут быть какие-то данные заголовка, но они, похоже, не имеют фиксированной ширины байта или даже появляются в одном и том же месте для каждого блока.

Кроме того, последовательный номер блока в поле Memo файла DBF не всегда совпадает с фактическими данными. Т.е. запись 2 в dbf говорит, что она начинается в блоке 2, но на самом деле в файле dbt она начинается в блоке 6.

Кто-нибудь знает больше информации о структуре файла DBT? Может быть, что-то, что мне не хватает?

Пример кода (VB.Net):

' Holds information about data in the header .dbf file.
Public Class HeaderFileClass
    Public Property AccountNo As String     ' 6 bytes
    Public Property BlockNumber As String   '10 bytes
    Public Property DateInfo As String      '8 bytes
    Public Property EditBy As String        '3 bytes
    Public Sub New()
        AccountNo = String.Empty
        BlockNumber = String.Empty
        DateInfo = String.Empty
        EditBy = String.Empty
    End Sub
    Public Sub New(newAcctNo As String, newBlockNo As String, newDateInf As String, newEditBy As String)
        AccountNo = newAcctNo
        BlockNumber = newBlockNo
        DateInfo = newDateInf
        EditBy = newEditBy
    End Sub
End Class
' Strips a byte array of anything but alpha-numerics, space, or line feed.
Private Function CleanBytes(ByRef bytes As Byte()) As Byte()
    Dim newBytes As Byte()
    Dim BLOCKSIZE As Integer = 512
    Dim j As Integer = 0
    Dim strOut As String = String.Empty
    ReDim newBytes(BLOCKSIZE)
    newBytes.Initialize()
    For Each i As Byte In bytes
        Dim intVal As Integer = Convert.ToInt32(i)
        If (intVal >= 32 And intVal <= 126) Or intVal = 10 Then
            newBytes(j) = i
            j += 1
        End If
    Next
    Return newBytes
End Function
Private Sub ParseFile()
    Dim fileName As String = "C:\dbbackup\Schalls\Schalls_CleanLegacy\Schall_Clean_DATA\PATNOTES"       ' data location.
    Dim BLOCKSIZE As Integer = 512                  ' Default block size.
    Dim bytes As Byte() = Nothing                   ' bytes to be read from dbt file.
    Dim buffer As Char()                            ' buffer to use for reading dbf file.
    Dim hList As New List(Of HeaderFileClass)       ' DBF header data storage.
    Dim lstData As New List(Of Byte())              ' DBT block data storage.
    ReDim buffer(28)                                ' Set size of buffer array.

    'header file load
    Using inFile As New StreamReader(File.Open(fileName & ".DBF", FileMode.Open))
        ' read DBF header lines.
        inFile.ReadLine()
        inFile.ReadLine()

        ' read DBF data.
        While inFile.Read(buffer, 0, 28) > 0
            Dim strBuf As New String(buffer)
            Dim acctNo As String = strBuf.Substring(0, 7)
            Dim blockNo As String = strBuf.Substring(7, 10).Trim
            Dim dateInfo As String = strBuf.Substring(17, 8)
            Dim editBy As String = strBuf.Substring(25, 3)
            hList.Add(New HeaderFileClass(acctNo, blockNo, dateInfo, editBy))
        End While
    End Using


    'memo file load
    Using inFile As New BinaryReader(File.Open(fileName & ".DBT", FileMode.Open))
        ' read data sequentially by blocksize.
        Do
            bytes = inFile.ReadBytes(BLOCKSIZE)
            If bytes.Length > 0 Then
                lstData.Add(bytes)
            End If
        Loop While bytes.Length > 0
    End Using

    If hList.Count > 2 Then
        For i As Integer = 0 To hList.Count - 2
            Dim h As HeaderFileClass = hList(i)             ' get data for the current record from the header file data. (contains block number to start)
            Dim h2 As HeaderFileClass = hList(i + 1)        ' get the next data for the current record. (contains next starting block number)
            Dim intFrom As Integer = CInt(h.BlockNumber)    ' starting block number.
            Dim intTo As Integer = CInt(h2.BlockNumber)     ' next record's starting block number.
            Dim sbStr As New System.Text.StringBuilder      ' output string.

            ' read the bytes, ensure they are text data, 
            For j As Integer = intFrom To intTo - 1
                sbStr.Append(System.Text.Encoding.ASCII.GetString(CleanBytes(lstData(j))))
            Next
            Debug.Print(sbStr.ToString)
        Next
    End If
End Sub

1 ответ

Вам нужно использовать OLEDB, например:

Imports System.Data.OleDb
Public Class Form1
    Private FileName As String = IO.Path.Combine(Application.StartupPath, "Customer.dbf")
    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        If IO.File.Exists(FileName) Then
            Dim Builder As New OleDbConnectionStringBuilder With
                {
                    .DataSource = IO.Path.GetDirectoryName(FileName),
                    .Provider = "Microsoft.Jet.OLEDB.4.0"
                }
            Builder.Add("Extended Properties", "dBase III")
            Using cn As New OleDb.OleDbConnection With {.ConnectionString = Builder.ConnectionString}
                Using cmd As New OleDbCommand With {.Connection = cn}
                    cmd.CommandText = "SELECT * FROM " & IO.Path.GetFileName(FileName)
                    cn.Open()
                    Dim dt As New DataTable
                    dt.Load(cmd.ExecuteReader)
                End Using
            End Using
        End If
    End Sub
End Class
Другие вопросы по тегам