Delphi:使用 Reset/ReadLn 读取文本文件的替代方法
我想逐行处理文本文件。过去,我将文件加载到 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
的问题是没有选项可以在不锁定的情况下读取文件(即 fmShareDenyNone
>)。前一个 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< /code>、
CRLF
的。
我考虑过内存映射文件,但是如果整个文件不适合(映射)到虚拟内存中,并且必须一次映射文件的视图(片段),则会出现困难。开始变得丑陋。
我只想使用 fmShareDenyNone
来重置
!
i want to process a text file line by line. In the olden days i loaded the file into a StringList
:
slFile := TStringList.Create();
slFile.LoadFromFile(filename);
for i := 0 to slFile.Count-1 do
begin
oneLine := slFile.Strings[i];
//process the line
end;
Problem with that is once the file gets to be a few hundred megabytes, i have to allocate a huge chunk of memory; when really i only need enough memory to hold one line at a time. (Plus, you can't really indicate progress when you the system is locked up loading the file in step 1).
The i tried using the native, and recommended, file I/O routines provided by Delphi:
var
f: TextFile;
begin
Reset(f, filename);
while ReadLn(f, oneLine) do
begin
//process the line
end;
Problem withAssign
is that there is no option to read the file without locking (i.e. fmShareDenyNone
). The former stringlist
example doesn't support no-lock either, unless you change it to 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;
So now even though i've gained no locks being held, i'm back to loading the entire file into memory.
Is there some alternative to Assign
/ReadLn
, where i can read a file line-by-line, without taking a sharing lock?
i'd rather not get directly into Win32 CreateFile
/ReadFile
, and having to deal with allocating buffers and detecting CR
, LF
, CRLF
's.
i thought about memory mapped files, but there's the difficulty if the entire file doesn't fit (map) into virtual memory, and having to maps views (pieces) of the file at a time. Starts to get ugly.
i just want Reset
with fmShareDenyNone
!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
因为看起来 FileMode 变量对于文本文件无效,但我的测试表明从文件中多次读取是没有问题的。您在问题中没有提到它,但是如果您在读取文本文件时不打算写入它,那么应该没问题。
As it seems the FileMode variable is not valid for Textfiles, but my tests showed that multiple reading from the file is no problem. You didn't mention it in your question, but if you are not going to write to the textfile while it is read you should be good.
如果您需要在旧版 Delphis 中支持 ansi 和 Unicode,您可以使用我的 GpTextFile 或GpTextStream。
If you need support for ansi and Unicode in older Delphis, you can use my GpTextFile or GpTextStream.
我所做的是使用 TFileStream,但我将输入缓冲到相当大的块(例如,每个块几兆字节)中,并一次读取和处理一个块。这样我就不必一次加载整个文件。
这样,即使对于大文件,它的工作速度也相当快。
我确实有一个进度指示器。当我加载每个块时,我将其增加额外加载的文件的一部分。
一次读取一行,如果没有任何缓冲,对于大文件来说太慢了。
What I do is use a TFileStream but I buffer the input into fairly large blocks (e.g. a few megabytes each) and read and process one block at a time. That way I don't have to load the whole file at once.
It works quite quickly that way, even for large files.
I do have a progress indicator. As I load each block, I increment it by the fraction of the file that has additionally been loaded.
Reading one line at a time, without something to do your buffering, is simply too slow for large files.
几年前我遇到了同样的问题,尤其是锁定文件的问题。我所做的是使用 shellapi 中的低级读取文件。我知道这个问题自从我回答以来已经很老了(两年),但也许我的贡献可以帮助将来的某人。
对我来说,速度的提高似乎是显着的。
I had same problem a few years ago especially the problem of locking the file. What I did was use the low level readfile from the shellapi. I know the question is old since my answer (2 years) but perhaps my contribution could help someone in the future.
For me the speed improvement appeared to be significant.
为什么不直接从 TFileStream 本身一次读取文件行呢?
即(伪代码):
您可能会发现的一个问题是 iirc TFileStream 没有缓冲,因此大文件的性能将不是最佳的。不过,对于非缓冲流的问题有很多解决方案,包括这个 ,您可能希望调查此方法是否解决了您最初的问题。
Why not simply read the lines of the file directly from the TFileStream itself one at a time ?
i.e. (in pseudocode):
One problem you may find with this is that iirc TFileStream is not buffered so performance over a large file is going to be sub-optimal. However, there are a number of solutions to the problem of non-buffered streams, including this one, that you may wish to investigate if this approach solves your initial problem.
在最新的 Delphi 版本中,您可以使用
TStreamReader
。使用文件流构建它,然后调用 其ReadLine
方法(继承自TTextReader
)。所有 Delphi 版本的一个选项是使用 Peter Below 的 StreamIO 单元,它为您提供
AssignStream
。它的工作方式与AssignFile
类似,但针对的是流而不是文件名。使用该函数将流与TextFile
变量关联后,您可以像调用任何其他文件一样调用ReadLn
以及其他 I/O 函数。With recent Delphi versions, you can use
TStreamReader
. Construct it with your file stream, and then call itsReadLine
method (inherited fromTTextReader
).An option for all Delphi versions is to use Peter Below's StreamIO unit, which gives you
AssignStream
. It works just likeAssignFile
, but for streams instead of file names. Once you've used that function to associate a stream with aTextFile
variable, you can callReadLn
and the other I/O functions on it just like any other file.您可以使用此示例代码:
用法:
You can use this sample code:
Usage: