lw549说
取得字符串中指定子字符串出现第n次的位置,效率不高,勉强可用 。感上兴趣,于是试上一试。
程序附在最后,这里是一些说明文字:
1、为快速写好,没有使用应当使用的控制台方式,而是使用了GUI方式;
2、测试的样例是查找包含有四处子串的字符串,四次分别查四个位置。这个在Button1Click方法中完成,它调用Tests来进行具体测试,以被测函数、第几次出现、循环次数为参数;
3、Tests依次在一个循环中重复调用每个具体的函数,同时为了公平起见(也许前面的函数为后面的铺了一些路——内存、高速缓冲),这样的测试进行TEST_COUNT次,最后输出每次的平均时间;
4、经过前期的测试,lw549 的代码的确效率不高,所以单独给它小一些的循环次数(一千次),以免造成程序假死现象;其它的为十万次;
5、其它三个函数的思想为:
PosN_Pos: 使用Pos函数及Copy函数;
PosN_PosEx: 使用Delphi 7中增加的PosEx函数;
PosN_StrPos: 使用StrPos函数。
程序输出:
Search "function GetNSubStringPos(N: Integer; SubString,AString: String): Integer;" for "String"
1:
substr index: 1; LOOP COUNT = 1000
GetNSubStringPos: return 17; Timing: 37.60 ms
substr index: 1; LOOP COUNT = 100000
PosN_Pos: return 17; Timing: 40.40 ms
PosN_PosEx: return 17; Timing: 15.60 ms
PosN_StrPos: return 22; Timing: 37.80 ms
2:
substr index: 2; LOOP COUNT = 1000
GetNSubStringPos: return 42; Timing: 96.80 ms
substr index: 2; LOOP COUNT = 100000
PosN_Pos: return 42; Timing: 81.20 ms
PosN_PosEx: return 42; Timing: 47.00 ms
PosN_StrPos: return 47; Timing: 53.00 ms
3:
substr index: 3; LOOP COUNT = 1000
GetNSubStringPos: return 50; Timing: 109.40 ms
substr index: 3; LOOP COUNT = 100000
PosN_Pos: return 50; Timing: 118.80 ms
PosN_PosEx: return 50; Timing: 53.00 ms
PosN_StrPos: return 55; Timing: 62.60 ms
4:
substr index: 4; LOOP COUNT = 1000
GetNSubStringPos: return 58; Timing: 128.20 ms
substr index: 4; LOOP COUNT = 100000
PosN_Pos: return 58; Timing: 162.60 ms
PosN_PosEx: return 58; Timing: 59.40 ms
PosN_StrPos: return 63; Timing: 74.80 ms
1:
substr index: 1; LOOP COUNT = 1000
GetNSubStringPos: return 17; Timing: 37.60 ms
substr index: 1; LOOP COUNT = 100000
PosN_Pos: return 17; Timing: 40.40 ms
PosN_PosEx: return 17; Timing: 15.60 ms
PosN_StrPos: return 22; Timing: 37.80 ms
2:
substr index: 2; LOOP COUNT = 1000
GetNSubStringPos: return 42; Timing: 96.80 ms
substr index: 2; LOOP COUNT = 100000
PosN_Pos: return 42; Timing: 81.20 ms
PosN_PosEx: return 42; Timing: 47.00 ms
PosN_StrPos: return 47; Timing: 53.00 ms
3:
substr index: 3; LOOP COUNT = 1000
GetNSubStringPos: return 50; Timing: 109.40 ms
substr index: 3; LOOP COUNT = 100000
PosN_Pos: return 50; Timing: 118.80 ms
PosN_PosEx: return 50; Timing: 53.00 ms
PosN_StrPos: return 55; Timing: 62.60 ms
4:
substr index: 4; LOOP COUNT = 1000
GetNSubStringPos: return 58; Timing: 128.20 ms
substr index: 4; LOOP COUNT = 100000
PosN_Pos: return 58; Timing: 162.60 ms
PosN_PosEx: return 58; Timing: 59.40 ms
PosN_StrPos: return 63; Timing: 74.80 ms
可以看出,测试的结果(效率)是: PosN_PosEx > PosN_StrPos > PosN_Pos >> GetNSubStringPos 。
我本来期望的是 PosN_StrPos 最厉害,但结果不是。估计是 PosEx 优化得比较厉害。
附代码:
Unit1.pas:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DB, DBTables; type TPosNFunc = function (N: Integer; const SubString,AString: String): Integer; type TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); procedure FormCreate(Sender: TObject); private procedure Tests(const funcs: array of TPosNFunc; const funcnames: array of string; iStr: integer; loopcount: integer); public end; var Form1: TForm1; implementation uses StrUtils; {$R *.dfm} function GetNSubStringPos(N: Integer; SubString,AString: String): Integer; //返回第n个SubString在AString中出现的位置 //如果没找到,返回-1 var FindCount: Integer; Pos: Integer; begin Result := -1; Pos := 0; for FindCount := 1 to N do begin Inc(Pos); while MidStr(AString, Pos, Length(SubString)) <> SubString do begin if Length(AString) < Length(SubString) + Pos then Exit;//未找到 Inc(Pos); end; end; Result := Pos; end; function PosN_Pos(N: Integer; SubString, AString: String): Integer; var p: integer; nSub: integer; nSrc: integer; begin nSub := Length( SubString ); nSrc := Length( AString ); result := -nSub; while N>0 do begin p := Pos(SubString, AString); if p=0 then break; Dec( N ); Inc( result, p+nSub ); AString := Copy( AString, p+nSub+1, nSrc-nSub-p-1 ); Dec( nSrc, nSub+p ); end; if N>0 then result := -1; end; function PosN_PosEx(N: Integer; SubString,AString: String): Integer; var p: integer; nSub: integer; begin nSub := Length( SubString ); result := 0; p := 0; while N>0 do begin p := PosEx( SubString, AString, p+1 ); if p=0 then break; Dec( N ); result := p; Inc( p, nSub ); end; if N>0 then result := -1; end; function PosN_StrPos(N: Integer; SubString, AString: String): Integer; var pSub, pSrc, p: Pchar; nSub: integer; begin nSub := Length( SubString ); pSub := PChar(SubString); pSrc := PChar(AString); p := pSrc; while (N>0) do begin p := StrPos( p, pSub ); if (p=nil) then break; Inc( p, nSub ); Dec( N ); end; if (N=0) and (p<>nil) then result := p - pSrc else result := 0; end; const STR = 'function GetNSubStringPos(N: Integer; SubString,AString: String): Integer;'; SUBSTR = 'String'; TEST_COUNT = 5; procedure TForm1.Tests( const funcs: array of TPosNFunc; const funcnames: array of string; iStr: integer; loopcount: integer ); var i, j, k: Integer; tm: Longword; func: TPosNFunc; count: integer; retv: array of integer; results: array of Longword; begin count := Length(funcs); assert( count=Length(funcnames) ); Memo1.Lines.Add( Format('substr index: %d; LOOP COUNT = %d', [iStr, loopCount]) ); SetLength( retv, count ); SetLength( results, count ); for j:=0 to count-1 do results[j] := 0; for k:=1 to TEST_COUNT do begin for j:=0 to count-1 do begin func := funcs[j]; tm := GetTickCount; for i:=1 to loopcount do retv[j] := func( iStr, SUBSTR, str ); Inc( results[j], GetTickCount - tm ); end; end; for j:=0 to count-1 do begin Memo1.Lines.Add( Format( '%s: return %d; Timing: %n ms', [funcnames[j], retv[j], results[j]*1.0/TEST_COUNT ] ) ); end; end; procedure TForm1.Button1Click(Sender: TObject); var i: integer; begin for i:=1 to 4 do begin Memo1.Lines.Add( Format( '%d:', [i]) ); Tests( [@GetNSubStringPos], ['GetNSubStringPos'], i, 1000 ); Tests( [@PosN_Pos, @PosN_PosEx, @PosN_StrPos], ['PosN_Pos', 'PosN_PosEx', 'PosN_StrPos'], i, 100000 ); end; end; procedure TForm1.FormCreate(Sender: TObject); begin Memo1.Clear; Memo1.Lines.Add( Format( 'Search "%s" for "%s"', [STR, SUBSTR] ) ); end; end.
Unit1.dfm:
object Form1: TForm1 Left = 243 Top = 164 Width = 578 Height = 516 AlphaBlendValue = 192 Caption = 'Form1' Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] OldCreateOrder = False Position = poDefaultPosOnly OnCreate = FormCreate DesignSize = ( 570 489) PixelsPerInch = 96 TextHeight = 13 object Memo1: TMemo Left = 3 Top = 32 Width = 565 Height = 457 Anchors = [akLeft, akTop, akRight, akBottom] Lines.Strings = ( 'Memo1') ScrollBars = ssVertical TabOrder = 0 end object Button1: TButton Left = 3 Top = 6 Width = 75 Height = 25 Caption = 'Button1' TabOrder = 1 OnClick = Button1Click end end