binary.read возвращает "неожиданный EOF" независимо от того, какой файл dBase (.dbf) я использую

func main() {
        file, err := os.Open("example.dbf") // For read access.
        if err != nil {
            log.Fatal(err)
        }

        dBaseioReader, err := NewReader(file)
        if err != nil {
            log.Fatal(err)
        }
        return nil
}

type dbHeader struct {
    Version             byte
    LastUpdate          [3]byte
    NumRecords          int32
    NumBytesInHeader    int16
    NumBytesInRecord    int16
    _                   [2]byte //reserved
    IncompatFlag        byte
    EncryptionFlag      byte
    MultiUserProcessing [12]byte
    MDXProductionFlag   byte
    LangDriverId        byte
    _                   [2]byte //reserved
    LangDriverName      [32]byte
    _                   [4]byte //reserved
}


type dbFieldDescriptor struct {
    FieldName         [32]byte
    FieldType         byte
    FieldLen          byte
    FieldDec          byte
    _                 [2]byte
    MDXProductionFlag byte
    _                 [2]byte
    NextAutoIncrement [4]byte
    _                 [4]byte
}


type DBaseReader struct {
    rawInput *bufio.Reader
    Header   *dbHeader
    Fields   []*dbFieldDescriptor

    recordsLeft int
}

func NewReader(input io.Reader) (dbr *DBaseReader, err error) {
    dbr = &DBaseReader{
        rawInput: bufio.NewReaderSize(input, 32*1024),
        Header:   &dbHeader{},
    }

    err = binary.Read(dbr.rawInput, binary.LittleEndian, dbr.Header)
    if err != nil{
        return
    }

    dbr.recordsLeft = int(dbr.Header.NumRecords)

    headerBytesLeft := dbr.Header.NumBytesInHeader
    headerBytesLeft -= dbHeaderSize

    // read field descriptors until 0x0D termination byte
    var term []byte
    for {
        field := &dbFieldDescriptor{}

        err = binary.Read(dbr.rawInput, binary.LittleEndian, field)
        if err != nil{
            //FIRST CRASH HAPPENS HERE.
            return
        }

        dbr.Fields = append(dbr.Fields, field)
        headerBytesLeft -= dbFieldDescriptorSize

        // check for terminator byte
        term, err = dbr.rawInput.Peek(1)
        if err != nil{
            return
        }

        if term[0] == 0x0D {
            break
        }
    }

    // read the terminator
    _, err = dbr.rawInput.ReadByte()
    if err != nil {
        return
    }
    headerBytesLeft -= 1

    if headerBytesLeft > 0 {
        err = fmt.Errorf("Error: Header Bytes Left: %d.. Read Properties?!..\n", headerBytesLeft)
        return

        // headerLeftOver := make([]byte, headerBytesLeft)
        // err = binary.Read(dbr.rawInput, binary.LittleEndian, headerLeftOver)
        // if err != nil {
        //  return
        // }

        // props := &dbFieldProperties{}
        // err = binary.Read(dbr.rawInput, binary.LittleEndian, props)
        // if err != nil {
        //  return
        // }

        // fmt.Printf("Props: %#v\n", props)
    }

    // read until first record marker
    _, err = dbr.rawInput.ReadBytes(' ')
    if err != nil {
        return
    }
    return dbr, nil
}

Выше соответствующий код. Программа аварийно завершает работу, независимо от того, какой пример файла dbf я использую. Я не уверен, почему я получаю ошибку "Неожиданный EOF". За последние несколько дней я пытался это выяснить, к сожалению, безуспешно.

1 ответ

Решение

Вы не предоставили никаких доказательств правильности формата вашего файла. Прежде чем написать свою программу, вы должны были подтвердить, что у вас есть правильный формат для файла.

Каковы первые 256 байтов файла? Например,

hex.go:

package main

import (
    "encoding/hex"
    "fmt"
    "io/ioutil"
    "os"
    "strconv"
)

func main() {
    if len(os.Args) <= 1 {
        fmt.Fprintln(os.Stderr, "usage: hex filename [bytes]")
        return
    }
    data, err := ioutil.ReadFile(os.Args[1])
    if err != nil {
        fmt.Fprintln(os.Stderr, "filename:", err)
        return
    }
    n := len(data)
    if len(os.Args) > 2 {
        i, err := strconv.Atoi(os.Args[2])
        if err != nil {
            fmt.Fprintln(os.Stderr, "bytes:", err)
            return
        }
        if n > i {
            n = i
        }
    }
    fmt.Print(hex.Dump(data[:n]))
}

Выход:

$ go run hex.go example.dbf 256
00000000  03 01 04 18 01 00 00 00  41 07 d0 05 00 00 00 00  |........A.......|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 03 00 00  |................|
00000020  54 52 41 43 4b 5f 49 44  00 00 00 43 01 00 00 00  |TRACK_ID...C....|
00000030  0b 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000040  4c 4d 55 4c 54 00 00 00  00 00 00 4c 0c 00 00 00  |LMULT......L....|
00000050  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000060  4e 54 41 58 59 45 41 52  00 00 00 4e 0d 00 00 00  |NTAXYEAR...N....|
00000070  04 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000080  4e 43 4f 55 4e 54 59 43  4f 44 00 4e 11 00 00 00  |NCOUNTYCOD.N....|
00000090  02 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000a0  43 50 52 4f 50 41 44 44  00 00 00 43 13 00 00 00  |CPROPADD...C....|
000000b0  3c 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |<...............|
000000c0  4c 43 4f 4d 4d 49 4e 44  00 00 00 4c 4f 00 00 00  |LCOMMIND...LO...|
000000d0  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000e0  4c 56 41 43 4c 41 4e 44  00 00 00 4c 50 00 00 00  |LVACLAND...LP...|
000000f0  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
$

Уровень таблицы

Но каков уровень стола? Уровень означает его версию. Формат таблицы dBASE - это стандарт, который развивался с течением времени. Когда новая версия dBASE внесла некоторые улучшения в этот формат, был присвоен новый номер уровня формата, идентичный новой версии dBASE. Например, у нас есть уровни 3, 4, 5 и 7, соответствующие dBASE III, dBASE IV, dBASE 5 и Visual dBASE 7. Нет уровня 6, потому что не было Visual dBASE 6.

Уровень 7 принес много улучшений. Имена полей могут содержать до 31 символа (максимум до 10). Появились некоторые новые типы полей (например, поле AutoIncrement, которое делает практически невозможным присвоение одинакового номера двум записям в одной таблице). Если ваши таблицы должны использоваться другим программным обеспечением, вам, возможно, придется пожертвовать этими преимуществами ради совместимости, поскольку лишь немногие приложения могут использовать таблицу уровня 7.

Форматы файлов.dbf:

Байт 0 заголовка файла, биты 0-2 указывают номер версии: 3 для уровня 5 dBASE, 4 для уровня 7 dBASE.

Заголовки DOS уровня 5

Файл данных Xbase (*.dbf)

Файл таблицы dBASE версии 7

Другие вопросы по тегам