在一个10M的文本文件中搜索指定字符串

来源:互联网 发布:淘宝液晶电视最低价 编辑:程序博客网 时间:2024/06/11 18:30

主  题:  我想在一个10M的文本文件中搜索指定字符串,请问该怎么做
作  者:  yangling18(皮皮)
等  级:  
信 誉 值:  93
所属论坛:  Delphi 语言基础/算法/系统设计
问题点数:  50
回复次数:  40
发表时间:  2005-11-22 8:52:41
  
 
  
如果用BMP算法,需要提供的参数有源字符串和目标字符串,但是源目标字符串有10M,恐怕不行,如果用strPos函数,则需要为这个文本文件buffer分配10M空间,然后再用一个pChar指针指向它,但是给buffer分配10M的空间是否会出错呢,有人能指点一下吗
 
 
 
 
 回复人:kiboisme(蓝色光芒)(http://www.1284.net/)(Delphi编写的脚本)() 信誉:109 2005-11-22 9:18:23 得分:5 1楼
 
 用StrPos,不需要分配内存,就在原内存上找
看例子
var
  Buf : String;
  Key : String;
  FP  : PChar;
begin
  Buf := '';//改成你的读取过程
  Key := '查询字符串';
  //首先得判断一下查询和被查询字符串是否为空
  FP := StrPos(@Buf[1],@Key[1]);
  .......
end; 

 
 
 
 回复人:firstrose(kots)() 信誉:100 2005-11-22 9:55:48 得分:0 2楼
 
 用KMP算法
 
 
 
 回复人:wizardqi(男巫)() 信誉:100 2005-11-22 11:10:55 得分:0 3楼
 
 //楼主说错了吧,应该是KMP才对,这种算法最适应查询外部大容量存储信息,我写了个算法如下,可以实现文件内容高速查询,希望能为楼主带来帮助
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
function KMP(tent,path:String;StartPos:Integer=1;EndPos:Integer=-1):Integer;
var
  f:array of Integer;
  t,i,j,p:Integer;
  lent:Integer;
  lenp:Integer;
begin
  if EndPos>0 then
    Lent:=EndPos
  else
    lent:=Length(tent);
  lenp:=Length(path);
  SetLength(f,lenp+1);
  f[1]:=0;
  for j:=2 to lenp do
  begin
    i:=f[j-1];
    while (path[j]<>path[i+1])and (i>0) do i:=f[i];
    if path[j]=path[i+1] then
      f[j]:=i+1
    else
      f[j]:=0;
  end;
  t:=StartPos;
  p:=1;
  while (t<=lent) and (p<=lenp) do
  begin
    if tent[t]=path[p] then
    begin
      Inc(t);
      Inc(p);
    end
    else if p=1 then
       Inc(t)
    else p:=f[p-1]+1;
  end;
  if p<lenp then
    Result:=-1
  else
    Result:=t-lenp;
end;

function FindStrInFile(Str,FileName:String;var Positions:Array of Int64):Integer;
var
  FS:TFileStream;
  Buffer:String;
  i,ReadBegin,ReadSize,ReadLen:Integer;
  BeginPos:Int64;
  SLen:Integer;
begin
  Result:=0;
  SLen:=Length(Str);
  ReadBegin:=1;
  ReadSize:=SLen*100;
  BeginPos:=0;
  SetLength(Buffer,ReadSize*2);
  FS:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyNone);

  try
    while Result<High(Positions) do
    begin
      ReadLen:=FS.Read(Buffer[ReadBegin],ReadSize);
      if ReadLen>0 then
      begin
        i:=1;
        repeat
          i:=KMP(Buffer,Str,i,ReadBegin+ReadLen-1);
          if i>0 then
          begin
            Positions[Result]:=BeginPos+i;
            Inc(Result);
            Inc(i,SLen);
          end;
        until i<0;
        ReadSize:=SLen*99+1;
        if FS.Size-FS.Position<ReadSize then ReadSize:=FS.Size-FS.Position;
        ReadBegin:=100*SLen-ReadSize;
        Move(Buffer[ReadSize+1],Buffer[1],ReadBegin);
        BeginPos:=BeginPos+ReadSize;
      end
      else
        Break;
    end;
  finally
    FS.Free;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  ps:Array[0..100] of Int64;//搜索匹配窜的确位置序列
  i,n:Integer;
