你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 编程语言
利用内存映射文件作快速的全文检索
 

在文件里查找某一字符串,在程序设计上常常要用到。若用Delphi写,小文件可以用TmemoryStream把整个文件读进内存,在内存上进行查找。但若文件比较大,上几十兆,就不能用此法,若你你强行用TmemoryStream,硬盘因交换文件而不断的读写,效率很低。

大家对Win32的内存映射文件(File Mapping)一定不会陌生,用它可以方便的实现多个进程共享数据。这里介绍用内存映射文件作快速的全文检索,功能如Windows的查找(在某目录的文件里查找某一字符串),但此法比Windows的查找效率要高。

内存映射文件可以让我们在访问磁盘文件时如同正在访问内存中的文件一样,可以避免进行文件的输入输出操作。它先是保留一段虚拟内存地址空间,然后将磁盘文件提交给这段内存空间。我们只需要一个指向该区域的指针就可以访问整个文件的内容了。系统负责处理数据的缓存、缓冲、写入和调用以及内存的分配和释放,我们就好像在一块大的内存区域上查找字符串,效率比较高。

下面介绍具体的做法:

1、获得查找文件的文件句柄。可以用FileOpen()打开文件。

2、创建文件内存映射对象

无论是命名的或是无命名的内存映射文件对象,都是用CreateFileMapping()函数创建。函数的参数如下:

function CreateFileMapping(hFile: THandle; //OpenFile()返回的句柄

lpFileMappingAttributes: PSecurityAttributes;//安全属性,一般为nil

flProtect, {

PAGE_READONLY(文件只读)

PAGE_READWRITE  (文件可读写)

PAGE_WRITECOPY(文件可读写,但进行写操作时,会复制修改过的页面)}

dwMaximumSizeHigh,  //文件最大尺寸的高32位,除非文件大于4GB,否则为0

dwMaximumSizeLow: DWORD; //文件最大尺寸高32

lpName: Pchar//文件映射对象的名称,可含除‘/’的所有字符,若nil创建无名对象

): THandle; stdcall;

3.映射文件视图到进程的地址空间

MapViewOfFile()函数,函数参数:

function MapViewOfFile(hFileMappingObject: THandle; //CreateFileMapping()返回句柄

dwDesiredAccess: DWORD;//数据访问模式,可设读写、只读、Copy-on-write

dwFileOffsetHigh, dwFileOffsetLow,

dwNumberOfBytesToMap: DWORD //需要映射的字节数,0为文件全部

): Pointer; //返回视图的起初地址

4.在视图里查找字符串

本查找算法应用了回调函数,当找到符合的字符串时调用函数,若想停止查找,可返回False,详见源程序。

5.事后清洁工作

UnmapViewOfFile()解除文件视图,CloseHandle()分别关闭文件映射对象和文件内核对象。


下面是演示程序窗体和代码:


unit Unit1;interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  StdCtrls;

type

  TForm1 = class(TForm)

    OpenDialog1: TOpenDialog;

    GroupBox1: TGroupBox;

    LFilename: TLabel;

    EdSt: TEdit;

    BBrowse: TButton;

    BSearch: TButton;

    ListBox1: TListBox;

    Label2: TLabel;

    Button3: TButton;

    EdText: TEdit;

    LText: TLabel;

    Cbignore: TCheckBox;

    procedure BBrowseClick(Sender: TObject);

    procedure BSearchClick(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

type FindCallback=function(pos:integer):boolean;

var

  Form1: TForm1;

implementation

{$R *.DFM}

//回调函数,pos为找到字符串在文件的偏移量,若返回True继续查找,False停止查找

function found(pos:integer):boolean;

begin

  Form1.Listbox1.Items.Add('在偏移量:'+inttostr(pos)

   +' 处找到此字符串');

  result:=true;

end;

//查找函数,Fname为查找文件名,CallBack为回调函数(见下),ignoreCase为是否忽略

procedure searchText(Fname:string;text:pchar;

 callback:FindCallback;ignoreCase:boolean=TRUE);

var

  FFileHandle: THandle; // 文件内核句柄

  FMapHandle: THandle;  // 文件映射句柄

  FFileSize: Integer;

  PData: PChar;      // 文件视图的地址

  textlen,i:integer;

  buf:array[0..255] of char;

begin

  if not Assigned(callback) then exit;

  //CallBack过程是否有效,否退出

  FFileHandle := FileOpen(FName, fmOpenRead);

  //打开文件内核对象

  if FFileHandle = INVALID_HANDLE_VALUE then

    raise Exception.Create('打开文件错误');

  try

    FFileSize := GetFileSize(FFileHandle, Nil);

    FMapHandle := CreateFileMapping(FFileHandle, nil,

       PAGE_READONLY, 0, FFileSize, nil);

       //只读方式创建文件内存映射对象

    if FMapHandle = 0 then

      raise Exception.Create('创建文件内存映射对象错误');

  finally

    CloseHandle(FFileHandle);

    //若异常者关闭文件对象

  end;

  try

    PData := MapViewOfFile(FMapHandle, FILE_MAP_READ, 0, 0,

     FFileSize); //映射文件视图

     //PData为映射文件视图,返回映射视图的初始地址

    if PData = Nil then

      raise Exception.Create('创建映射视图出错!');

  finally

    CloseHandle(FMapHandle); //关闭文件映射对象

  end;

  try

   textlen:=strlen(text);

   if ignorecase then //忽略大小写的处理

    begin

      strcopy(text,StrUpper(text));

      for i:=0 to FFileSize-1-textlen do

       begin

        if UpCase(PData[i])=text[0] then

          //找第一个吻合的字符

          begin

             move(Pdata[i+1],buf[0],textlen-1);

             if strLcomp(StrUpper(buf),pchar(@text[1]),

               textlen-1)=0 then //对比剩下的字符串

               if not callback(i) //回传CallBack函数,若

                then  break;  //返回False者退出查找

          end;

       end;

     end else

        begin //大小写区别的处理,和上面的处理相近

         for i:=0 to FFileSize-1-textlen do

           if PData[i]=text[0] then

              begin

                move(Pdata[i+1],buf[0],textlen-1);

                if strLcomp(buf,pchar(@text[1]),

                  textlen-1)=0 then

                  if not callback(i) then  break;

              end;

        end;

   finally

    UnmapViewOfFile(PData); //删除文件视图

  end;

end;

procedure TForm1.BBrowseClick(Sender: TObject);

begin

    if opendialog1.Execute  then

      edst.Text :=opendialog1.FileName;

end;

procedure TForm1.BSearchClick(Sender: TObject);

begin

  searchText(Edst.text,pchar(edtext.text),found,Cbignore.checked);

end;

end.

 

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089