Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/276.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何用PHP阅读FoxPro备忘录?_Php_Mysql_Foxpro_Dbf - Fatal编程技术网

如何用PHP阅读FoxPro备忘录?

如何用PHP阅读FoxPro备忘录?,php,mysql,foxpro,dbf,Php,Mysql,Foxpro,Dbf,我必须将.DBF和.FPT文件从Visual FoxPro转换为MySQL。现在,我的脚本适用于.DBF文件,它使用dbase_open和dbase_get_record_以及_名称打开并读取这些文件,然后执行MySQL INSERT命令 但是,这些.DBF文件的某些字段属于MEMO类型,因此存储在以.FPT结尾的单独文件中。如何读取此文件 我在中找到了此文件类型的规范,但我不知道如何使用PHP按字节读取此文件,我更喜欢更简单的解决方案 有什么想法吗?我认为PHP中不太可能有FoxPro库 您可

我必须将.DBF和.FPT文件从Visual FoxPro转换为MySQL。现在,我的脚本适用于.DBF文件,它使用dbase_open和dbase_get_record_以及_名称打开并读取这些文件,然后执行MySQL INSERT命令

但是,这些.DBF文件的某些字段属于MEMO类型,因此存储在以.FPT结尾的单独文件中。如何读取此文件

我在中找到了此文件类型的规范,但我不知道如何使用PHP按字节读取此文件,我更喜欢更简单的解决方案


有什么想法吗?

我认为PHP中不太可能有FoxPro库

您可能需要从头开始编写代码。有关字节读取,请参见


编辑:似乎有一个问题。您可以通过PDO和该连接器连接到FoxPro数据库。我不知道成功的机会有多大,需要做多少工作

我认为PHP中不太可能有FoxPro库

您可能需要从头开始编写代码。有关字节读取,请参见


编辑:似乎有一个问题。您可以通过PDO和该连接器连接到FoxPro数据库。我不知道成功的机会有多大,需要做多少工作

虽然不是PHP,但VFP是基于1的引用,我认为PHP是基于零的引用,因此您必须进行相应的解密和调整,但这是可行的,希望您能够 完成后发布此部分的版本

VFP中的FILETOSTR将打开一个文件,并将整个内容读入 作为字符串的单个内存变量-所有转义键、高字节字符等都完好无损。你可能需要依赖FOPEN、FSEEK、FCLOSE等

MemoTest.FPT是我的示例备忘录表/文件 fpt1=FILETOSTR MEMOTEST.FPT

首先,您必须检测创建文件时使用的备注块大小。通常这是64个字节,但根据您在文章中的链接

收割台位置6-7标识尺寸VFP,位置7和8。第一个字节是高阶字节

nBlockSize = ASC( SUBSTR( fpt1, 7, 1 )) * 256 + ASC( SUBSTR( fpt1, 8, 1 ))
现在,看看你的个人记录。只要DBF结构中有memo字段,并且每个记录结构中可以有多个字段,那么就有4个字节。在记录字段中,它标识备忘录文件中存储内容的块

MemoBytes=标识字段位置的4个字节。这些将存储为0-255之间的ASCII码。此字段存储时,第一个字节为低位,第四个字节为256^3=16777216。使用过的第一个块将根据memo.fpt文件规范以512的位置偏移开始,该文件规范的头占据位置0-511

因此,如果您的第一个备注字段的内容为8000,其中8是实际的0x08,而不是数字8,即0x38,并且零是0x00

YourMemoField=8000实际上使用ascii,但为了可读性,显示十六进制预期值

First Byte is ASCII value  * 1   ( 256 ^ 0 )
Second Byte is ASCII value * 256   (256 ^ 1)
Third Byte is ASCII value * 65536   (256 ^ 2)
Fourth Byte is ASCII value * 16777216 (256 ^ 3)

nMemoBlock =  byte1 + ( byte2 * 256 ) + ( byte3 * 65536 ) + ( byte4 * 16777216 )
现在,你需要去看电影

FSEEK( handle, nMemoBlock * nBlockSize +1 )
对于要查找的块的第一个字节。这将指向块标题。在这种情况下,根据规范,前4个字节标识块签名,后4个字节是内容的长度。对于这两个,字节首先以高位字节存储