begin
  n:=FindStrInFile('xml',ExtractFilePath(Application.ExeName)+'QTDB.TXT',ps);
  for i:=0 to n-1 do
  begin
    Memo1.Lines.Add('第'+IntToStr(i)+'位置在:'+IntToStr(ps[i]));
  end;
end;

end.

 
 
 
 回复人:yangling18(皮皮)() 信誉:93 2005-11-22 11:13:36 得分:0 4楼
 
 我的笔误,把KMP算法写成了"BMP"算法,KMP算法要源字符串作参数,很难想象源字符串有10M大小
 
 
 
 回复人:Hero4444(阿神)() 信誉:100 2005-11-22 11:47:32 得分:0 5楼
 
 頂
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-22 16:23:32 得分:0 6楼
 
 好像BM算法比KMP算法快了很多,楼主去找找,我记得以前有过,不过不记得了。
 
 
 
 回复人:yangling18(皮皮)() 信誉:93 2005-11-22 17:39:00 得分:0 7楼
 
 BM算法我早试过了,但是不知如何读取10M的文件,难道要length(s)=10M吗
 
 
 
 回复人:baiduan(小安子)() 信誉:100 2005-11-23 8:19:57 得分:0 8楼
 
 关注一下
 
 
 
 回复人:firstrose(kots)() 信誉:100 2005-11-23 8:27:12 得分:5 9楼
 
 AnsiString的长度可以达到4G,如果你有那么多内存的话。

4G>>10M
 
 
 
 回复人:firstrose(kots)() 信誉:100 2005-11-23 8:43:02 得分:0 10楼
 
 我感觉lz的头脑不大清楚。

你题目说要在10M的文件里搜索一个str。显然,str是要在文件里匹配的模式串
然后你又在问题里说“如果用BMP算法,需要提供的参数有源字符串和目标字符串,但是源目标字符串有10M,恐怕不行”。为什么不行?KMP根本是不需要回溯的。而且,你就算用strpos,照样要把那10M从头到尾过一遍。那么,你说那话的原因很清楚,你把那个10M的文件当做模式串了。
下面的话证实了我的判断“KMP算法要源字符串作参数,很难想象源字符串有10M大小”

所以,你完全可以用KMP,只是要先把概念弄明白。
 
 
 
 回复人:li8()() 信誉:99 2005-11-23 8:46:55 得分:5 11楼
 
 用POS定位查询
 
 
 
 回复人:wizardqi(男巫)() 信誉:100 2005-11-23 8:53:33 得分:0 12楼
 
 晕倒,对于外部存储设备的大容量信息查询,使用KMP是最适宜的算法,它的前滚无重复匹配过程最适应外部信息的分段匹配,而且即使纯内部信息查询当信息量大时其时间复杂度也是最优的,在最坏的情况下是O(m)。再者几位所说的MP是指哪个算法??
 
 
 
 回复人:yangling18(皮皮)() 信誉:93 2005-11-23 9:00:39 得分:0 13楼
 
 对不起,是我错了,结贴吧
 
 
 
 回复人:wizardqi(男巫)() 信誉:100 2005-11-23 9:01:25 得分:0 14楼
 
 大家可以访问以下网站确认KMP算法
http://mhss.nease.net/string/KMP.html
 
 
 
 回复人:yangling18(皮皮)() 信誉:93 2005-11-23 9:11:16 得分:0 15楼
 
 我实际上是有一个大约200M的数据表要读,我想把内容导入到文本,分成10M一个的文本文件,再用KMP或BM算法搜索匹配的字符串,要在4秒内返回结果,反正我在数据库内用KMP和BM算法遍历搜索要用40秒以上,所以想把记录导入到文本文件试验一下,如果10M的速度可以的话,考虑分成50M一个的文件,大家有做过这类搜索吗
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 9:52:41 得分:0 16楼
 
 function FindPosWithSys(const ASource, ASub: string; StartPos: Integer): Integer;
