簡體   English   中英

在Delphi 7中處理海量文本文件數據的最佳解決方案

[英]The best solution to process the huge text file data in Delphi 7

我有這樣的文本文件:

"01","AAA","AAAAA" 
"02","BBB","BBBBB","BBBBBBBB" 
"03","CCC" 
"04","DDD","DDDDD"

我想將此文本文件數據加載到sybase db中的temp表中。 因此,我需要構建一個程序來逐行讀取該文本文件直到eof。 如果文本文件較小,則逐行讀取的過程會很快。 但是,如果文本文件太大(可能超過500M),則逐行讀取的過程太慢。 我認為逐行讀取方法不適合大型文本文件。 因此,需要找到其他解決方案以將文本文件數據加載到db中,而不是逐行讀取文本文件。 有什么建議嗎? 示例代碼:

var
  myFile : TextFile;
  text   : string;

begin
  // Try to open the Test.txt file for writing to
  AssignFile(myFile, 'Test.txt');

  // Display the file contents
  while not Eof(myFile) do
  begin
    ReadLn(myFile, text);
    TempTable.append;
    TempTable.FieldByName('Field1').asstring=Copy(text,2,2);
    TempTable.FieldByName('Field2').asstring=Copy(text,7,3);
    TempTable.FieldByName('Field3').asstring=Copy(text,13,5);
    TempTable.FieldByName('Field4').asstring=Copy(text,21,8);
    TempTable.post;
  end;

  // Close the file for the last time
  CloseFile(myFile);
end;

文本文件通常具有非常小的緩沖區。 考慮使用SetTextBuf函數來提高性能。

var
  myFile : TextFile;
  text   : string;
  myFileBuffer: Array[1..32768] of byte;
begin
// Try to open the Test.txt file for writing to
  AssignFile(myFile, 'Test.txt');
  SetTextBuf(MyFile, myFileBuffer);
  Reset(MyFile);

// Display the file contents
  while not Eof(myFile) do
    begin
      ReadLn(myFile, text);
    end;

// Close the file for the last time
  CloseFile(myFile);
end;

一些一般性提示:

  • 確保您的TempTable在內存中,或者使用快速的數據庫引擎-查看SQlite3或其他方式(例如FireBird嵌入式,NexusDB或ElevateDB)作為可能的數據庫替代方案;
  • 如果您不使用TTable而是真正的數據庫,請確保將插入嵌套在Transaction中
  • 對於真正的數據庫,請檢查您是否不能使用ArrayDML功能,該功能可以在遠程數據庫(如Sybase)中插入所需的大量數據,速度更快。 例如,Array DML是通過FireDAC AFAIK 處理的
  • FieldByName('...')方法非常慢:使用locals TField變量代替;
  • 使用TextFile時,分配更大的臨時緩沖區;
  • 如果您正在使用Delphi(2009+)的較新Unicode版本,則使用TextFile不是最佳選擇。

因此,您的代碼可能是:

var
  myFile : TextFile;
  myFileBuffer: array[word] of byte;
  text   : string;
  Field1, Field2, Field3, Field4: TField;
begin

  // Set Field* local variables for speed within the main loop
  Field1 := TempTable.FieldByName('Field1');
  Field2 := TempTable.FieldByName('Field2');
  Field3 := TempTable.FieldByName('Field3');
  Field4 := TempTable.FieldByName('Field4');

  // Try to open the Test.txt file for writing to
  AssignFile(myFile, 'Test.txt');
  SetTextBuf(myFile, myFileBuffer); // use 64 KB read buffer

  // Display the file contents
  while not Eof(myFile) do
  begin
    ReadLn(myFile, text);
    TempTable.append;
    Field1.asInteger := StrToInt(Copy(text,2,2));
    Field2.asString := Copy(text,7,3);
    Field3.asString := Copy(text,13,5);
    Field4.asString := Copy(text,21,8);
    TempTable.post;
  end;

  // Close the file for the last time
  CloseFile(myFile);
end;

使用嵌入式引擎可以實現非常高的速度,幾乎沒有大小限制,但是可以存儲。 例如,查看我們可以在ORM中向SQLite3數據庫添加內容的速度 :在數據庫文件中,包括所有ORM編組,每秒大約130,000 / 150,000行。 我還發現, SQLite3生成的數據庫文件比替代文件小得多。 如果要快速檢索任何字段,請不要忘了在數據庫中定義INDEX ,如果可能的話在插入行數據之后(以提高速度)。 對於SQLite3 ,我想已經有一個ID/RowID整數主鍵可用,它映射了您的第一個數據字段。 ID/RowID整數主鍵已被SQLite3索引。 順便說一下,我們的ORM現在支持FireDAC / AnyDAC及其高級Array DML功能

除了已經說過的內容,我還將避免使用任何TTable組件。 使用TQuery類型的組件會更好(取決於您使用的訪問層)。 像這樣的東西:

qryImport.SQL := 'Insert Into MyTable Values (:Field1, :Field2, :Field3, :Field4);';

Procedure ImportRecord(Const pField1, pField2, pField3, pField4 : String);
Begin
  qryImport.Close;
  qryImport.Params[0].AsString := pField1;      
  qryImport.Params[1].AsString := pField2;`
  qryImport.Params[2].AsString := pField3;
  qryImport.Params[3].AsString := pField4;
  qryImport.ExecSQL;
End;

希望這可以幫助。

另一種方法是使用內存映射文件(您可以在google或torry.net上找到實現)。 對於> 1gb的文件,它不能很好地工作(在Win32中,在Win64中您幾乎可以映射任何文件)。 它將所有文件轉換成PAnsiChar ,您可以像掃描一個大緩沖區一樣進行掃描,搜索#10和#13(單獨或成對),從而手動分割字符串。

如果您使用(或不介意開始使用) JEDI Jvcl ,則它們具有TJvCSVDataSet ,使您可以像在Delphi中使用任何其他數據集一樣簡單地使用CSV文件,包括能夠定義持久字段和使用“標准” Delphi數據庫功能:

JvCSVDataSet1.FileName := 'MyFile.csv';
JvCSVDataSet1.Open;
while not JvCSVDataSet1.Eof do
begin
  TempTable.Append; // Posts last appended row automatically;
                    // no need to call Post here.

  // Assumes TempTable has same # of fields in the
  // same order
  for i := 0 to JvCSVDataSet1.FieldCount - 1 do
    TempTable.Fields[i].AsString := JvCSVDataSet1.Fields[i].AsString;
  JvCSVDataSet1.Next;  
end;

// Post the last row appended when the loop above exited
if TempTable.State in dsEditModes then
  TempTable.Post;

在Delphi 7中,您可以使用Turbo Power SysTools TStAnsiTextStream()以面向行的方式進行讀寫,但要使用線程安全的TStream實現而不是不安全的舊pascal文件接口。 在更高的Delphi版本中,您會在標准RTL中找到類似的東西(盡管它們在實現上有所不同),但是Delphi 7在文本文件操作方面沒有提供太多功能。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM