2

我有这样的文本文件:

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

我想将此文本文件数据加载到 sybase db 中的临时表中。所以,我需要构建一个程序来逐行读取这个文本文件,直到 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;
4

6 回答 6

4

文本文件通常有一个非常小的缓冲区。考虑使用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;
于 2013-07-18T04:07:35.320 回答
4

一些一般提示:

  • 确保您TempTable在内存中,或使用快速数据库引擎 - 看看 SQlite3 或其他方式(如 FireBird 嵌入式、NexusDB 或 ElevateDB)作为可能的数据库替代方案;
  • 如果您不使用TTable,而是使用真正的数据库,请确保将插入嵌套在 Transaction 中
  • 对于真正的数据库,请检查您是否不能使用ArrayDML功能,该功能可以更快地在远程数据库(如 Sybase)中插入大量数据 -例如使用FireDAC AFAIK 处理此类 Array DML;
  • 众所周知,该FieldByName('...')方法非常慢:改用局部TField变量;
  • 使用 TextFile 时,分配更大的临时缓冲区;
  • 如果您使用的是较新的 Unicode 版本的 Delphi (2009+),则使用 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数据库添加内容的速度有多快:数据库文件中每秒大约 130,000 / 150,000 行,包括所有 ORM 编组。我还发现SQLite3生成的数据库文件比替代方案小得多。如果您想快速检索任何字段,请不要忘记在您的数据库中定义INDEXes,如果可能的话,在插入行数据之后(以获得更好的速度)。对于SQLite3,我想已经有一个ID/RowID可用的整数主键,它映射您的第一个数据字段。这个ID/RowID整数主键已被SQLite3索引。顺便说一句,我们的 ORM 现在支持FireDAC/AnyDAC 及其先进的 Array DML 功能

于 2013-07-18T06:00:36.070 回答
0

除了已经说过的,我也会避免使用任何 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;

希望这可以帮助。

于 2013-07-18T08:24:16.733 回答
0

另一种方法是使用内存映射文件(您可以 google 或去 torry.net 查找实现)。它不适用于> 1gb的文件(在win32中,在win64中,您几乎可以映射任何文件)。它会将您的所有文件变成PAnsiChar您可以像一个大缓冲区一样扫描的文件,搜索#10 和#13(单独或成对),从而手动拆分字符串。

于 2013-07-18T08:25:05.590 回答
0

如果您使用(或不介意开始使用)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;
于 2013-07-18T12:36:43.507 回答
-1

在 Delphi 7 中,您可以使用 Turbo Power SysTools TStAnsiTextStream() 以面向行的方式读取和写入,但使用线程安全的 TStream 实现而不是不安全的旧 pascal 文件接口。在后来的 Delphi 版本中,您会在标准 RTL 中找到类似的东西(尽管它们的实现略有不同),但 Delphi 7 并没有提供太多的文本文件操作。

于 2013-07-18T07:19:56.133 回答