begin
  // PosEx in D7.StrUtils
  Result := PosEx(ASub, ASource, StartPos);
end;


type
  TFindPos = function(const ASource, ASub: string; StartPos: Integer): Integer;

procedure TForm1.CompareButtonClick(Sender: TObject);

  function StringFromFile(const FileName: string): string;
  var
    FileSize: Integer;
  begin
    with TMemoryStream.Create do
    try
      LoadFromFile(FileName);
      FileSize := Size;
      SetLength(Result, FileSize);
      Move(Memory^, Result[1], FileSize);
    finally
      Free;
    end;
  end;
 
  procedure Search(const ASource, ASub, MethodName: string; SearchPos: TFindPos);
  var
    S1, E1: DWORD;
    S2, E2: Int64;
    SubLen, StartPos, SearchCount: Integer;
  begin
    S1 := GetTickCount;
    QueryPerformanceCounter(S2);
    StartPos := 1;
    SearchCount := 0;
    SubLen := Length(ASub);
    StartPos := SearchPos(ASource, ASub, StartPos);
    while StartPos <> 0 do
    begin
      Inc(SearchCount);
      Inc(StartPos, SubLen);
      StartPos := SearchPos(ASource, ASub, StartPos);
    end;
    QueryPerformanceCounter(E2);
    E1 := GetTickCount;
    MsgMemo.Lines.Add(Format('%20.20s GetTick Time: %10.10d, Query Counter: %10.10d, Search: %d',
      [MethodName, E1 - S1, E2 - S2, SearchCount]));
  end;

var
  Source, Find: string;
begin
  Find := Trim(FindEdit.Text);
  if Find = '' then
    raise Exception.Create('嗯,找到N多,俺就不说了。');
  Source := StringFromFile(FileEdit.Text);
  try
    Search(Source, Find, 'FindPosWithPosEx', FindPosWithSys);
  finally
    SetLength(Source, 0);
  end;
end;

 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 10:07:38 得分:0 17楼
 
 10M一般查找内容一般都在0.0N秒内好像(以前测的),即时你用的是PosEx, 费时的是read/write文件,所以,一般优化的操作是在读写文件,看你的用途了。

谁将KMP算法写成:
TFindPos = function(const ASource, ASub: string; StartPos: Integer): Integer;
然后套到上面的
  try
    Search(Source, Find, 'FindPosWithKMP', FindPosWithKMP);
  finally
    SetLength(Source, 0);
  end;

看看KMP跟PosEx快多少
 
 
 
 回复人:wizardqi(男巫)() 信誉:100 2005-11-23 10:09:02 得分:0 18楼
 
 假如考虑内存问题,没必要将200M文件分块,你可以使用我的那段程序作如下调整:
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
function KMP(tent,path:String;StartPos:Integer=1;EndPos:Integer=-1):Integer;
var
  f:array of Integer;
  t,i,j,p:Integer;
  lent:Integer;
  lenp:Integer;
begin
  if EndPos>0 then
    Lent:=EndPos
  else
    lent:=Length(tent);
  lenp:=Length(path);
  SetLength(f,lenp+1);
  f[1]:=0;
  for j:=2 to lenp do
  begin
    i:=f[j-1];
    while (path[j]<>path[i+1])and (i>0) do i:=f[i];
    if path[j]=path[i+1] then
      f[j]:=i+1
    else
      f[j]:=0;
  end;
  t:=StartPos;
  p:=1;
  while (t<=lent) and (p<=lenp) do
  begin
    if tent[t]=path[p] then
    begin
      Inc(t);
      Inc(p);
    end
    else if p=1 then
       Inc(t)
    else p:=f[p-1]+1;
  end;
  if p<lenp then
    Result:=-1
  else
    Result:=t-lenp;
end;

function FindStrInFile(Str,FileName:String;var Positions:Array of Int64):Integer;
const
  BUFFERBLOCK=10000;//调整这个可以加快文件读取进度
var
  FS:TFileStream;
  Buffer:String;
  i,ReadBegin,ReadSize,ReadLen:Integer;
  BeginPos:Int64;
  SLen:Integer;