从您的FSEEK来看,它与上面的nMemoBlock相反,具有高字节。这里的字节1-4来自您的FSEEK位置

nSignature = ( byte1 * 16777216 ) + ( byte2 * 65536 ) + ( byte3 * 256 ) + byte4

nMemoLength = ( byte5 * 16777216 ) + ( byte6 * 65536 ) + ( byte7 * 256 ) + byte8
现在,FSEEK到第9个字节,即刚才读取的签名和备忘录长度头的8个字节之后的数据的第一个实际字符。这是数据的开始

现在,阅读剩下的内容

FSEEK() +9 characters to new position

cFinalMemoData = FREAD( handle, nMemoLength )
我知道这不是完美的,PHP脚本也不是完美的,但关于如何存储东西的伪代码已经足够了,希望能让您顺利上路


同样,在调试过程中,请考虑确保0或1偏移量。为了帮助简化和测试这一点,我创建了一个包含2个字段的简单.DBF。。。一个字符字段和一个备注字段,添加了一些记录和一些基本内容,以确认所有内容、位置等。

虽然不是PHP,但VFP是基于1的引用,我认为PHP是基于零的引用,因此您必须进行相应的解密和调整,但这很有效,希望您能够 完成后发布此部分的版本

VFP中的FILETOSTR将打开一个文件,并将整个内容读入 作为字符串的单个内存变量-所有转义键、高字节字符等都完好无损。你可能需要依赖FOPEN、FSEEK、FCLOSE等

MemoTest.FPT是我的示例备忘录表/文件 fpt1=FILETOSTR MEMOTEST.FPT

首先,您必须检测创建文件时使用的备注块大小。通常这是64个字节,但根据您在文章中的链接

收割台位置6-7标识尺寸VFP,位置7和8。第一个字节是高阶字节

nBlockSize = ASC( SUBSTR( fpt1, 7, 1 )) * 256 + ASC( SUBSTR( fpt1, 8, 1 ))
现在,看看你的个人记录。DBF结构中的任何位置都有“备注”字段 每个记录结构可以有多个字节,其中有4个字节。在记录字段中,它标识备忘录文件中存储内容的块

MemoBytes=标识字段位置的4个字节。这些将存储为0-255之间的ASCII码。此字段存储时,第一个字节为低位,第四个字节为256^3=16777216。使用过的第一个块将根据memo.fpt文件规范以512的位置偏移开始,该文件规范的头占据位置0-511

因此,如果您的第一个备注字段的内容为8000,其中8是实际的0x08,而不是数字8,即0x38,并且零是0x00

YourMemoField=8000实际上使用ascii,但为了可读性,显示十六进制预期值

First Byte is ASCII value  * 1   ( 256 ^ 0 )
Second Byte is ASCII value * 256   (256 ^ 1)
Third Byte is ASCII value * 65536   (256 ^ 2)
Fourth Byte is ASCII value * 16777216 (256 ^ 3)

nMemoBlock =  byte1 + ( byte2 * 256 ) + ( byte3 * 65536 ) + ( byte4 * 16777216 )
现在,你需要去看电影

FSEEK( handle, nMemoBlock * nBlockSize +1 )
对于要查找的块的第一个字节。这将指向块标题。在这种情况下,根据规范,前4个字节标识块签名,后4个字节是内容的长度。对于这两个,字节首先以高位字节存储

从您的FSEEK来看,它与上面的nMemoBlock相反,具有高字节。这里的字节1-4来自您的FSEEK位置

nSignature = ( byte1 * 16777216 ) + ( byte2 * 65536 ) + ( byte3 * 256 ) + byte4

nMemoLength = ( byte5 * 16777216 ) + ( byte6 * 65536 ) + ( byte7 * 256 ) + byte8
现在,FSEEK到第9个字节,即刚才读取的签名和备忘录长度头的8个字节之后的数据的第一个实际字符。这是数据的开始

现在,阅读剩下的内容

FSEEK() +9 characters to new position

cFinalMemoData = FREAD( handle, nMemoLength )
我知道这不是完美的,PHP脚本也不是完美的,但关于如何存储东西的伪代码已经足够了,希望能让您顺利上路


同样,在调试过程中,请考虑确保0或1偏移量。为了帮助简化和测试这一点,我创建了一个包含2个字段的简单.DBF。。。一个字符字段和一个备注字段,添加了一些记录和一些基本内容以确认所有内容、位置等。

好的,我仔细研究了DBF和FPT文件结构的MSDN规范,结果是一个漂亮的PHP类,它可以同时打开DBF和可选的FPT备注文件。这个类将给你一条又一条记录,从而从备忘录文件中获取任何备忘录(如果打开的话)

class Prodigy_DBF {
    private $Filename, $DB_Type, $DB_Update, $DB_Records, $DB_FirstData, $DB_RecordLength, $DB_Flags, $DB_CodePageMark, $DB_Fields, $FileHandle, $FileOpened;
    private $Memo_Handle, $Memo_Opened, $Memo_BlockSize;

    private function Initialize() {

        if($this->FileOpened) {
            fclose($this->FileHandle);
        }

        if($this->Memo_Opened) {
            fclose($this->Memo_Handle);
        }

        $this->FileOpened = false;
        $this->FileHandle = NULL;
        $this->Filename = NULL;
        $this->DB_Type = NULL;
        $this->DB_Update = NULL;
        $this->DB_Records = NULL;
        $this->DB_FirstData = NULL;
        $this->DB_RecordLength = NULL;
        $this->DB_CodePageMark = NULL;
        $this->DB_Flags = NULL;
        $this->DB_Fields = array();

        $this->Memo_Handle = NULL;
        $this->Memo_Opened = false;
        $this->Memo_BlockSize = NULL;
    }

    public function __construct($Filename, $MemoFilename = NULL) {
        $this->Prodigy_DBF($Filename, $MemoFilename);
    }

    public function Prodigy_DBF($Filename, $MemoFilename = NULL) {
        $this->Initialize();
        $this->OpenDatabase($Filename, $MemoFilename);
    }

    public function OpenDatabase($Filename, $MemoFilename = NULL) {
        $Return = false;
        $this->Initialize();

        $this->FileHandle = fopen($Filename, "r");
        if($this->FileHandle) {
            // DB Open, reading headers
            $this->DB_Type = dechex(ord(fread($this->FileHandle, 1)));
            $LUPD = fread($this->FileHandle, 3);
            $this->DB_Update = ord($LUPD[0])."/".ord($LUPD[1])."/".ord($LUPD[2]);
            $Rec = unpack("V", fread($this->FileHandle, 4));
            $this->DB_Records = $Rec[1];
            $Pos = fread($this->FileHandle, 2);
            $this->DB_FirstData = (ord($Pos[0]) + ord($Pos[1]) * 256);
            $Len = fread($this->FileHandle, 2);
            $this->DB_RecordLength = (ord($Len[0]) + ord($Len[1]) * 256);
            fseek($this->FileHandle, 28); // Ignoring "reserved" bytes, jumping to table flags
            $this->DB_Flags = dechex(ord(fread($this->FileHandle, 1)));
            $this->DB_CodePageMark = ord(fread($this->FileHandle, 1));
            fseek($this->FileHandle, 2, SEEK_CUR);    // Ignoring next 2 "reserved" bytes

            // Now reading field captions and attributes
            while(!feof($this->FileHandle)) {

                // Checking for end of header
                if(ord(fread($this->FileHandle, 1)) == 13) {
                    break;  // End of header!
                } else {
                    // Go back
                    fseek($this->FileHandle, -1, SEEK_CUR);
                }

                $Field["Name"] = trim(fread($this->FileHandle, 11));
                $Field["Type"] = fread($this->FileHandle, 1);
                fseek($this->FileHandle, 4, SEEK_CUR);  // Skipping attribute "displacement"
                $Field["Size"] = ord(fread($this->FileHandle, 1));
                fseek($this->FileHandle, 15, SEEK_CUR); // Skipping any remaining attributes
                $this->DB_Fields[] = $Field;
            }

            // Setting file pointer to the first record
            fseek($this->FileHandle, $this->DB_FirstData);

            $this->FileOpened = true;

            // Open memo file, if exists
            if(!empty($MemoFilename) and file_exists($MemoFilename) and preg_match("%^(.+).fpt$%i", $MemoFilename)) {
                $this->Memo_Handle = fopen($MemoFilename, "r");
                if($this->Memo_Handle) {
                    $this->Memo_Opened = true;

                    // Getting block size
                    fseek($this->Memo_Handle, 6);
                    $Data = unpack("n", fread($this->Memo_Handle, 2));
                    $this->Memo_BlockSize = $Data[1];
                }
            }
        }

        return $Return;
    }

    public function GetNextRecord($FieldCaptions = false) {
        $Return = NULL;
        $Record = array();

        if(!$this->FileOpened) {
            $Return = false;
        } elseif(feof($this->FileHandle)) {
            $Return = NULL;
        } else {
            // File open and not EOF
            fseek($this->FileHandle, 1, SEEK_CUR);  // Ignoring DELETE flag
            foreach($this->DB_Fields as $Field) {
                $RawData = fread($this->FileHandle, $Field["Size"]);
                // Checking for memo reference
                if($Field["Type"] == "M" and $Field["Size"] == 4 and !empty($RawData)) {
                    // Binary Memo reference
                    $Memo_BO = unpack("V", $RawData);
                    if($this->Memo_Opened and $Memo_BO != 0) {
                        fseek($this->Memo_Handle, $Memo_BO[1] * $this->Memo_BlockSize);
                        $Type = unpack("N", fread($this->Memo_Handle, 4));
                        if($Type[1] == "1") {
                            $Len = unpack("N", fread($this->Memo_Handle, 4));
                            $Value = trim(fread($this->Memo_Handle, $Len[1]));
                        } else {
                            // Pictures will not be shown
                            $Value = "{BINARY_PICTURE}";
                        }
                    } else {
                        $Value = "{NO_MEMO_FILE_OPEN}";
                    }
                } else {
                    $Value = trim($RawData);
                }

                if($FieldCaptions) {
                    $Record[$Field["Name"]] = $Value;
                } else {
                    $Record[] = $Value;
                }
            }

            $Return = $Record;
        }

        return $Return;
    }

    function __destruct() {
        // Cleanly close any open files before destruction
        $this->Initialize();
    }
}
该类可以这样使用:

    $Test = new Prodigy_DBF("customer.DBF", "customer.FPT");
    while(($Record = $Test->GetNextRecord(true)) and !empty($Record)) {
        print_r($Record);
    }
这可能不是一门全能的完美课程,但它对我很有用。可以随意使用这段代码,但请注意,该类非常宽容——它不关心fread和fseek是否返回true或其他任何东西——因此您可能希望在使用之前对其进行一些改进


还请注意,有许多私有变量,如记录数、记录大小等,目前尚未使用。

好的,我仔细研究了DBF和FPT文件结构的MSDN规范,结果是一个漂亮的PHP类,它可以同时打开DBF和可选的FPT备忘录文件。这个类将给你一条又一条记录,从而从备忘录文件中获取任何备忘录(如果打开的话)

class Prodigy_DBF {
    private $Filename, $DB_Type, $DB_Update, $DB_Records, $DB_FirstData, $DB_RecordLength, $DB_Flags, $DB_CodePageMark, $DB_Fields, $FileHandle, $FileOpened;
    private $Memo_Handle, $Memo_Opened, $Memo_BlockSize;

    private function Initialize() {

        if($this->FileOpened) {
            fclose($this->FileHandle);
        }

        if($this->Memo_Opened) {
            fclose($this->Memo_Handle);
        }

        $this->FileOpened = false;
        $this->FileHandle = NULL;
        $this->Filename = NULL;
        $this->DB_Type = NULL;
        $this->DB_Update = NULL;
        $this->DB_Records = NULL;
        $this->DB_FirstData = NULL;
        $this->DB_RecordLength = NULL;
        $this->DB_CodePageMark = NULL;
        $this->DB_Flags = NULL;
        $this->DB_Fields = array();

        $this->Memo_Handle = NULL;
        $this->Memo_Opened = false;
        $this->Memo_BlockSize = NULL;
    }

    public function __construct($Filename, $MemoFilename = NULL) {
        $this->Prodigy_DBF($Filename, $MemoFilename);
    }

    public function Prodigy_DBF($Filename, $MemoFilename = NULL) {
        $this->Initialize();
        $this->OpenDatabase($Filename, $MemoFilename);
    }

