SetLength的替代品

时间:2012-04-02 18:52:32

标签: delphi dynamic

我遇到了SetLength命令的问题。

这基本上就是这个问题:

我尽可能地与WindowsAPI合作。我的目标是将一定大小设置为动态数组:

以下是一些需要理解的代码:

var
thefile : array of char;
// or thefile : array [0..9999] of char; // <---- not really a good way, works tho

FileHandle := CreateFileA(paramstr0, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
dwSize := GetFileSize(FileHandle,NIL);
SetFilePointer(FileHandle, 0, nil, FILE_BEGIN);
SetLength(TheFile, dwsize); // <--- I can't use this
ReadFile(FileHandle, thefile[1], dwSize , dwRead, nil);
CloseHandle (FileHandle);
CloseHandle(CreateFileA (sfile, 0, 0,NIL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL,0));
FileHandle := CreateFileA(sFile, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_ALWAYS, 0, 0);
WriteFile (FileHandle, thefile, dwSize, testCardinal, NIL);
CloseHandle (FileHandle);

如何替换SetLength?我还想了解Windows / Delphi如何在内存中分配数组等。感谢您的帮助

3 个答案:

答案 0 :(得分:8)

你的代码中有几个缺陷

  1. 使用CreateFile函数时,必须根据INVALID_HANDLE_VALUE检查返回的句柄。

  2. 您必须始终使用try ..来释放资源。

  3. 您正在尝试使用ReadFile函数使用索引1读取thefile缓冲区内的内容,这是错误的,您必须使用索引0,因为动态数组为零基于索引。

  4. 您正在使用此代码

  5.   

    CloseHandle(CreateFileA(sfile,0,0,NIL,CREATE_NEW,   FILE_ATTRIBUTE_NORMAL,0));

    要创建一个空文件,然后再使用CreateFile函数打开和写入文件,您可以一步完成相同的操作

      FileHandle := CreateFileA(sFile, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_NEW, 0, 0);
    

    尝试使用此代码的改进版本。

    顺便说一句,使用SetLengh没有任何问题,问题在第3点有解释。

    var
      thefile : array of AnsiChar;
    begin
    
      FileHandle := CreateFileA(paramstr0, GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);
      if FileHandle <> INVALID_HANDLE_VALUE then
      try
        dwSize := GetFileSize(FileHandle, nil);
        SetFilePointer(FileHandle, 0, nil, FILE_BEGIN);
        SetLength(TheFile, dwsize);
        ReadFile(FileHandle, thefile[0], dwSize , dwRead, nil);
      finally
        CloseHandle (FileHandle);
      end;
    
      FileHandle := CreateFileA(sFile, GENERIC_WRITE, FILE_SHARE_WRITE, nil, CREATE_NEW, 0, 0);
      if FileHandle <> INVALID_HANDLE_VALUE then
      try
        WriteFile (FileHandle, thefile[0], dwSize, testCardinal, nil);
      finally
        CloseHandle (FileHandle);
      end;
    
      SetLength(TheFile, 0);    
    end;
    

答案 1 :(得分:3)

SetLength()不是问题所在。

如果您真的喜欢Windows API,只需使用CopyFile()函数即可完成所有工作:

CopyFile(pointer(paramstr0),pointer(sFile));

使用几MB的临时固定缓冲区比一次读取所有文件要快。

您的代码将无法处理文件&gt; 2GB。您需要在代码中处理Int64大小和位置。

恕我直言,你最好在这里使用非Windows API来处理文件,但TFileStream是跨平台的。

SetLength()可能是此处唯一的好调用之一 - 但您应该编码theFile[0]pointer(theFile)^而不是theFile[1]

答案 2 :(得分:1)

您可以将GetMem / FreeMem与通用缓冲区一起使用,而不是使用动态数组。它与你在C中的表现方式更为相似。

无论如何设置与文件一样大的缓冲区通常不是最佳解决方案。通常使用固定大小的缓冲区并以块的形式读/写文件。即使有足够的可用内存,您也可能无法分配整个数组。数组内存需要是连续的,因此您需要一个与您需要分配的数组一样大或更大的空闲内存块。随着时间的推移,内存可能会碎片化,因此无法获得足够大的块。

即使您能够分配它,您也可以强制操作系统将一些内存从RAM中交换出来以分配它 - 从而减慢其他应用程序的速度。因此,应该考虑可用的RAM,性能和磁盘速度来设置缓冲区的大小。

Delphi如何分配内存取决于您正在使用的内存管理器。大多数人会使用Windows内存分配功能请求更大的块,并根据需要对它们进行子分配(为了加快内存管理,Windows并不像大多数OO应用程序那样有效地分配小内存块)。只有在包含不再使用的数据时,才能将块返回给操作系统。

为什么 char 的数组?在Delphi中,char不是8位类型。例如,它是Delphi 2007的8位,但从2009年开始是16位类型,因为它变成了Unicode字符(不像C)。使用字节类型(或更新版本中的UInt8),无论您使用何种版本的Delphi和目标平台,它都将保持8位类型。

如果您需要复制文件,TStream.CopyFrom()将在一次调用中完成您所需的操作(在您创建两个流之后)。

相关问题