begin
  Result:=0;
  SLen:=Length(Str);
  ReadBegin:=1;
  ReadSize:=SLen*BUFFERBLOCK;
  BeginPos:=0;
  SetLength(Buffer,ReadSize);
  FS:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyNone);
  try
    while Result<High(Positions) do
    begin
      ReadLen:=FS.Read(Buffer[ReadBegin],ReadSize);
      if ReadLen>0 then
      begin
        i:=1;
        repeat
          i:=KMP(Buffer,Str,i,ReadBegin+ReadLen-1);
          if i>0 then
          begin
            Positions[Result]:=BeginPos+i;
            Inc(Result);
            Inc(i,SLen);
          end;
        until i<0;
        ReadSize:=(BUFFERBLOCK-1)*SLen+1;
        if FS.Size-FS.Position<ReadSize then ReadSize:=FS.Size-FS.Position;
        ReadBegin:=BUFFERBLOCK*SLen-ReadSize+1;
        Move(Buffer[ReadSize+1],Buffer[1],ReadBegin-1);
        BeginPos:=BeginPos+ReadSize;
      end
      else
        Break;
    end;
  finally
    FS.Free;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  ps:Array[0..100] of Int64;//搜索匹配窜的确位置序列
  i,n:Integer;
begin
  n:=FindStrInFile('xml',ExtractFilePath(Application.ExeName)+'QTDB.TXT',ps);
  for i:=0 to n-1 do
  begin
    Memo1.Lines.Add('第'+IntToStr(i)+'位置在:'+IntToStr(ps[i]));
  end;
end;

end.
 
 
 
 回复人:wizardqi(男巫)() 信誉:100 2005-11-23 10:11:59 得分:0 19楼
 
 //楼上大哥看看PosEx的源代码,保证吓你一大跳
function PosEx(const SubStr, S: string; Offset: Cardinal = 1): Integer;
var
  I,X: Integer;
  Len, LenSubStr: Integer;
begin
  if Offset = 1 then
    Result := Pos(SubStr, S)
  else
  begin
    I := Offset;
    LenSubStr := Length(SubStr);
    Len := Length(S) - LenSubStr + 1;
    while I <= Len do
    begin
      if S[I] = SubStr[1] then
      begin
        X := 1;
        while (X < LenSubStr) and (S[I + X] = SubStr[X + 1]) do
          Inc(X);
        if (X = LenSubStr) then
        begin
          Result := I;
          exit;
        end;
      end;
      Inc(I);
    end;
    Result := 0;
  end;
end;
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 10:49:55 得分:0 20楼
 
 2 wizardqi
  你将你的KMP写成: FindPos(const ASource, ASub: string; StartPos: Integer): Integer;格式,然后代入那上面的代码去比较一下。

我只想比较一下,不管哪种写法。
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 10:51:52 得分:0 21楼
 
 而且PosEx简单,你没看到PosEx只有几个简单的变量,进行简单的比较,而没能其它函数中初始的过程,一般过程,专门的算法只是为了专门的用途,所以,俺平常用的还是它的多。
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 10:53:42 得分:0 22楼
 
 PosEx代码很正常啊,怎么吓我一跳了?
另:刚才改了你的KMP函数,还真是吓我一跳,丢到我的那函数中,好像死掉了,郁闷,还是你来吧。
 
 
 
 回复人:firstrose(kots)() 信誉:100 2005-11-23 11:05:36 得分:0 23楼
 
 那么,用BM好了。

lz最后发的帖子终于比较清楚地说出了真实目的。

不管你文件多大,关键是,你可以用MapFile来快速读文件,这样就不用考虑什么内存问题。这样,再配BM算法,应该比较快。

我刚刚google了一下,KMP的平均查找时间不如BM。
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 11:31:17 得分:0 24楼
 
 function FindPosWithBM(const ASource, ASub: string; StartPos: Integer): Integer;
const
  MAX_CHAR = 256;
  SizeInt = SizeOf(Integer);
type
  PByteArr = ^TByteArr;
  TByteArr = array [0..MaxInt - 1] of Byte;
var
  Src, Sub: PByte;
  I, J, CurrPos, SubLen, SrcLen: Integer;
  Buffer: array [0..MAX_CHAR - 1] of Integer;
begin
  Result := 0;
  SubLen := Length(ASub);
  SrcLen := Length(ASource);
  if SubLen > SrcLen then Exit;

  Sub := PByte(ASub);
  Src := PByte(ASource);
  for I := 0 to MAX_CHAR - 1 do
    Buffer[I] := SubLen;
  for I := 0 to SubLen - 2 do
    Buffer[PByteArr(Sub)^[I]] := SubLen - I - 1;

  CurrPos := SubLen + StartPos - 2;
  while CurrPos < SrcLen do
  begin
    I := CurrPos;
    J := SubLen - 1;
    while (J >= 0) and ((PByteArr(Src)^[I] = PByteArr(Sub)^[J])) do
    begin
      Dec(J);
      Dec(I);
    end;
    if -1 = J then
    begin
      Result := CurrPos - SubLen + 1;
      break;
    end;
    Inc(CurrPos, Buffer[PByteArr(Src)^[CurrPos]]);
  end;
end;

function KMP(const ASource, ASub: string; StartPos: Integer = 1):Integer;
var
  F: array of Integer;
  I, J, SrcLen, SubLen: Integer;
begin
  Result := 0;
  SubLen := Length(ASub);
  SetLength(f,SubLen + 1);
  SrcLen := Length(ASource);
  F[0] := 0;
  F[1] := 0;

  I := 1; J := 0;
  FillChar(F[0], SizeOf(Integer) * (SubLen + 1), 0);
  while I < SubLen do
    if (J = 0) or (ASub[I] = ASub[J]) then
    begin
      Inc(I);
      Inc(J);
      F[I] := J;
    end else
      J := F[J];

  J := 1;
  I := StartPos;
  while (I <= SrcLen) and (J <= SubLen) do
  begin
    if ASource[I] = ASub[J] then
    begin 
      Inc(I);
      Inc(J);
    end
    else
    if J = 1 then
      Inc(I)
    else
      J := F[J - 1] + 1;
    if J > SubLen then
    begin
      Result := I - SubLen;
      break;
    end;
  end;
end;

function FindPosWithKMP(const ASource, ASub: string; AStartPos: Integer): Integer;
begin
  Result := KMP(ASource, ASub, AStartPos);
end;
 
 
 
 回复人:yangling18(皮皮)() 信誉:93 2005-11-23 11:42:44 得分:0 25楼
 
 弱弱的问一下,MapFile怎么用,另外,BM和KMP各自在数据表中查找时BM比KMP快6秒左右,我想大概是因为我查找的是中文字符串的缘故吧,若是英文,大概就能比BMP快3到5倍,就像文献中说的一样,我之所以想用文本文件查找,主要是因为数据库查找不能令人满意,我的文件有45000多个,在sqlserver中存储,每一项都是一个文本文件的字符串,用BM和KMP查找,用时40多秒,我就想把这些项分别导入几个大的文本文件中,然后查找,在一个文本文件中找到后可以确定它对应数据库中的哪些项,然后再在数据库中查找相应的项,返回正确的文件名,我试验过,用sql语句查找7000项的内容是瞬间完成的,我不知道我的想法是否可行,或者大家有更好的想法
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 11:44:32 得分:15 26楼
 
 用下面这个函数,生成一个大概10M的文件,内容是楼主发贴的内容:
>>如果用BMP算法,需要提供的参数有源字符串和目标字符串,但是源目标字符串有10M,恐怕不
>>行,如果用strPos函数,则需要为这个文本文件buffer分配10M空间,然后再用一个pChar指针
>>指向它,但是给buffer分配10M的空间是否会出错呢,有人能指点一下吗

用发贴内容生成的文件有10M,查找的内容是:buffer
测试的结果是:
FindPosWithBM GetTick Time: 0000000047, Query Counter: 0000194279, Search: 88318
FindPosWithSys GetTick Time: 0000000031, Query Counter: 0000125786, Search: 88318
FindPosWithKMP GetTick Time: 0000000125, Query Counter: 0000474927, Search: 88318

