这个问题好多年前就有了呀
世界上最快的替换函数 来自:Another_eYes
Type
TFastPosProc = function (TagStr, SrcStr: PChar;
TagCount: Integer; var SrcCount: Integer): Integer;
function FastPos(TagStr, SrcStr: PChar; TagCount: Integer;
var SrcCount: Integer): PChar; assembler;
asm
// 进入程序时, TagStr在EAX, SrcStr在EBX, TagCount在ECX
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP
MOV ESI, EAX // EAX = TagStr
MOV EDI, EDX // EDX = SrcStr
MOV EDX, SrcCount // EDX= SrcCount
AND EDX, EDX // SrcCount <= 0?
JLE @NotFound // Yes, not found
DEC ECX // 如果第一个字符匹配时 需要比较的字节数
JL @NotFound // TagCount < 0? Yes-> not found
SUB EDX, ECX // else 计算需要搜索的SrcStr长度
// 此长度=SrcCount - TagCount + 1
JLE @NotFound // SrcCount < TagCount ? Yes-> not found
LODSB // AL = TagStr[1] ESI指向下一个字节
MOV AH, CL //保存当前TagStr长度mod 4的尾数
AND AH, 3 // 到AH中
SHR ECX, 1
SHR ECX, 1
MOV EBP, ECX // 按4字节算的长度, 保存在EBP
MOV ECX, EDX // ECX=SrcCount
@StartFind:
REPNZ SCASB // 搜索第一个字符
JNZ @NotFound // 找不到
MOV EDX, ECX // 保存剩余的SrcCount
MOV ECX, EBP // 取得TagStr长度
MOV EBX, EDI //保存当前SrcStr地址
REPZ CMPSD // 按4字节为单位比较比较Src和Tag
JNZ @FindNext // 不相等
XOR ECX, ECX // ECX清0
MOV CL, AH
REPZ CMPSB // 剩下的字节进行比较
JNZ @FindNext // 不相等
// 找到了, 返回找到的地址, 并修改SrcCount变量(已经去掉了匹配字符串的长度)
DEC EDX // 程序开始时多算一个字节, 现在去掉
MOV SrcCount, EDX
MOV EAX, EBX // 匹配起始地址, 未包含第一个字符
DEC EAX // 包含进第一个匹配字符
JMP @Found
@FindNext:
MOV ESI, SrcStr
INC ESI // 恢复参数准备查找下一轮匹配的字符
MOV ECX, EDX
MOV EDI, EBX
JMP @StartFind
@NotFound:
XOR EAX, EAX // 未找到, 返回 0, 并且SrcCount不变
@Found:
POP EBP
POP EBX
POP EDI
POP ESI
end;
function FastPosNoCase(Tag, Src: PChar; TagCount: Integer;
var SrcCount: Integer): PChar; assembler;
asm
PUSH ESI
PUSH EDI
PUSH EBX
MOV EDI, EAX // EAX = Tag
MOV ESI, EDX // EDX = Src
MOV EDX, SrcCount
XCHG EDX, ECX // 结果: ECX = SrcCount EDX = TagCount
DEC EDX // 找到第一个字符后需要匹配的个数
JL @NotFound // TagCount <= 0 ?
OR ECX, ECX // SrcCount <= 0 ?
JLE @NotFound
SUB ECX, EDX
JLE @NotFound // SrcCount < TagCount ?
MOV AH, [EDI] // 取第一个字符进行查找
INC EDI
CMP AH, 97
JB @StartScan // < 'a' ?
CMP AH, 122 // > 'z' ?
JA @StartScan
SUB AH, 32 // 转大写
@StartScan:
OR ECX, ECX
JZ @NotFound
LODSB
CMP AL, 97
JB @@1
CMP AL, 122
JA @@1
SUB AL, 32 // 转大写
@@1:
CMP AH, AL
JZ @CmpStr
DEC CX
JNZ @StartScan
JMP @NotFound
@CmpStr:
DEC ECX
MOV EBX, EDX
SHL EAX, 16 // 保存AH
@ContinueCompare:
MOV AH, [ESI + EBX]
CMP AH, 97
JB @@2
CMP AH, 122
JA @@2
SUB AH, 32
@@2:
MOV AL, [EDI+EBX]
CMP AL, 97
JB @@3
CMP AL, 122
JA @@3
SUB AL, 32
@@3:
CMP AH, AL
JNZ @FindNext
DEC EBX
JG @ContinueCompare
@Found:
DEC ECX
MOV SrcCount, ECX
MOV EAX, ESI
DEC EAX
JMP @OutPoint
@NotFound:
XOR EAX, EAX
JMP @OutPoint
@FindNext:
SHR EAX, 16 // 恢复AH
JMP @StartScan
@OutPoint:
POP EBX
POP EDI
POP ESI
end;
function FastReplace(var Target: string; FindStr: string; ReplaceStr: string;
CaseSensitive: Boolean = True): Integer;
// Target: 需要进行替换操作的字符串
// FindStr: 要替换的字符串
// ReplaceStr: 替换成
// CaseSensitive: 是否大小写区分, 默认True (速度快)
// 返回值: 共替换了几个字符串
var
MaxCnt: Integer; // 保存匹配位置缓冲区的大小
MatchPoses: array of Integer; // 缓冲区, 保存所有匹配的位置
AdjustLen: Integer; // 最后需要调整Target的大小
n, // 当前匹配位置
l1, // Target的原始长度
l2, // FindStr的长度
l3, // ReplaceStr的长度
l: Integer; // 当前剩余需要匹配字符数
Proc: TFastPosProc; // 查找过程(大小写区分用FastPos, 不区分用FastPosNoCase)
begin
Result := 0;
l1 := Length(Target);
l2 := Length(FindStr);
l3 := Length(ReplaceStr);
if (l1 = 0) or (l2 = 0) then Exit;
AdjustLen := 0;
MaxCnt:=0;
l := l1;
if CaseSensitive then
Proc := @FastPos
else
Proc := @FastPosNoCase;
// 找出所有匹配字符串的位置, 并填到MatchPoses数组中
n := Integer(PChar(Target));
while (n <> 0) do
begin
n := Integer(Proc(PChar(FindStr), Ptr
, l2, l));
if n <> 0 then
begin
if Result >= MaxCnt then // 超过缓冲区大小?
begin
MaxCnt := MaxCnt + 256; // 扩展缓冲区
SetLength(MatchPoses, MaxCnt);
end;
MatchPoses[Result] := n + AdjustLen; // 完成替换后在字符串中的位置
Inc(AdjustLen, l3 - l2);
Inc(Result);
Inc(n, l2);
end;
end;
if Result = 0 then Exit; // 未找到匹配
if AdjustLen > 0 then // 新字串比老字串长
begin
SetLength(Target, l1 + AdjustLen); // 分配内存
// 因为新字串比老字串长
// 因此复制字串时将从尾部开始
asm
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP // 保存现场
MOV EAX, ReplaceStr
ADD EAX, L3
DEC EAX // ReplaceStr的结尾地址
MOV EBP, EAX // 保存
MOV EDX, MatchPoses // EDX将指向MatchPoses中最后一个替换
MOV EAX, Result
MOV EBX, EAX // EBX 保存需要替换的次数
DEC EAX
SHL EAX, 1
SHL EAX, 1
ADD EDX, EAX // EDX 定位到MatchPoses[Result-1]
MOV ESI, Target
ADD ESI, L1
DEC ESI // 原Target结尾处
MOV EDI, ESI
ADD EDI, AdjustLen // 新Target结尾处
STD // 设置方向标志: 从尾到头
@@1:
MOV ECX, EDI
LEA EAX, [EDX]
ADD EAX, L3
DEC EAX
SUB ECX, EAX // ECX为两个要替换字符串中间的字符数
MOV EAX, ECX
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EAX
AND ECX, 3
REP MOVSB // 复制原字符串
MOV EAX, ESI // 保存当前原串位置到EAX
SUB EAX, L2 // 调整位置跳过需要被替换的字符串
MOV ESI, EBP // ESI指向替换成的字符串
MOV ECX, L3 // 长度
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, L3
AND ECX, 3
REP MOVSB // 复制新串到相应位置
SUB EDX, 4 // 下一个要替换的位置
MOV ESI, EAX // 恢复原串位置
DEC EBX
JNZ @@1 // 替换已完成?
// 因为是倒序, 所以第一个需要
// 替换的字符串前的原始串不会
// 改变, 没有必要再进行复制
CLD // 清方向标志
POP EBP
POP EBX
POP EDI
POP ESI // 恢复现场
end;
end
else if AdjustLen < 0 then // 因为是缩短Target, 所以先进行
// 替换与复制, 然后再调整内存
begin
asm
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP // 保存现场
MOV EAX, ReplaceStr
ADD EAX, L1
ADD EAX, AdjustLen
PUSH EAX // 保存字符串的结尾(为计算最后一次替换
// 后还需要复制多少字节做准备)
MOV EBX, Result
MOV EDX, MatchPoses // EDX指向MatchPoses[0]
LEA ESI, [EDX]
MOV EDI, ESI
JMP @@2 // 第一次替换发生前的部分不需要进行复制
@@1:
LEA ECX, [EDX]
SUB ECX, EDI // 两次替换之间需要复制的字符数
MOV EAX, ECX
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EAX
AND ECX, 3
REP MOVSB // 复制
@@2:
MOV EAX, ESI
ADD EAX, L2 // 跳过原串中被替换字符串后的位置保存在EAX
MOV ESI, ReplaceStr
MOV ECX, L3
MOV EBP, ECX
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EBP
AND ECX, 3
REP MOVSB // 替换
MOV ESI, EAX // 恢复ESI到下一次需要移动或替换的位置
ADD EDX, 4 // EDX指向MatchPoses[i+1]
DEC EBX // 所有替换都已经完成?
JNZ @@1
POP ECX
SUB ECX, EDI // 最后一次替换后需要复制的字节数
MOV EBP, ECX
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EBP
AND ECX, 3
REP MOVSB // 复制
POP EBP
POP EBX
POP EDI
POP ESI // 恢复现场
end;
SetLength(Target, l1 + AdjustLen);
end
else begin // 字符串长度不变时
// 只需要进行替换而不必复制了
asm
PUSH ESI
PUSH EDI
PUSH EBX
PUSH EBP
MOV EBX, ReplaceStr //EBX 保存ReplaceStr地址
MOV EDX, MatchPoses
MOV EAX, Result
MOV EBP, L3
@@1:
LEA EDI, [EDX]
MOV ESI, EBX // 比 MOV ESI, ReplaceStr快
MOV ECX, EBP
SHR ECX, 1
SHR ECX, 1
REP MOVSD
MOV ECX, EBP
AND ECX, 3
REP MOVSB
ADD EDX, 4
DEC EAX
JNZ @@1
POP EBP
POP EBX
POP EDI
POP ESI
end;
end;
end;