    public function OpenDatabase($Filename, $MemoFilename = NULL) {
        $Return = false;
        $this->Initialize();

        $this->FileHandle = fopen($Filename, "r");
        if($this->FileHandle) {
            // DB Open, reading headers
            $this->DB_Type = dechex(ord(fread($this->FileHandle, 1)));
            $LUPD = fread($this->FileHandle, 3);
            $this->DB_Update = ord($LUPD[0])."/".ord($LUPD[1])."/".ord($LUPD[2]);
            $Rec = unpack("V", fread($this->FileHandle, 4));
            $this->DB_Records = $Rec[1];
            $Pos = fread($this->FileHandle, 2);
            $this->DB_FirstData = (ord($Pos[0]) + ord($Pos[1]) * 256);
            $Len = fread($this->FileHandle, 2);
            $this->DB_RecordLength = (ord($Len[0]) + ord($Len[1]) * 256);
            fseek($this->FileHandle, 28); // Ignoring "reserved" bytes, jumping to table flags
            $this->DB_Flags = dechex(ord(fread($this->FileHandle, 1)));
            $this->DB_CodePageMark = ord(fread($this->FileHandle, 1));
            fseek($this->FileHandle, 2, SEEK_CUR);    // Ignoring next 2 "reserved" bytes

            // Now reading field captions and attributes
            while(!feof($this->FileHandle)) {

                // Checking for end of header
                if(ord(fread($this->FileHandle, 1)) == 13) {
                    break;  // End of header!
                } else {
                    // Go back
                    fseek($this->FileHandle, -1, SEEK_CUR);
                }

                $Field["Name"] = trim(fread($this->FileHandle, 11));
                $Field["Type"] = fread($this->FileHandle, 1);
                fseek($this->FileHandle, 4, SEEK_CUR);  // Skipping attribute "displacement"
                $Field["Size"] = ord(fread($this->FileHandle, 1));
                fseek($this->FileHandle, 15, SEEK_CUR); // Skipping any remaining attributes
                $this->DB_Fields[] = $Field;
            }

            // Setting file pointer to the first record
            fseek($this->FileHandle, $this->DB_FirstData);

            $this->FileOpened = true;

            // Open memo file, if exists
            if(!empty($MemoFilename) and file_exists($MemoFilename) and preg_match("%^(.+).fpt$%i", $MemoFilename)) {
                $this->Memo_Handle = fopen($MemoFilename, "r");
                if($this->Memo_Handle) {
                    $this->Memo_Opened = true;

                    // Getting block size
                    fseek($this->Memo_Handle, 6);
                    $Data = unpack("n", fread($this->Memo_Handle, 2));
                    $this->Memo_BlockSize = $Data[1];
                }
            }
        }

        return $Return;
    }

    public function GetNextRecord($FieldCaptions = false) {
        $Return = NULL;
        $Record = array();

        if(!$this->FileOpened) {
            $Return = false;
        } elseif(feof($this->FileHandle)) {
            $Return = NULL;
        } else {
            // File open and not EOF
            fseek($this->FileHandle, 1, SEEK_CUR);  // Ignoring DELETE flag
            foreach($this->DB_Fields as $Field) {
                $RawData = fread($this->FileHandle, $Field["Size"]);
                // Checking for memo reference
                if($Field["Type"] == "M" and $Field["Size"] == 4 and !empty($RawData)) {
                    // Binary Memo reference
                    $Memo_BO = unpack("V", $RawData);
                    if($this->Memo_Opened and $Memo_BO != 0) {
                        fseek($this->Memo_Handle, $Memo_BO[1] * $this->Memo_BlockSize);
                        $Type = unpack("N", fread($this->Memo_Handle, 4));
                        if($Type[1] == "1") {
                            $Len = unpack("N", fread($this->Memo_Handle, 4));
                            $Value = trim(fread($this->Memo_Handle, $Len[1]));
                        } else {
                            // Pictures will not be shown
                            $Value = "{BINARY_PICTURE}";
                        }
                    } else {
                        $Value = "{NO_MEMO_FILE_OPEN}";
                    }
                } else {
                    $Value = trim($RawData);
                }

                if($FieldCaptions) {
                    $Record[$Field["Name"]] = $Value;
                } else {
                    $Record[] = $Value;
                }
            }

            $Return = $Record;
        }

        return $Return;
    }

    function __destruct() {
        // Cleanly close any open files before destruction
        $this->Initialize();
    }
}
该类可以这样使用:

    $Test = new Prodigy_DBF("customer.DBF", "customer.FPT");
    while(($Record = $Test->GetNextRecord(true)) and !empty($Record)) {
        print_r($Record);
    }
这可能不是一门全能的完美课程,但它对我很有用。可以随意使用这段代码,但请注意,该类非常宽容——它不关心fread和fseek是否返回true或其他任何东西——因此您可能希望在使用之前对其进行一些改进


还请注意,有许多私有变量,如记录数、记录大小等,目前未使用。

FPT文件包含备忘录数据。在DBF中,有类型为memo的列,该列中的信息是指向FPT文件中条目的指针

如果要查询表中的数据,只需引用备注列即可获得数据。您不需要单独解析FPT文件中的数据。OLE DB驱动程序或ODBC驱动程序(如果您的文件是VFP 6或更早版本)应该只提供此信息

有一个工具可以自动将您的Visual FoxPro数据迁移到MySQL。您可能想查看一下,看看是否可以节省一些时间:

并搜索Stru2MySQL_2中用于迁移数据结构的工具和VFP2MySQL数据上传程序中用于帮助迁移的工具


Rick Schummer VFP MVP

FPT文件包含备忘录数据。在DBF中,有类型为memo的列,该列中的信息是指向FPT文件中条目的指针

如果要查询表中的数据,只需引用备注列即可获得数据。您不需要单独解析FPT文件中的数据。OLE DB驱动程序或ODBC驱动程序(如果您的文件是VFP 6或更早版本)应该只提供此信息

有一个工具可以自动将您的Visual FoxPro数据迁移到MySQL。您可能想查看一下,看看是否可以节省一些时间:

并搜索Stru2MySQL_2中用于迁移数据结构的工具和VFP2MySQL数据上传程序中用于帮助迁移的工具


Rick Schummer VFP MVP

您可能还需要检查
PHP数据库库。它们可以很好地处理DBF文件。

您可能还需要检查PHP数据库库。它们可以很好地处理DBF文件。

我替换了以下代码:

<?
class Prodigy_DBF {
    private $Filename, $DB_Type, $DB_Update, $DB_Records, $DB_FirstData, $DB_RecordLength, $DB_Flags, $DB_CodePageMark, $DB_Fields, $FileHandle, $FileOpened;
    private $Memo_Handle, $Memo_Opened, $Memo_BlockSize;

    private function Initialize() {

        if($this->FileOpened) {
            fclose($this->FileHandle);
        }

        if($this->Memo_Opened) {
            fclose($this->Memo_Handle);
        }

        $this->FileOpened = false;
        $this->FileHandle = NULL;
        $this->Filename = NULL;
        $this->DB_Type = NULL;
        $this->DB_Update = NULL;
        $this->DB_Records = NULL;
        $this->DB_FirstData = NULL;
        $this->DB_RecordLength = NULL;
        $this->DB_CodePageMark = NULL;
        $this->DB_Flags = NULL;
        $this->DB_Fields = array();

        $this->Memo_Handle = NULL;
        $this->Memo_Opened = false;
        $this->Memo_BlockSize = NULL;
    }

    public function __construct($Filename, $MemoFilename = NULL) {
        $this->Prodigy_DBF($Filename, $MemoFilename);
    }

    public function Prodigy_DBF($Filename, $MemoFilename = NULL) {
        $this->Initialize();
        $this->OpenDatabase($Filename, $MemoFilename);
    }

    public function OpenDatabase($Filename, $MemoFilename = NULL) {
        $Return = false;
        $this->Initialize();

        $this->FileHandle = fopen($Filename, "r");
        if($this->FileHandle) {
            // DB Open, reading headers
            $this->DB_Type = dechex(ord(fread($this->FileHandle, 1)));
            $LUPD = fread($this->FileHandle, 3);
            $this->DB_Update = ord($LUPD[0])."/".ord($LUPD[1])."/".ord($LUPD[2]);
            $Rec = unpack("V", fread($this->FileHandle, 4));
            $this->DB_Records = $Rec[1];
            $Pos = fread($this->FileHandle, 2);
            $this->DB_FirstData = (ord($Pos[0]) + ord($Pos[1]) * 256);
            $Len = fread($this->FileHandle, 2);
            $this->DB_RecordLength = (ord($Len[0]) + ord($Len[1]) * 256);
            fseek($this->FileHandle, 28); // Ignoring "reserved" bytes, jumping to table flags
            $this->DB_Flags = dechex(ord(fread($this->FileHandle, 1)));
            $this->DB_CodePageMark = ord(fread($this->FileHandle, 1));
            fseek($this->FileHandle, 2, SEEK_CUR);    // Ignoring next 2 "reserved" bytes

            // Now reading field captions and attributes
            while(!feof($this->FileHandle)) {

                // Checking for end of header
                if(ord(fread($this->FileHandle, 1)) == 13) {
                    break;  // End of header!
                } else {
                    // Go back
                    fseek($this->FileHandle, -1, SEEK_CUR);
                }

                $Field["Name"] = trim(fread($this->FileHandle, 11));
                $Field["Type"] = fread($this->FileHandle, 1);
                fseek($this->FileHandle, 4, SEEK_CUR);  // Skipping attribute "displacement"
                $Field["Size"] = ord(fread($this->FileHandle, 1));
                fseek($this->FileHandle, 15, SEEK_CUR); // Skipping any remaining attributes
                $this->DB_Fields[] = $Field;
            }

            // Setting file pointer to the first record
            fseek($this->FileHandle, $this->DB_FirstData);

            $this->FileOpened = true;

            // Open memo file, if exists
            if(!empty($MemoFilename) and file_exists($MemoFilename) and preg_match("%^(.+).fpt$%i", $MemoFilename)) {
                $this->Memo_Handle = fopen($MemoFilename, "r");
                if($this->Memo_Handle) {
                    $this->Memo_Opened = true;

                    // Getting block size
                    fseek($this->Memo_Handle, 6);
                    $Data = unpack("n", fread($this->Memo_Handle, 2));
                    $this->Memo_BlockSize = $Data[1];
                }
            }
        }

        return $Return;
    }

    public function GetNextRecord($FieldCaptions = false) {
        $Return = NULL;
        $Record = array();

        if(!$this->FileOpened) {
            $Return = false;
        } elseif(feof($this->FileHandle)) {
            $Return = NULL;
        } else {
            // File open and not EOF
            fseek($this->FileHandle, 1, SEEK_CUR);  // Ignoring DELETE flag
            foreach($this->DB_Fields as $Field) {
                $RawData = fread($this->FileHandle, $Field["Size"]);
                // Checking for memo reference
                if($Field["Type"] == "M" and $Field["Size"] == 4 and !empty($RawData)) {
                    // Binary Memo reference
                    $Memo_BO = unpack("V", $RawData);
                    if($this->Memo_Opened and $Memo_BO != 0) {
                        fseek($this->Memo_Handle, $Memo_BO[1] * $this->Memo_BlockSize);
                        $Type = unpack("N", fread($this->Memo_Handle, 4));
                        if($Type[1] == "1") {
                            $Len = unpack("N", fread($this->Memo_Handle, 4));
                            $Value = trim(fread($this->Memo_Handle, $Len[1]));
                        } else {
                            // Pictures will not be shown
                            $Value = "{BINARY_PICTURE}";
                    }
                } else {
                    $Value = "{NO_MEMO_FILE_OPEN}";
                }
            } else {
                if($Field["Type"] == "M"){
                    if(trim($RawData) > 0)   {
                        fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+8);
                        $Value = trim(fread($this->Memo_Handle, $this->Memo_BlockSize));
                    }
                }else{
                    $Value = trim($RawData);
                }
            }

            if($FieldCaptions) {
                $Record[$Field["Name"]] = $Value;
            } else {
                $Record[] = $Value;
            }
        }

            $Return = $Record;
        }

        return $Return;
    }

    function __destruct() {
        // Cleanly close any open files before destruction
        $this->Initialize();
    }
}
?>
fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+8);
$Value = trim(fread($this->Memo_Handle, $this->Memo_BlockSize));
fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+4);
$Len  = unpack("N", fread($this->Memo_Handle, 4));
$Value = trim(fread($this->Memo_Handle, $Len[1] ));
使用此代码:

fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+8);
$Value = trim(fread($this->Memo_Handle, $this->Memo_BlockSize));
fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+4);
$Len  = unpack("N", fread($this->Memo_Handle, 4));
$Value = trim(fread($this->Memo_Handle, $Len[1] ));
这对我有帮助

我替换了以下代码:

fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+8);
$Value = trim(fread($this->Memo_Handle, $this->Memo_BlockSize));
fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+4);
$Len  = unpack("N", fread($this->Memo_Handle, 4));
$Value = trim(fread($this->Memo_Handle, $Len[1] ));
使用此代码:

fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+8);
$Value = trim(fread($this->Memo_Handle, $this->Memo_BlockSize));
fseek($this->Memo_Handle, (trim($RawData) * $this->Memo_BlockSize)+4);
$Len  = unpack("N", fread($this->Memo_Handle, 4));
$Value = trim(fread($this->Memo_Handle, $Len[1] ));

这对我很有帮助

谢谢你的快速回答。现在我正试着用fopen和fread读取文件。我在这里面临的唯一问题是,我不知道如何将字节转换为整数。我如何将字节存储在PHP变量中?好的,我做了一些研究,发现了解包函数,它可以将原始数据转换为几种整数或其他格式。如果我能解决这个问题,我会告诉你的。否则,欢迎您提出进一步建议:谢谢您的快速回答。现在我正试着用fopen和fread读取文件。我在这里面临的唯一问题是,我不知道如何将字节转换为整数。我如何将字节存储在PHP变量中?好的,我做了一些研究,发现了解包函数,它可以将原始数据转换为几种整数或其他格式。如果我能解决这个问题,我会告诉你的。否则-欢迎进一步建议:感谢您的详细解释。我正在开发的代码大致如您所述。然而,数据似乎有问题,因为一些记录的字段比其他记录少,这意味着我无法判断记录何时开始和何时结束。你说过DBF会在FPT中识别块,你有更多的信息吗?我使用的是PHP的数据库函数,我认为它们不会返回二进制数据。。。DBF中的备注字段为空。如果为空,则不会有与该条目相关联的备注内容。但是,为备注字段分配的字段字节始终为4字节。如果它有一个值,它将是解析,正如我所描述的指向.FPT文件中的块。感谢您的详细解释。我正在开发的代码大致如您所述。然而,数据似乎有问题,因为一些记录的字段比其他记录少,这意味着我无法判断记录何时开始和何时结束。你说过DBF会在FPT中识别块,你有更多的信息吗?我使用的是PHP的数据库函数,我认为它们不会返回二进制数据。。。DBF中的备注字段为空。如果为空,则不会有与该条目相关联的备注内容。但是,为备注字段分配的字段字节始终为4字节。如果它有一个值,它将是解析,正如我所描述的,指向.FPT文件中的块。是的,您可以使用dbase函数轻松地读取它们,但我发现这些函数不显示备忘录字段中的数据,这些字段很可能存储在单独的文件中。这就是为什么我编写了自己的类,它可以自动读取相应的备忘录文件。是的,您可以使用dbase函数轻松地读取它们,但我发现这些函数不显示备忘录字段中的数据,这些字段很可能存储在单独的文件中。这就是为什么我写了我自己的类,可以自动读取相应的备忘录文件。你解决你的问题了吗?如果是,您修改了哪些行?是的..解决了我的问题。。。。。。。更改此$Value=trim$RawData;到下面。。。这里我添加了:如果$Field[Type]==M{iftrim$RawData>0{fseek$this->Memo_句柄,trim$RawData*$this->Memo_BlockSize+8;$Value=trimfrad$this->Memo_句柄,$this->Memo_BlockSize;}否则{$Value=trim$RawData;}rite做对了吗?但是我解决了我的问题你解决了你的问题吗?如果是,您修改了哪些行?是的..解决了我的问题。。。。。。。更改此$Value=trim$RawData;到下面。。。这里我添加了:如果$Field[Type]==M{iftrim$RawData>0{fseek$this->Memo_句柄,trim$RawData*$this->Memo_BlockSize+8;$Value=trimfrad$this->Memo_句柄,$this->Memo_BlockSize;}否则{$Value=trim$RawData;}rite做对了吗?但是我解决了我的问题。这是上面这个类的一个编辑版本,它也支持dBase 7级DBF格式。下面是上述类的编辑版本,它还支持dBase级别7 DBF格式。