Что нового

[ADODB] Работа с DBF файлами

F9

Новичок
Сообщения
71
Репутация
2
Добрый день!

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

Во-первых, очень полезный ресурс https://www.connectionstrings.com/, будет полезен всем кто пишет свои внешние обработчики БД.

Во-вторых, ресурс http://www.script-coding.com/ADO.html также подарит много методов и свойств для работы с полученными данными.

Итак, извлечение данных из DBF-файла (занесение в массив):

Код:
#include <Array.au3>
Dim $Arr[0][2]
	Global $Connect = ObjCreate ("ADODB.Connection")
	$Connect.Open("Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=C:Temp\DBF\")  ;Dbq - путь к папке, где лежат dbf-файлы. Сам файл указывать не нужно

	If $Connect.State = 0 Then
		MsgBox(0,"","Невозможно подключиться к БД",5)
        Exit
        EndIf
$rs = ObjCreate("ADODB.recordset")  ; создание объекта RecordSet
$rs.open ('"Select * from test.dbf"', $Connect)
With $rs
			While Not .EOF
				_ArrayAdd($Arr, $rs.Fields(1).Value &"|"& $rs.Fields(2).Value)
				.MoveNext
			WEnd
		EndWith
_ArrayDisplay($Arr)
$rs.Close


Объект Recordset состоит из записей и полей. Не все свойства объекта поддерживаются всеми провайдерами. Например пресловутое RecordCount будет априори "-1", т.к. не поддерживается этим провайдером.

А вот с UPDATE я сначала прилип, так как логика UPDATE провайдера Microsoft dBASE Driver отличается от MySQL или FireBird.
К примеру, данные команды
Код:
$rs.open("UPDATE TEST.DBF FILE SET FILE.Field1 = '100500' WHERE FILE.Field2 = '1'")
$rs.Execute("UPDATE TEST.DBF FILE SET FILE.Field1 = '100500' WHERE FILE.Field2 = '1'")

Не дадут никакого эффекта, хотя и ошибок выполнения не будет. Почему-то даже $rs.Update или $rs.Commit или $rs.CommitTrans не дают эффекта сохранения данных рекордсета в файл.
Правильно будет использовать
Код:
;~ .... все из предыдущего примера....
$sqlCMD = ObjCreate("ADODB.Command")  ; Создание объекта Command
$sqlCMD.ActiveConnection = $Connect
$sqlCMD.CommandText = "UPDATE TEST.DBF FILE SET FILE.Field1 = '100500' WHERE FILE.Field2 = '1'"
$sqlCMD.Execute


8)
UPD:
Есть еще один способ извлечения данных и записи (частично) - через UDF
DBF.7z - во вложении
Вполне рабочий инструмент, отстутствуют только функции Update, insert и тп


:whistle: :whistle:
UPD2:
Еще один пример чтения DBF-файла из сети:
Код:
#include-once
;===============================================================================
;
; Description:      Directly read DBF database file to array
; Parameter(s):     $sFileName - name of DBF file
;                   $nFlags
;                       1 - convert from OEM to ANSI
;                       2 - strip leading and trailing witespaces
; Requirement(s):   Autoit 3.2.9.0 +
; Return Value(s):  On Success - 2D array, first row - names of fields
;                   On Failure - no decent way to check errors
; Author(s):        Dmitry Yudin (Lazycat)
; Version:          0.3
; Date:             14.12.2009
; Note(s):          Many DBF functions and flags are not read - just because
;                   I wasn't need it
;
;===============================================================================


_ArrayDisplay(_FileReadDBF(@ScriptDir & "\File.dbf",2))


Func _FileReadDBF($sFileName, $nFlags = 0)
    Local $hFile = FileOpen($sFileName, 16)
    Local $pReadBuffer = DllStructCreate("byte[32]") ; Header size
    DllStructSetData($pReadBuffer, 1, FileRead($hFile, 32))
    $pDBFHeader = DllStructCreate("byte;byte;byte;byte;int;short;short;byte[20]" , DllStructGetPtr($pReadBuffer)) ; Header struct
    $nRecords = DllStructGetData($pDBFHeader, 5) ; Get number of records
    $nDataPos = DllStructGetData($pDBFHeader, 6) ; Get data start position
    $nRecordSize = DllStructGetData($pDBFHeader, 7) ; Get record size (included deleted flag)
    $nFields = Floor($nDataPos / 32) - 1
    $nDataGap = $nDataPos - ($nFields + 1) * 32

    Local $aData[$nRecords+1][$nFields]
    Local $sRecordStruct = "byte;" ; Struct string, based on fields size and type, first byte - deleted mark

    For $i=0 To $nFields - 1
        DllStructSetData($pReadBuffer, 1, FileRead($hFile, 32))
        $pField = DllStructCreate("char[11];byte;int;byte;byte[15]" , DllStructGetPtr($pReadBuffer)) ; Field structure
        $aData[0][$i] = DllStructGetData($pField, 1) ; Name of field
        ; Create struct based on field size and type (now unfinished, treat all types as string)
        Switch DllStructGetData($pField, 2)
            Case Asc("C") or Asc("D") ;or Else
                $sRecordStruct &= "char[" & DllStructGetData($pField, 4) & "];"
        EndSwitch
    Next

    $sRecordStruct = StringTrimRight($sRecordStruct, 1) ; Trim last ";"

    FileRead($hFile, $nDataGap) ; Skip ending marker, it's size may vary

    $pReadBuffer = DllStructCreate("byte["&$nRecordSize&"]") ; New buffer, now based on record size

    For $i = 1 To $nRecords
        DllStructSetData($pReadBuffer, 1, FileRead($hFile, DllStructGetSize($pReadBuffer)))
        $pRecord = DllStructCreate($sRecordStruct , DllStructGetPtr($pReadBuffer))
        For $j = 0 To $nFields - 1
            $aData[$i][$j] = DllStructGetData($pRecord, $j+2)
            If BitAND($nFlags, 1) Then $aData[$i][$j] = _Ascii2Ansi($aData[$i][$j])
            If BitAND($nFlags, 2) Then $aData[$i][$j] = StringStripWS($aData[$i][$j], 3)
        Next
    Next
    FileClose($hFile)
    Return $aData
EndFunc

; Helper function, convert OEM text to ANSI
Func _Ascii2Ansi($sText)
  Local $src = DllStructCreate("char[" & StringLen($sText) + 1 & "]")
  Local $dst = DllStructCreate("char[" & StringLen($sText) + 1 & "]")
  DllStructSetData($src, 1, $sText)
  DllCall("user32.dll", "int", "OemToCharA", "ptr", DllStructGetPtr($src), "ptr", DllStructGetPtr($dst))
  Return DllStructGetData($dst, 1)
EndFunc
 

Вложения

  • dbf.7z
    10.2 КБ · Просмотры: 15

ra4o

AutoIT Гуру
Сообщения
1,165
Репутация
246
Интересное решение, но лично для себя нашел интересную библиотеку в комплекте с dll - широкий набор функций, просто и удобно в работе. Архив прилагаю, пробуйте.
 

Вложения

  • DBF.rar
    12.6 КБ · Просмотры: 24
Автор
F9

F9

Новичок
Сообщения
71
Репутация
2
ra4o сказал(а):
Интересное решение, но лично для себя нашел интересную библиотеку в комплекте с dll - широкий набор функций, просто и удобно в работе. Архив прилагаю, пробуйте.
Увы, с ней нельзя выполнять стандартные запросы.




Добавлено:
Сообщение автоматически объединено:

Столкнулся с еще одной проблемой : не выполняется INSERT.
Синтаксис самого запроса корректен, проверял на таблицах через VFP 9.0

Код:
Global $Connect = ObjCreate ("ADODB.Connection")
	$Connect.Open("Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq="& @ScriptDir& "\DBF\")
	$Connect.Mode=3

$sqlCMD = ObjCreate("ADODB.Command")
$sqlCMD.ActiveConnection = $Connect
$sqlCMD.CommandType = "adCmdTable"
$sqlCMD.CommandText = "insert INTO '15_09.DBF' (Ndok, Typedoc) VALUES ('7','7')"  ;не происходит никакого INSERT данных
;~ $sqlCMD.CommandText = "UPDATE 15_09.DBF FILE SET FILE.Typedoc = '12' WHERE FILE.ndok = '1'" ; UPDATE при этом совершенно прекрасно работает.
;~ $Connect.Execute("insert INTO '15_09.DBF' (DateReg, Ndok, Typedoc) VALUES ({^2016-01-02},'5','1')")  ;так тоже не выполняется

$sqlCMD.Execute
 
Верх