Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.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
Delphi:使用Reset/ReadLn读取文本文件的替代方法_Delphi_Text Files_Readline - Fatal编程技术网

Delphi:使用Reset/ReadLn读取文本文件的替代方法

Delphi:使用Reset/ReadLn读取文本文件的替代方法,delphi,text-files,readline,Delphi,Text Files,Readline,我想逐行处理一个文本文件。以前,我将文件加载到StringList: slFile := TStringList.Create(); slFile.LoadFromFile(filename); for i := 0 to slFile.Count-1 do begin oneLine := slFile.Strings[i]; //process the line end; 问题是一旦文件达到几百兆字节,我就必须分配一大块内存;实际上,我一次只需要足够的内存来容纳一行。(另外,

我想逐行处理一个文本文件。以前,我将文件加载到
StringList

slFile := TStringList.Create();
slFile.LoadFromFile(filename);

for i := 0 to slFile.Count-1 do
begin
   oneLine := slFile.Strings[i];
   //process the line
end;
问题是一旦文件达到几百兆字节,我就必须分配一大块内存;实际上,我一次只需要足够的内存来容纳一行。(另外,当系统在步骤1中被锁定加载文件时,您不能真正指示进度)

我尝试使用Delphi提供的本机和推荐的文件i/O例程:

var
   f: TextFile;
begin
   Reset(f, filename);
   while ReadLn(f, oneLine) do
   begin
       //process the line
   end;
Assign
的问题是,没有锁定就无法读取文件(即
fmsharedynone
)。前一个
stringlist
示例也不支持无锁,除非将其更改为
LoadFromStream

slFile := TStringList.Create;
stream := TFileStream.Create(filename, fmOpenRead or fmShareDenyNone);
   slFile.LoadFromStream(stream);
stream.Free;

for i := 0 to slFile.Count-1 do
begin
   oneLine := slFile.Strings[i];
   //process the line
end;
所以现在,即使我没有获得任何被持有的锁,我还是回到了将整个文件加载到内存中

除了
Assign
/
ReadLn
,我可以逐行读取文件,而无需使用共享锁,还有其他方法吗

我不想直接进入Win32
CreateFile
/
ReadFile
,而必须处理分配缓冲区和检测
CR
LF
CRLF

我考虑过内存映射文件,但是如果整个文件不适合(映射)到虚拟内存中,并且必须一次映射文件的视图(片段),则会有困难。开始变丑了


我只想用
fmsharedynone
重置

为什么不直接从TFileStream本身一次读取一行文件

i、 e.(伪代码):

readline:
而不是EOF和(readchar EOL)do
追加字符到结果
而不是EOF做什么
开始
s:=读线
过程s
结束;

您可能会发现这样做的一个问题是iirc TFileStream没有缓冲,因此大文件上的性能将是次优的。但是,对于非缓冲流的问题,有许多解决方案,您可能希望研究这种方法是否解决了初始问题。

我所做的是使用TFileStream,但我将输入缓冲到相当大的块(例如,每个块数兆字节)中,并一次读取和处理一个块。这样我就不必一次加载整个文件

这样工作速度相当快,即使对于大文件也是如此

我有一个进度指标。当我加载每个块时,我会将其增加额外加载的文件部分


一次读取一行,而不进行缓冲,对于大文件来说太慢了。

对于最新的Delphi版本,您可以使用。用您的文件流构造它,然后调用(继承自
TTextReader


所有Delphi版本都可以使用一个选项,它为您提供
AssignStream
。它的工作原理类似于
AssignFile
,但用于流而不是文件名。使用该函数将流与
TextFile
变量关联后,您可以像调用任何其他文件一样调用
ReadLn
及其上的其他I/O函数。

因为FileMode变量似乎对Textfiles无效,但我的测试表明,从该文件多次读取没有问题。您在问题中没有提到它,但是如果您不打算在读取文本文件时写入文本文件,您应该会很好。

如果您需要在旧的Delphis中支持ansi和Unicode,您可以使用我的或

您可以使用以下示例代码:

TTextStream = class(TObject)
      private
        FHost: TStream;
        FOffset,FSize: Integer;
        FBuffer: array[0..1023] of Char;
        FEOF: Boolean;
        function FillBuffer: Boolean;
      protected
        property Host: TStream read FHost;
      public
        constructor Create(AHost: TStream);
        destructor Destroy; override;
        function ReadLn: string; overload;
        function ReadLn(out Data: string): Boolean; overload;
        property EOF: Boolean read FEOF;
        property HostStream: TStream read FHost;
        property Offset: Integer read FOffset write FOffset;
      end;

    { TTextStream }

    constructor TTextStream.Create(AHost: TStream);
    begin
      FHost := AHost;
      FillBuffer;
    end;

    destructor TTextStream.Destroy;
    begin
      FHost.Free;
      inherited Destroy;
    end;

    function TTextStream.FillBuffer: Boolean;
    begin
      FOffset := 0;
      FSize := FHost.Read(FBuffer,SizeOf(FBuffer));
      Result := FSize > 0;
      FEOF := Result;
    end;

    function TTextStream.ReadLn(out Data: string): Boolean;
    var
      Len, Start: Integer;
      EOLChar: Char;
    begin
      Data:='';
      Result:=False;
      repeat
        if FOffset>=FSize then
          if not FillBuffer then
            Exit; // no more data to read from stream -> exit
        Result:=True;
        Start:=FOffset;
        while (FOffset<FSize) and (not (FBuffer[FOffset] in [#13,#10])) do
          Inc(FOffset);
        Len:=FOffset-Start;
        if Len>0 then begin
          SetLength(Data,Length(Data)+Len);
          Move(FBuffer[Start],Data[Succ(Length(Data)-Len)],Len);
        end else
          Data:='';
      until FOffset<>FSize; // EOL char found
      EOLChar:=FBuffer[FOffset];
      Inc(FOffset);
      if (FOffset=FSize) then
        if not FillBuffer then
          Exit;
      if FBuffer[FOffset] in ([#13,#10]-[EOLChar]) then begin
        Inc(FOffset);
        if (FOffset=FSize) then
          FillBuffer;
      end;
    end;

    function TTextStream.ReadLn: string;
    begin
      ReadLn(Result);
    end;

几年前我也遇到过同样的问题,尤其是锁定文件的问题。我所做的是使用来自shellapi的低级读取文件。我知道这个问题在我回答(2年)后已经过时了,但也许我的贡献可以在将来帮助别人

const
  BUFF_SIZE = $8000;
var
  dwread:LongWord;
  hFile: THandle;
  datafile : array [0..BUFF_SIZE-1] of char;

hFile := createfile(PChar(filename)), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, 0);
SetFilePointer(hFile, 0, nil, FILE_BEGIN);
myEOF := false;
try
  Readfile(hFile, datafile, BUFF_SIZE, dwread, nil);   
  while (dwread > 0) and (not myEOF) do
  begin
    if dwread = BUFF_SIZE then
    begin
      apos := LastDelimiter(#10#13, datafile);
      if apos = BUFF_SIZE then inc(apos);
      SetFilePointer(hFile, aPos-BUFF_SIZE, nil, FILE_CURRENT);
    end
    else myEOF := true;
    Readfile(hFile, datafile, BUFF_SIZE, dwread, nil);
  end;
finally
   closehandle(hFile);
end;

对我来说,速度的提高似乎意义重大。

-1。即使对于非文本文件,调用
Reset
时,除了
FileMode
的下两位之外,所有的
都会被屏蔽,因此共享标志也会被忽略。你真的尝试过吗?我制作了一个简单的应用程序,用fmOpenRead+fmShareDenyWrite打开一个文本文件,每次点击按钮都读取一行,然后将其添加到TMemo中。我可以执行两次应用程序,同时读取文件。此外,禁止写入文件。如果有人感兴趣,我可以编辑我的答案,包括相关的源代码。顺便说一句,用D2010测试。我刚刚做了另一个测试:即使没有FmSharedyWrite,它也能工作。到目前为止,我遇到的唯一缺点是,在文件打开读取时(即使使用fmsharedynone),似乎不可能写入文件,但从多个进程读取似乎没有问题。我不想这样做的原因是,不容易做到正确。例如,您的伪代码有3个细微的bug。因此,与其重新发明一个有缺陷的轮子,我更愿意使用封装的、经过测试的代码。它怎么能包含缺陷呢?这是用来说明想法的伪代码,不是真正的代码!!如何实现真正的代码将决定它是否包含bug。您希望在从光盘读取if时处理文件,而不是在读取整个内容后处理文件,然后在流媒体传输时处理文件,这正是您所需要的(您会注意到,所有其他答案都是此主题的变体!)。如果你已经知道你想听到什么样的答案,为什么还要问这个问题呢?伪代码用于显示算法,而不需要处理特定的语言。在这种情况下,算法是有缺陷的。我很抱歉,但是如果你需要在这样的论坛上用填鸭式输入完整的工作代码,即使是伪代码的形式,那么你应该找另一份工作。软件开发显然不是你的专长(如果你能很好地发现细微的逻辑缺陷,那么你就能很好地编写没有这些缺陷的真正代码)。FFS这是我能在t上发现虫子的唯一原因
procedure ReadFileByLine(Filename: string);
var
  sLine: string;
  tsFile: TTextStream;
begin
  tsFile := TTextStream.Create(TFileStream.Create(Filename, fmOpenRead or    fmShareDenyWrite));
  try
    while tsFile.ReadLn(sLine) do
    begin
      //sLine is your line
    end;
  finally
    tsFile.Free;
  end;
end;
const
  BUFF_SIZE = $8000;
var
  dwread:LongWord;
  hFile: THandle;
  datafile : array [0..BUFF_SIZE-1] of char;

hFile := createfile(PChar(filename)), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, 0);
SetFilePointer(hFile, 0, nil, FILE_BEGIN);
myEOF := false;
try
  Readfile(hFile, datafile, BUFF_SIZE, dwread, nil);   
  while (dwread > 0) and (not myEOF) do
  begin
    if dwread = BUFF_SIZE then
    begin
      apos := LastDelimiter(#10#13, datafile);
      if apos = BUFF_SIZE then inc(apos);
      SetFilePointer(hFile, aPos-BUFF_SIZE, nil, FILE_CURRENT);
    end
    else myEOF := true;
    Readfile(hFile, datafile, BUFF_SIZE, dwread, nil);
  end;
finally
   closehandle(hFile);
end;