请教各位大虾一个关于TStringList的字符串的优化判断问题! (200分)

  • 主题发起人 白衣书生
  • 开始时间

白衣书生

Unregistered / Unconfirmed
GUEST, unregistred user!
有文本abc.txt内容如下:
.............
XXXXX-20020905
XXXXX-20021115
XXXXX-20021116
XXXXX-20021117
XXXXX-20021119
..........
行数不定。其中每行14位字符。 前5位XXXXX没有规律是任意字符。后8位是日期生成的数字字符串。

现在要判断以‘20021118’结尾的字符串“XXXXX-20021118”在不在文本中,我该如何构建判断代码?
如下:
var Str_List:TStringList;
Str_List:=TStringList.Create;
Str_List.LoadFromFile('abc.txt');
//判断字符串是否存在:为-1,则字符串不存在。
if Str_List.IndexOf('XXXXX-20021118') = -1 then //-------->>这里字符串如何写?
^^^^^^^^^^^^^^^^^^^^
//------因为XXXXX的值不确定,所以无法确定字符串的内容。

假如这样用:
for i:=0 to Str_List.Count-1 do
if copy(Str_List.String,7,8)='20021118' then //---循环判断每一个字符串
.............
但如文本量很多的话,感觉这个循环的消费量会太大,估计机子得“死”一会儿!

在此请教各位大虾有何优化的代码进行判断??? 多谢!!

 
这个简单阿,Pos后换转一下是第几行就行了
使用iPos := Pos('-20021118',str_list.text)取得在字符串中的位置
if iPos > 0 //找到了
iPos := iPos - 5 //转换到XXXXX的起始位置
iLineNumber = iPos div (14 + 2) //要加2,这是回车换行字符长度。
 
直接写if Str_List.IndexOf('-20021118') = -1 then 不行吗?
 
to Adnil:
多谢老兄指点。
果然可以判断到所在的行数。
我还没有用大量的数据测试,不知效率如何?

to zw84611:
直接写IndexOf('-20021118') 是不行的。
因为IndexOf判断的是一个StringList中完整的一项。
 
to Adnil:
再问老兄一个问题:
StringList.Text = StringList.String[0] + 回车换行字符 + StringList.String[1] + 回车换行字符 +.....

是不是这样的存储的???
 
这样的需求用TStringList不是一个好主意,很浪费。
知道TStringList.Text怎么得来的吗? 它是通过一个函数两次循环访问整个Strings的每一项然后添加在一起(每项后自动加入#13#10)得到的。

还不如直接将文件读到一个字符串中然后用pos查找更高效快速呢。
如果文件很大, 那么直接读到string里用pos查找比用TStringList读入再访问Text属性用pos查找快得多。
 
白衣书生:
我觉得Another_eYes说得有一定的道理。
不过,不管是读入一个字符串中,还是使用TStringList,我都推荐你使用俄罗斯人写的
字符串处理函数集(QString),你可以在http://www.torry.net/找到,速度极其快,
我曾经在TStringList上做过测试,读入100万行,而且每行的数据量都比你现在的大多了,
搜索一个字符串也就用了1秒钟时间!
 
to Another_eYes:
多谢大虾您的指点。我明白了很多。
说实话,没有选择用string的原因是我不知道String字符串存储量有没有限制?
直觉上,感觉StringList比String容量大。 [:)] --不知道有没有错?

to DLLKing:
多谢大哥指点。我这就去看看俄国人的东东。
ps:说实话,俄罗斯人做的第三方的东东有些的确不错。
 
在导入文本文件时,可以把它分成两个TStringList
ts1,ts2:TStingList;
ts1 存储XXXXX
ts2 存储20021119

检索ts2 通过index取得ts1中的数据。
 
你如果用richedit的查找功能就快得多,不要用TStringlist,我用过的
 
to DLLKing:
老兄,在http://www.torry.net转了转,发现只有for D5的,没有D6的。

to 宝祯:
>>>>在导入文本文件时,可以把它分成两个TStringList
该怎么导入啊? 我只会一个StringList.LoadFromFile.....

to yanghai0437:
老兄的意思是先把richedit的visible=false,导入RichEdit.Lines.LoadFromFile('a.txt');
然后再查找?---这倒是没有想到。
 
to 白衣书生:
String的存储量有限制, 最大寻址控件(大约4G吧), StringList比String容量大完全是一种想当然的说法, 事实正好相反。
用RichEdit来载入再查找是很愚蠢的想法。
 
白衣书生:
你也没仔细看啊?!这不是控件,就是俄罗斯人写的一个函数单元,你看到其中有限制版本的编译指令了
吗?也就是说,你可以将其中的一个或几个函数提出来,直接放到你自己的程序中使用就可以了。
这个单元有字符串搜索函数、字符串替换函数、大小写转换函数、字符串比较函数......太丰富了!!
英文说明在QS_Eng.pas中,代码不仅使用汇编语言编写,而且经过了优化,所以,速度极快!

