【问题标题】:Direct file access in delphidelphi直接文件访问
【发布时间】:2009-02-06 11:11:07
【问题描述】:

我的应用程序打开文件进行转换并将数据保存到另一个文件..或者可能是同一个文件..文件大小发生变化,但我不知道它有多大或多小,直到我看到第一个文件中的数据..

目前,我将文件加载到动态数组中,然后在其中执行所有我需要做的事情,然后将其保存回来......这看起来不错,直到我进入测试阶段,在那里我发现在具有 128mb 内存的系统引起了一些问题……哈哈 这是我的代码..

procedure openfile(fname:string);
var
  myfile: file;
  filesizevalue:integer;
begin
  AssignFile(myfile,fname);
  filesizevalue := GetFileSize(fname);
  Reset(myFile, 1);
  SetLength(dataarray, filesizevalue);
  BlockRead(myFile, dataarray[0], filesizevalue);
  CloseFile(myfile);
end;

我需要的是直接文件访问以最小化内存使用..这就是我认为我需要的/ 这是我需要的,可以在delphi中完成吗

【问题讨论】:

    标签: delphi file-access


    【解决方案1】:

    我会考虑使用 TFileStream,也许是带缓冲的,但您需要真正展示您对数据的处理方式,因为很难确定最佳策略。正如 gabr 所说,一种选择是内存映射文件,其代码在他的链接中,但既然是我的代码,我也会在这里添加它!

    procedure TMyReader.InitialiseMapping(szFilename : string);
    var
    //  nError : DWORD;
        bGood : boolean;
    begin
        bGood := False;
        m_hFile := CreateFile(PChar(szFilename), GENERIC_READ, 0, nil, OPEN_EXISTING, 0, 0);
        if m_hFile <> INVALID_HANDLE_VALUE then
        begin
            m_hMap := CreateFileMapping(m_hFile, nil, PAGE_READONLY, 0, 0, nil);
            if m_hMap <> 0 then
            begin
                m_pMemory := MapViewOfFile(m_hMap, FILE_MAP_READ, 0, 0, 0);
                if m_pMemory <> nil then
                begin
                        htlArray := Pointer(Integer(m_pMemory) + m_dwDataPosition);
                        bGood := True;
                end
                else
                begin
     //                      nError := GetLastError;
                end;
           end;
        end;
        if not bGood then
            raise Exception.Create('Unable to map token file into memory');
    end;
    

    【讨论】:

      【解决方案2】:

      您还可以将部分文件直接映射到内存中。这绝对是最直接的方式。示例见What is the fastest way to Parse a line in Delphi

      【讨论】:

        【解决方案3】:

        如果问题允许,您可以使用BlockReadBlockWrite 读取输入文件的块,对其进行处理,然后将该块写入输出文件。像这样的:

          AssignFile(inFile,inFname); 
          AssignFile(outFile,outFname); 
          repeat      
            BlockRead(inFile, buff, SizeOf(buff), bytesRead);
            ProcessBuffer(buff);
            BlockWrite(outFile, buff, bytesRead, bytesWritten);
          until (bytesRead = 0) or (bytesWritten <> bytesRead);
        

        代码假定您在处理缓冲区时不会更改缓冲区的大小。如果文件大小发生变化,则应更改示例代码的最后两行。

        【讨论】:

          【解决方案4】:

          我更喜欢使用 tFileStream 进行这种处理。在此示例中,我假设有一个常量 ArraySize 设置为单个数组元素的大小。例如,如果您的“数组”是一个整数数组,那么它将被设置为:

          ArraySize := SizeOf( Integer );
          

          这会将 ArraySize 设置为 4。

          Function LoadPos(inFIlename:string;ArrayPos:Int64;var ArrayBuff) : boolean;
          var
            fs : tFileStream;
          begin
            result := false;
            fs := tFileStream.Create(inFilename,fmOpenRead);
            try
              // seek to the array position
              fs.Seek( ArrayPos * ArraySize, soFromBeginning);
              // load the element
              result := fs.Read( ArrayBuff, ArraySize ) = ArraySize;
            finally
              fs.free;
            end;
          end;
          

          这种方法的唯一问题是它只适用于固定大小的结构,可变长度的字符串需要不同的方法。

          【讨论】:

            【解决方案5】:

            我认为您不会获得“更直接”的文件访问权限。您是否使用文件中的所有数据?否则,您也许可以使用流并仅将所需的数据加载到内存中。但是,如果您使用所有数据,恕我直言,只有一种解决方案:分块读取文件。但这在很大程度上取决于您要应用的转换类型。如果转换不是本地的(因此组合的数据元素都在同一个块中),你就会遇到问题。

            【讨论】:

            • 他可以写入一个新文件并在转换后用新文件覆盖旧文件。
            • 是的,但是如何最大限度地减少 RAM 使用量,尤其是在全局转换的情况下?
            • 幸运的是,它是一个本地转换,除了最后 2 个和一点 k 之外,它甚至可以在字节流上完成,尽管我认为我更喜欢更大的块,否则读写开销将是 emense。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-12-13
            • 2014-08-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多