FindPosWithBM GetTick Time: 0000000063, Query Counter: 0000217063, Search: 88318
FindPosWithSys GetTick Time: 0000000047, Query Counter: 0000190896, Search: 88318
FindPosWithKMP GetTick Time: 0000000110, Query Counter: 0000419372, Search: 88318

FindPosWithBM GetTick Time: 0000000078, Query Counter: 0000234192, Search: 88318
FindPosWithSys GetTick Time: 0000000047, Query Counter: 0000150726, Search: 88318
FindPosWithKMP GetTick Time: 0000000140, Query Counter: 0000482356, Search: 88318

procedure TForm1.AddFileButtonClick(Sender: TObject);

  procedure AddFile(FileName: string);
  const
    TEST_SIZE = 10024 * 1000;
  var
    Buffer: Pointer;
    FileSize, Count: Integer;
  begin
    with TFileStream.Create(FileName, fmOpenReadWrite) do
    try
      FileSize := Size;
      Count := FileSize;
      GetMem(Buffer, Count);
      try
        ReadBuffer(Buffer^, Count);
        Inc(FileSize, Write(Buffer^, Count));
        while TEST_SIZE > FileSize do
          Inc(FileSize, Write(Buffer^, Count));
        Caption := Format('文件大小为:%d', [FileSize]);
      finally
        FreeMem(Buffer);
      end;
    finally
      Free;
    end;
  end;
 
begin
  AddFile(FileEdit.Text);
end;
 
 
 
 回复人:wizardqi(男巫)() 信誉:100 2005-11-23 11:47:15 得分:15 27楼
 
 //可能吧. 不过我使用下面的代码扫描100m的文件耗时247毫秒相信楼主对这个结果应该满意了吧!^_^

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, StrUtils, Math, ZLib;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}
function KMP(tent,path:String;StartPos:Integer=1;EndPos:Integer=-1):Integer;
var
  f:array of Integer;
  t,i,j,p:Integer;
  lent:Integer;
  lenp:Integer;
begin
  if EndPos>0 then
    Lent:=EndPos
  else
    lent:=Length(tent);
  lenp:=Length(path);
  SetLength(f,lenp+1);
  f[1]:=0;
  for j:=2 to lenp do
  begin
    i:=f[j-1];
    while (path[j]<>path[i+1])and (i>0) do i:=f[i];
    if path[j]=path[i+1] then
      f[j]:=i+1
    else
      f[j]:=0;
  end;
  t:=StartPos;
  p:=1;
  while (t<=lent) and (p<=lenp) do
  begin
    if tent[t]=path[p] then
    begin
      Inc(t);
      Inc(p);
    end
    else if p=1 then
       Inc(t)
    else p:=f[p-1]+1;
  end;
  if p<lenp then
    Result:=-1
  else
    Result:=t-lenp;
end;


procedure FindStrInFile(Str,FileName:String;out Positions:Pointer;out GetPosNum:Integer);
const
  BUFFERBLOCK=10000;//调整这个可以加快文件读取进度
var
  FS:TFileStream;
  Buffer:String;
  i,ReadBegin,ReadSize,ReadLen:Integer;
  BeginPos,Count:Int64;
  SLen:Integer;
