如何逐行读取一个30多M的文本文件(100分)

  • 主题发起人 主题发起人 yangguangbingdi
  • 开始时间 开始时间
Traveller兄:TStringList到底应该怎么用?能举例说明码?免得我搞错了,如肯赐教,不胜感激!
 
to yangguangbingdian:
TStringList可以像下面这样用:
procedure TForm1.button1Click(sender: TObject)
var
sl : TStringList;
i : int64;
CurrItem : string;
begin
try
sl :=TStringList.Create;
sl.loadFromFile('c:/kkk.txt'); //将你的30M的txt文件读入
for i :=0 to sl.Count-1 do
begin
CurrItem :=sl.Strings; //将当前行赋给变量CurrItem;
//对CurrItem进行你想要的分析
end;
finally
sl.free;
end;
end;
 
呵呵,DELPHI会生成一个30M大小的STRING,够你处理的
 
(2003-02-11 20:26:36) tseug
这个用内存映射可以, 用BlockRead也可以,但是由于是搜索,所以缓冲区的尺寸要注意考虑,需要根据搜索字符串的长度处理...
(通过服务器中转)

(2003-02-11 20:28:38) DELPHI-FANS
是啊,如果要做到最快,开始的时候估计要多次测试,比较以后才能定缓冲区的大小,一般我认为应该是4M左右比较合适,不知道你的看法是?

(2003-02-11 20:36:40) tseug
大小需要考虑2方面,
1 磁盘扇区的尺寸,512的整倍数, 另外是内存页的尺寸4K, 所以应该用4K的倍数
2 需要搜索的字符串的长度, 缓冲区长度不是它的整数倍, 要处理边界情况, 这个影响代码的复杂度...
(通过服务器中转)

(2003-02-11 20:40:36) DELPHI-FANS
恩,对,我有一个代码是用文件映射写的,对尾部的情况考虑的还不错,
他是用一个和搜索字符串长度小一的字符串来保存上个缓冲区的尾巴,在下一个缓冲区开始的头部加上去,再进行扫描,

(2003-02-11 20:42:19) DELPHI-FANS
如果要用KPM匹配算法那就更麻烦了

(2003-02-11 20:43:02) tseug
呵呵, 当然了, 不过也不复杂, 你刚才说的那个办法就可以, 先局部用KPM检索
(通过服务器中转)
 
to it80:
我这里没有出现这种情况啊, 我用TMemoryStream载入30M文本只需要1秒钟,我的机器是PIII800, 192内存,算是中等偏下配置
 
用TStringList调入43.9M大小的5M行文本文件花费49秒,因为其中包含了对断行的分析时间。
 
to 楼主:
如果我没有猜错,那么这个文件应该是远程服务器上的日志吧。
如果这个文件只能通过ftp协议访问,那么除了下载到本地没有什么好办法,当然如果熟悉unix编程的话可以把分析程序放在unix上,通过远程命令来控制。
如果这个文件可以通过//server/path/filename.txt的形式访问,那么把它当作一个普通的文件处理就可以了:Buf.LoadFromFile('//server/path/filename.txt')
另外,日志的搜索是不需要分行的,因为你要搜索的字符串中不可能包含回车,所以按照普通字符串匹配就可以了,与TStringList相比,速度的提高将非常明显。
 
非常感谢Traveller兄,你分析的很好,我已经做了一个C程序直接在服务器上作后台控制,但是我想用前台的DELPHI进行控制后台程序是否运行,但找不到很好的控制办法,怎样让Delphi去运行一个远程UNIX 服务器上的程序?
你说通过远程命令来控制
需要使用什么控件吗?或使用什么函数命令?
 
如果你已经在服务器上写了程序,那么用telnet或者PCAnywhere等远程登录工具登录上去直接操作就行了。
当然,也可以让服务程序在服务器上等待客户端发来的tcp/ip命令串,不过安全性方面可能会比较复杂,这方面我不懂。
 
Traveller兄:因为我想用delphi去完成一部分功能,再用后台的C程序去完成另一部分功能。让Delphi去调用一个远程UNIX 服务器上的程序是不是真的没有什么很好的办法?
请答复!
 
Delphi有没有能完成telnet的功能的控件
 
抱歉,我没有做过unix下的程序,只能说说大致的原理:
1.服务器上的C程序负责监听特定的端口(比如:1024)
2.客户端Delphi程序通过tcp/ip发送命令字符串给服务器的1024端口
3.服务器上的C程序解释此命令串并执行相应的功能

网络编程方面比较复杂,我也不熟,说不清。

如果unix上有个web服务器,那么你可以用java在服务器上写一个管理程序,从IE登录就可以进行管理了。但要注意安全性。
另外,除非拨号,否则网络上一次传输30M文本的时间还是可以接受的,下载到本地可以减小编程的复杂度,纯粹在客户端完成也未尝不可。

URLMON单元的下列函数可以下载文件到本地:
UrlDownloadToFile(nil, 'ftp://server/path/filename.txt', 'c:/localdir/filename.txt', 0, nil);
这样就可以在本地分析了。
 
Traveller兄:我不是不想完全在本地进行分析,但是打开一个30多兆的文本文件(共计400多万行)还只是第一步,我还要对这个30多M的文本文件进行几万次的循环搜索,我不能想象在PC机上这会耗时多久,请注意有400 多万行,我不知到readln()对读取的行数有没有限制,而用后台C完成这些工作却是轻而易举。不知你有什么看法?
 
我不清楚你要进行什么样的搜索,你还是自己试试吧,30M文本不算太多,进行算法优化的话在一分钟左右应该可以完成的搜索。
 
Traveller兄:
我是用了小唐提供的TStringList可以像下面这样用:
procedure TForm1.button1Click(sender: TObject)
var
sl : TStringList;
i : int64;
CurrItem : string;
begin
try
sl :=TStringList.Create;
sl.loadFromFile('c:/kkk.txt'); //将你的30M的txt文件读入
for i :=0 to sl.Count-1 do
begin
CurrItem :=sl.Strings; //将当前行赋给变量CurrItem;
//对CurrItem进行你想要的分析
end;
finally
sl.free;
end;
end;
用这种方式打开那个文件读取到最后一行用了大约5、6分钟(我是P3 866 128M内存)
引用:
如果想用缓冲区可以用TMemoryStream,使用非常简便
Buf := TMemoryStream.Create;
try
Buf.LoadFromFile('FileName.txt');
p := PChar(Buf.Memory);
// 此时p指向文件缓冲区头部,可以用如下循环进行处理
for i := 0 to Buf.Size - 1 do
p...
finally
Buf.Free;
end;
你所说得P能等于一行文本吗?我要怎么样取出每一行的文本呢?
 
Traveller兄:
使用TMemoryStream怎样读取一行文本
 
p只是一个普通的缓冲区,不是一行文本,必须自己分析才行。
TStringList之所以很慢是因为它要不断的分配字符串,如果是只读的内容,那么你可以用一个数据结构保存各行的起始偏移和长度。
type
TLineRef = packed record
Offset: Integer;
Len: Integer;
end;
TLines = Array of TLineRef; // 为简单起见这里用动态数组,也可以用TList来保存。

你只要对整个缓冲区扫描一遍,然后把每一行的起始偏移和长度保存在这个列表里就可以非常方便的按行操作了。
这个方法比较简单,你不妨自己写写看,我的MSN是ASnowWolf@MSN.COM,如果还是不明白可以联系我。
 
我现在是将数据读到流中循环处理,
但是我总觉得还会有更快速的方法,
需要算法研究了。关注此问题。
 
1.如果只是搜索的话,分行是没有必要的,当作一个普通字符串处理要快得多。
2.字符串匹配有一个快速算法,好像是KPM还是什么的,记不清了。
3.加载数据速度最快的是内存映像文件——它直接把存放文件的硬盘区域映射到虚拟地址空间,不会有任何内存拷贝操作出现。
 
多人接受答案了。
 
后退
顶部