下面我就根据你题目中的要求,将搜索字符串的函数提出来一个,并做了个例子,你自己试一下,我想
在Delphi6下应该没有任何问题!代码如下:
//========================================================================================
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
TempList : TStringList;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

{
Q_PosStr函数说明
参数:FindString,要查找的字符串
SourceString,源字符串
StartPos,从什么位置开始查找(所以,你可以用这个参数来查找下一个匹配的字符串)
返回值:如果找到了,返回找到的位置,否则,返回0
}
function Q_PosStr(const FindString, SourceString: string; StartPos: Integer): Integer;
asm
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EDX
TEST EAX,EAX
JE @@qt
TEST EDX,EDX
JE @@qt0
MOV ESI,EAX
MOV EDI,EDX
MOV EAX,[EAX-4]
MOV EDX,[EDX-4]
DEC EAX
SUB EDX,EAX
DEC ECX
SUB EDX,ECX
JNG @@qt0
XCHG EAX,EDX
ADD EDI,ECX
MOV ECX,EAX
JMP @@nx
@@fr: INC EDI
DEC ECX
JE @@qt0
@@nx: MOV EBX,EDX
MOV AL,BYTE PTR [ESI]
@@lp1: CMP AL,BYTE PTR [EDI]
JE @@uu
INC EDI
DEC ECX
JE @@qt0
CMP AL,BYTE PTR [EDI]
JE @@uu
INC EDI
DEC ECX
JE @@qt0
CMP AL,BYTE PTR [EDI]
JE @@uu
INC EDI
DEC ECX
JE @@qt0
CMP AL,BYTE PTR [EDI]
JE @@uu
INC EDI
DEC ECX
JNE @@lp1
@@qt0: XOR EAX,EAX
@@qt: POP ECX
POP EBX
POP EDI
POP ESI
RET
@@uu: TEST EDX,EDX
JE @@fd
@@lp2: MOV AL,BYTE PTR [ESI+EBX]
CMP AL,BYTE PTR [EDI+EBX]
JNE @@fr
DEC EBX
JE @@fd
MOV AL,BYTE PTR [ESI+EBX]
CMP AL,BYTE PTR [EDI+EBX]
JNE @@fr
DEC EBX
JE @@fd
MOV AL,BYTE PTR [ESI+EBX]
CMP AL,BYTE PTR [EDI+EBX]
JNE @@fr
DEC EBX
JE @@fd
MOV AL,BYTE PTR [ESI+EBX]
CMP AL,BYTE PTR [EDI+EBX]
JNE @@fr
DEC EBX
JNE @@lp2
@@fd: LEA EAX,[EDI+1]
SUB EAX,[ESP]
POP ECX
POP EBX
POP EDI
POP ESI
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
TempList := TStringList.Create;
TempList.LoadFromFile('E:/a.txt');
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
TempList.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
iFindPos, iStartTime, iEndTime : Cardinal;
begin
iStartTime := GetTickCount;
iFindPos := Q_PosStr('大富翁123_&^&*)*)ad是啊东方宫bdk3643dsfg', TempList.Text, 0);
iEndTime := GetTickCount;
Caption := '第一个匹配字符串的位置:' + IntToStr(iFindPos) + ', 本次搜索用时:'
+ IntToStr(iEndTime - iStartTime) + '毫秒';
end;

end.
//========================================================================================

我用上面的程序又测试了一个12万行的文本文件,搜索结果是:150毫秒,也就是0.15秒钟
哈哈哈,太快了!!!爽死了!!! :p
 
Pos('-20021118',str_list.text) 可取。
 
如果文件不大(<10M),并且没有重复执行的需求,我觉得没有必要使用第三方的代码
(虽然她很不错),可以达到用户能接受的指标即可,我想一般是在1~2秒种内吧。毕竟
以后程序的维护,阅读都能简单很多。
 
to Another_eYes:多谢您的指点。 谢谢。

to DLLKing:老兄辛苦了,写了这么长的码!
原来这个东东嵌入了asm汇编。
 
可以在循环体里面加入一句
Application.processmessage;
这样就可以相应别的消息啦。不过,不能彻底解决问题,
我觉得用将文本全部读入一个字符串是一个不错的可行办法,
当然,用俄人的控件也不错,假如没有跨平台问题的话。
 
to stargazer:
多谢!
这个Application.processmessage;我在其他程序中见过,但是不知道是干什么用的。
怎么用,老兄能否明示??


 
直接读文件
然后查找
 
顶部