Hello Hua,
I recently spent some time working on direct access to the DBF.
You could build a much more powerful COPY TO command yourself. Where "Filter" is in the code, you can easily add which fields you want to export, and where "Search" is, any condition.
Target and format can also be easily added.
Best regards,
Otto
// DBFToTXT
#include "FiveWin.ch"
PROCEDURE Main
LOCAL cFilePath := "c:\fwh\samples\Data\database.dbf"
LOCAL cName := "clark"
LOCAL aResult
aResult := FindNameInDbf(cFilePath, cName)
IF !EMPTY(aResult)
WriteRecordsToFile(aResult, "c:\fwh\samples\Data\output.txt")
ENDIF
RETURN
FUNCTION FindNameInDbf(cFilePath, cName)
LOCAL nHandle := FOPEN(cFilePath)
LOCAL cHeader := SPACE(32)
LOCAL nHeaderSize, nRecordSize, nNumRecords
LOCAL aFieldDescriptors := {}
LOCAL aFieldOffsets := {}
LOCAL nOffset := 0
LOCAL cFieldDescriptor, cFieldName
LOCAL nFieldLength
LOCAL nNameOffset, nNameLength
LOCAL aMatchingRecords := {}
LOCAL cRecord, cExtractedName
LOCAL hField, hRecordData
LOCAL i, j
LOCAL cFieldValue
LOCAL hFieldDescriptor := { => }
LOCAL nFound := 0
LOCAL cFileData
LOCAL nVersion
LOCAL nYear
LOCAL nMonth
LOCAL nDay
LOCAL LastUpdate
Msginfo("Start Suche")
IF nHandle == -1
? "Konnte die Datei nicht öffnen."
RETURN {}
ENDIF
// Read entire file into memory
cFileData := MEMOREAD(cFilePath)
// Header lesen
cHeader := LEFT(cFileData, 32)
// Byte-Interpretation der Header-Daten
nNumRecords := (ASC(SUBSTR(cHeader, 5, 1)) + (ASC(SUBSTR(cHeader, 6, 1)) * 256) + (ASC(SUBSTR(cHeader, 7, 1)) * 65536) + (ASC(SUBSTR(cHeader, 8, 1)) * 16777216))
nHeaderSize := (ASC(SUBSTR(cHeader, 9, 1)) + (ASC(SUBSTR(cHeader, 10, 1)) * 256))
nRecordSize := (ASC(SUBSTR(cHeader, 11, 1)) + (ASC(SUBSTR(cHeader, 12, 1)) * 256))
// Felddeskriptoren lesen
FOR i := 33 TO nHeaderSize STEP 32
cFieldDescriptor := SUBSTR(cFileData, i, 32)
IF ASC(LEFT(cFieldDescriptor, 1)) == 13
EXIT
ENDIF
cFieldName := RTRIM(SUBSTR(cFieldDescriptor, 1, 11))
nFieldLength := ASC(SUBSTR(cFieldDescriptor, 17, 1))
AADD(aFieldDescriptors, { "name" => cFieldName, "length" => nFieldLength })
NEXT
// Feld-Offsets berechnen
FOR i := 1 TO LEN(aFieldDescriptors)
hFieldDescriptor := aFieldDescriptors[i]
AADD(aFieldOffsets, { hFieldDescriptor["name"], nOffset, hFieldDescriptor["length"] })
nOffset += hFieldDescriptor["length"]
NEXT
nNameOffset := AScan(aFieldOffsets, { |a| LEFT(a[1], 10) = "LAST" })
nNameLength := aFieldOffsets[nNameOffset, 3]
// FILTER, welche Felder
aFieldDescriptors := {}
AADD(aFieldDescriptors, { "name" => "FIRST", "length" => 20 })
AADD(aFieldDescriptors, { "name" => "LAST", "length" => 20 })
xbrowse(aFieldDescriptors)
// Process records
FOR i := 1 TO nNumRecords
cRecord := SUBSTR(cFileData, nHeaderSize + (i - 1) * nRecordSize + 1, nRecordSize)
cExtractedName := ALLTRIM(LOWER( SUBSTR(cRecord, aFieldOffsets[nNameOffset, 2] + 1, nNameLength) ))
// Search
IF cExtractedName = cName
nFound += 1
hRecordData := { "recno" => i }
nOffset := 0
FOR j := 1 TO LEN(aFieldDescriptors)
hField := aFieldDescriptors[j]
cFieldValue := (SUBSTR(cRecord, nOffset + 2, hField["length"]))
hRecordData[hField["name"]] := cFieldValue
nOffset += hField["length"]
NEXT
AADD(aMatchingRecords, hRecordData)
ENDIF
NEXT
xbrowse(aMatchingRecords)
RETURN(aMatchingRecords)
FUNCTION WriteRecordsToFile(aRecords, cFilePath)
LOCAL nHandle := FCREATE(cFilePath)
LOCAL cLine
LOCAL hRecord
LOCAL cFieldName
LOCAL cValue
IF nHandle == -1
? "Konnte die Datei nicht erstellen."
RETURN NIL
ENDIF
FOR EACH hRecord IN aRecords
cLine := ""
FOR cFieldName := 1 TO LEN(hRecord)
IF HGetKeyAt(hRecord, cFieldName) != "recno"
cValue := hRecord[HGetKeyAt(hRecord, cFieldName)]
cLine += cValue + CHR(9) // Tab-separated
ENDIF
NEXT
cLine := RTRIM(cLine) + CRLF
FWRITE(nHandle, cLine)
NEXT
FCLOSE(nHandle)
? "Datei erfolgreich erstellt: ", cFilePath
RETURN NIL
// Funktion zum rechtsbündigen Auffüllen eines Strings auf eine bestimmte Länge
FUNCTION PadR(cText, nLength)
RETURN SUBSTR(cText + SPACE(nLength), 1, nLength)