begin
  Count:=0;
  GetPosNum:=BUFFERBLOCK;
  GetMem(Positions,SizeOf(Int64)*GetPosNum);
  SLen:=Length(Str);
  ReadBegin:=1;
  ReadSize:=SLen*BUFFERBLOCK;
  BeginPos:=0;
  SetLength(Buffer,ReadSize);
  FS:=TFileStream.Create(FileName,fmOpenRead or fmShareDenyNone);
  try
    try
      while True do
      begin
        ReadLen:=FS.Read(Buffer[ReadBegin],ReadSize);
        if ReadLen>0 then
        begin
          i:=1;
          repeat
            i:=KMP(Buffer,Str,i,ReadBegin+ReadLen-1);
            if i>0 then
            begin
              if Count>GetPosNum then
              begin
                Inc(GetPosNum,BUFFERBLOCK*SizeOf(Int64));
                ReallocMem(Positions,GetPosNum);
              end;
              PInt64(Integer(Positions)+Count*SizeOf(Int64))^:=BeginPos+i;
              Inc(Count);
              Inc(i,SLen);
            end;
          until i<0;
          if ReadLen<ReadSize then Break;
          if Count>0 then
            ReadSize:=Max((BUFFERBLOCK-1)*SLen+1,i+SLen)
          else
            ReadSize:=(BUFFERBLOCK-1)*SLen+1;
          if FS.Size-FS.Position<ReadSize then ReadSize:=FS.Size-FS.Position;
          ReadBegin:=BUFFERBLOCK*SLen-ReadSize+1;
          Move(Buffer[ReadSize+1],Buffer[1],ReadBegin-1);
          BeginPos:=BeginPos+ReadSize;
        end
        else
          Break;
      end;
      GetPosNum:=Count;
      ReallocMem(Positions,GetPosNum*SizeOf(Int64));
    except
      on E:Exception do
      begin
        GetPosNum:=0;
        FreeMem(Positions);
      end;
    end;
  finally
    FS.Free;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  ps:Pointer;//搜索匹配窜的确位置序列
  s,e,i,n:Integer;
begin
  s:=GetTickCount;
  FindStrInFile('xml is a good document!',ExtractFilePath(Application.ExeName)+'QTDB2.TXT',ps,n);
  e:=GetTickCount;
  Memo1.Lines.Add('本次扫描耗时'+IntToStr(e-s)+'毫秒');
  Memo1.Lines.Add('扫描到'+IntToStr(n)+'个结果');
  for i:=0 to n-1 do
  begin
    Memo1.Lines.Add('第'+IntToStr(i+1)+'位置在:'+IntToStr(PInt64(Integer(ps)+i*SizeOf(Int64))^));
  end;
  FreeMem(ps);
end;

end.
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 11:48:09 得分:0 28楼
 
 你说的好像是全文检索,嘿,不是俺擅长的东西。
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-23 11:53:20 得分:0 29楼
 
 据说全文检索的东西,索引比内容大的多,所以索引要进行再索引。
一般是用HASH做,我也想研究一下,不过,感觉有点不自量力。
 
 
 
 回复人:yangling18(皮皮)() 信誉:93 2005-11-23 14:07:41 得分:0 30楼
 
 请问wizardqi(男巫) ,如果查找的内容是你的100M文件的最后一行的内容,要多少时间,能测一下吗
 
 
 
 回复人:wizardqi(男巫)() 信誉:100 2005-11-23 16:25:43 得分:0 31楼
 
 呵呵,231毫秒,也许是我的电脑配置好了点(DELL品牌机,P4 2.8+1G DDR(硬盘好象是7200转的,其实在这项应用中内存的频率和硬盘的读取效率影响极大).
 
 
 
 回复人:yangling18(皮皮)() 信誉:93 2005-11-24 8:58:01 得分:0 32楼
 
 请问,如果我一次分配不了所需的内存,那么怎么用字符串匹配函数呢
 
 
 
 回复人:firstrose(kots)() 信誉:100 2005-11-24 9:11:36 得分:5 33楼
 
 楼上贴程序的,统统打pp,然后拖出去面壁!

请注意了,程序都有问题。你们没有考虑到边界问题。

假如buf为1k,模式串是abcde
如果文件的1023处有abcde,那么按照你们的方法是找不到的!


lz可以在MSDN里查MapViewOfFile
 
 
 
 回复人:syfly739(飞仔)() 信誉:100 2005-11-24 13:20:35 得分:0 34楼
 
 有趣,学习一下!
 
 
 
 回复人:saoren(saoren)() 信誉:100 2005-11-24 13:39:43 得分:0 35楼
 
 2 firstrose
  我好像没使用buf吧,就是使用了buf,那是调用者的问题,而不是算法本身的问题。Windows N多API,我没调用好,那是自己的问题,关MS什么事啊。
 
 
 
 回复人:wizardqi(男巫)() 信誉:100 2005-11-24 17:12:59 得分:0 36楼
 
 不会吧,我考虑边界问题了,怎么会找不到呢我找到了呀!!用时258毫秒扫描扫描100M文件.