求一个ANSI字符的不管大小写最快的检索函数

danscort2000 2007-03-29 01:18:17
想在一个ANSI 字符串中检索一个子字符串
不区分大小写,也就是大写和小写看作一个字符
目前使用的方法是先拷贝,然后全部转换到小写,再调用strstr来进行检索
感觉效率太低,
有没有高手能提供一个高效率的函数?或者思路也行
不要使用CString string等,全部采用 char 指针进行操作,非常感谢
...全文
401 26 打赏 收藏 转发到动态 举报
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
ok1234567 2007-04-05
  • 打赏
  • 举报
回复
int main(int argc, char* argv[])
{
char str[20]="AbcdAbcDaabb";
char strsrc[100]="1abcab2dacababcdabcdaabbbcabcacab";
char *p=str;
LARGE_INTEGER b,e;
QueryPerformanceCounter(&b);
for(int x=0;x<1000;x++)
{
p=AsmStrStrI(strsrc,str);
//p=stristr(strsrc,str);
}
QueryPerformanceCounter(&e);
printf("%s\r\n%d", p,e.QuadPart-b.QuadPart);

getchar();
return 0L;

}
我的测试结果:
AsmStrStrI:548064
stristr:857976
大致为:5:8
汇编代码快很多!


ok1234567 2007-04-05
  • 打赏
  • 举报
回复
楼主说得对,如果使用for代替while,那么就可以优化寄存器使用,效率进一步提高
与汇编码几乎没有什么差距了,1000次运算
汇编:C 略等于44:45
对于c还是得有信心:)

不过,汇编还是快,这是没有办法改变的!
ok1234567 2007-04-05
  • 打赏
  • 举报
回复
char *AsmStrStrI(char *p1, char *p2)
{
__asm
{
mov ecx, p1
mov edx, p2
StartLoop:
mov al, [ecx]
mov ah, [edx]
inc ecx
inc edx

cmp al,0
je EndLoop
cmp ah,0
je EndLoop

cmp al, 'a'
jb Next
cmp al, 'z'
ja Next
and al, 0xDF
Next:
cmp ah, 'a'
jb Compare
cmp ah, 'z'
ja Compare
and ah, 0xDF
Compare:
cmp al, ah
je StartLoop
EndLoop:
cmp ah, 0
je Found
cmp al, 0
je NotFound
inc p1
mov ecx, p1
mov edx, p2
jmp StartLoop
NotFound:
mov p1, 0
Found:
}


return p1;
}


char * stristr(char *src,char *sub)
{
if (*src == 0)
{
if (*sub) return NULL;
return src;
}

char c;
size_t i=0;
while(sub[i])
{
if(sub[i] >= 'a' && sub[i] <= 'z') sub[i] &=0xDF;
i++;
}

while (*src)
{
i=0;
while (1)
{
if(sub[i] == 0) return src;
c=src[i];
if(c >= 'a' && c <= 'z') c &= 0xDF;
if( sub[i] != c) break;
i++;
}
src++;
}
return NULL;
}


int main(int argc, char* argv[])
{
int i;
char str[20]="AbcdabcDaabb";
char strsrc[200]="Abcab2daCababcdabcdaABBBCABCAcab";
char *p=str;
LARGE_INTEGER b,e;
QueryPerformanceCounter(&b);
for(int x=0;x<100;x++)
{
p=AsmStrStrI(strsrc,str);
//p=stristr(strsrc,str);
}
QueryPerformanceCounter(&e);
printf("%s\r\n%d", p,e.QuadPart-b.QuadPart);

getchar();
return 0L;

}

在release状态下:
AsmStrStrI:45264
stristr:50112
差异被缩小很多

使用and 比or(+)要快一些
ok1234567 2007-04-05
  • 打赏
  • 举报
回复
昨晚的测试忽略了发行版本,用的是 debug
如果用release,差异没有那么大,大概为4:5
danscort2000 2007-04-05
  • 打赏
  • 举报
回复
今天有想到了,
应该首先计算SUBSTR的长度
用FOR来代替WHILE
这样可以省略字符0的判断,又能提高效率了
ok1234567 2007-04-05
  • 打赏
  • 举报
回复
期望看到更优化的代码,毕竟不区分大小写的字串比较是很常用的
目前该c函数在测试中要快于chehw(chehw)的汇编代码
只有在pat字串特别短,并且直接匹配时略逊

char * stristr(char *src,char *pat)
{
int len=0;
for(;pat[len];len++)
{
if(pat[len] >= 'a' && pat[len] <= 'z') pat[len] &=0xDF;
}

int i=0;
char c;
while (*src)
{
for(i=0;i<len;i++)
{
c=src[i];
if(c >= 'a' && c <= 'z') c &= 0xDF;
if( pat[i] != c) break;
}
if(i == len) return src;
src++;
}

return NULL;
}
danscort2000 2007-04-05
  • 打赏
  • 举报
回复
这个算法还可以优化
用两个FOR代替WHILE
前面首先计算串长度
子串首先全部大写或者小写
这样比ASM这个函数速度还快
chehw 2007-04-04
  • 打赏
  • 举报
回复
在P4 2.66G 512M内存的计算机上测试,在有其他若干程序(包括装了XP的虚拟机)运行的状态下:

对s1和s2两个字符串比较100万次
AsmStrStrI平均用时260毫秒
CStrStrI平均用时480毫秒
chehw 2007-04-04
  • 打赏
  • 举报
回复
//** 测试如下
char *AsmStrStrI(char *p1, char *p2)
{
__asm
{
mov ecx, p1
mov edx, p2
StartLoop:
mov al, [ecx]
mov ah, [edx]
inc ecx
inc edx

cmp al,0
je EndLoop
cmp ah,0
je EndLoop

cmp al, 'a'
jb Next
cmp al, 'z'
ja Next
and al, 11011111b
Next:
cmp ah, 'a'
jb Compare
cmp ah, 'z'
ja Compare
and ah, 11011111b
Compare:
cmp al, ah
je StartLoop
EndLoop:
cmp ah, 0
je Found
cmp al, 0
je NotFound
inc p1
mov ecx, p1
mov edx, p2
jmp StartLoop
NotFound:
mov p1, 0
Found:
}


return p1;
}


char *CStrStrI(char *str1, char *str2)
{
char c1, c2;
char *p1, *p2;
while(1)
{
p1=str1;
p2=str2;
while((c1=*p1++) && (c2=*p2++))
{
if(c1>='a' && c1<='z') c1&=~0x20;
if(c2>='a' && c2<='z') c2&=~0x20;
if(c1!=c2) break;
}
if(c2==0) return str1;
if(c1==0) return NULL;
str1++;
}
return NULL;
}

int main(int argc, char* argv[])
{
char s1[100]="aaaaaaabHello World 12345asdfaf";
char s2[100]="HELLO WORLd 12345";



char *p1;
char *p2=s2;
DWORD dwTime;



int i,j;

for(j=0;j<10;j++)
{
dwTime=GetTickCount();
for(i=0;i<1000000;i++)
p1=AsmStrStrI(s1,s2);
dwTime=GetTickCount()-dwTime;
printf("time ellapse1=%ld(%s)\r\n", dwTime, p1);
}

for(j=0;j<10;j++)
{
dwTime=GetTickCount();
for(i=0;i<1000000;i++)
p1=CStrStrI(s1, s2);
dwTime=GetTickCount()-dwTime;
printf("time ellapse1=%ld(%s)\r\n", dwTime, p1);
}


getchar();
return 0;
}
ok1234567 2007-04-04
  • 打赏
  • 举报
回复
使用汇编,可以控制寄存器的操作,确保减少数据io,效率应该最好
而c语言,只能依赖编译器的优化
希望看到测试报告
danscort2000 2007-04-04
  • 打赏
  • 举报
回复
我明天测试看看
流程是一样的,但是大小写转化,好象你比用C我的要快那么一点
我是用+来实现的
chehw 2007-04-04
  • 打赏
  • 举报
回复
我测试过了,比直接用C++写的快一倍, 而且还可以再进一步优化.

and ah/al, 11011111b  这是大小写转换的一种高效方法
danscort2000 2007-04-04
  • 打赏
  • 举报
回复
楼上的,你这个插入ASM的检索函数测试过吗?
and ah/al, 11011111b  <-这样就能转换CASE了?

chehw 2007-04-03
  • 打赏
  • 举报
回复
//再贴一个
char *AsmStrStrI(char *p1, char *p2) // p1=source
{
char c=0;
while(*p1)
{
__asm
{
mov ecx, p1
mov edx, p2
StartLoop:
mov al, [ecx]
mov ah, [edx]
inc ecx
inc edx

cmp al,0
je EndLoop
cmp ah,0
je EndLoop

cmp al, 'a'
jb Next
cmp al, 'z'
ja Next
and al, 11011111b
Next:
cmp ah, 'a'
jb Compare
cmp ah, 'z'
ja Compare
and ah, 11011111b
Compare:
cmp al, ah
je StartLoop
EndLoop:
mov c, ah
}
if(c==0)
{
return p1;
}
p1++;
}
return NULL;
}
danscort2000 2007-04-03
  • 打赏
  • 举报
回复
楼上的,
你这个KMP函数比慢还慢了,在全文检索中可能有用
但是在我这个场合,不适合,谢谢
chehw 2007-04-03
  • 打赏
  • 举报
回复

int f[100];
void CalcF(char *p, int cb)
{
int j;
int i;
f[0]=-1;
for(j=1;j<cb;j++)
{
i=f[j-1];
while((*(p+j)!=*(p+i+1)) && (i>=0)) i=f[i];
if(*(p+j)==*(p+i+1)) f[j]=i+1;
else f[j]=-1;
}
}

int UpperCaseKMPFind(char *src, char *pat)
{
int cbsrc=lstrlen(src);
int cbpat=lstrlen(pat);
int sPos=0, pPos=0;
char ch;
while((pPos<cbpat) && (sPos<cbsrc))
{
ch=src[sPos];
if(ch>='a' && ch<='z') ch&=~0x20;
if(pat[pPos]==ch)
{
pPos++;
sPos++;
}else
{
if(pPos==0) sPos++;
else pPos=f[pPos-1]+1;
}
}
if(pPos<cbpat) return -1;
return sPos-cbpat;
}

char *UpperCase(char *pstr)
{
char ch;
char *p=pstr;
while(ch=*p)
{
if(ch>='a' && ch<='z')
{
ch&=~0x20;
*p=ch;
}
p++;
}
return pstr;
}

//用法示例
int main(void)
{
int i;
char str[20]="AbcdAbcDaabb";
UpperCase(str); //先将模式串改为大写

char strsrc[100]="1abcab2dacababcdabcdaabbbcabcacab";
int cb=strlen(str);
CalcF(str, cb); //计算failure因子

for(i=0;i<cb;i++)
{
printf("%c\t%d\r\n", str[i], f[i]);

}

int pos=UpperCaseKMPFind(strsrc, str);
if(-1!=pos)
printf("pos=%ld\r\n%s\r\n", pos, &strsrc[pos]);

getchar();
return 0L;
}
danscort2000 2007-04-03
  • 打赏
  • 举报
回复
继续上去
danscort2000 2007-03-31
  • 打赏
  • 举报
回复
顶,又想到一个效率更高的方法,也不复杂,明显实现一下
WingForce 2007-03-30
  • 打赏
  • 举报
回复
前段时间写的

#include <stdio.h>

#define is_lower( ch ) ( (ch) >= 'a' && (ch) <= 'z' )
#define is_upper( ch ) ( (ch) >= 'A' && (ch) <= 'Z' )
#define is_letter( ch ) ( is_lower( ch ) || is_upper( ch ) )
#define upper( ch ) ( is_lower( ch ) ? (ch) : ((ch) + 'a' - 'A') )
#define is_fit( ch1, ch2 ) ( (ch1) == (ch2) || upper( ch1 ) == upper( ch2 ) )


int handler( char* tarstr, char* substr )
{
while( 1 )
{
if( *substr == '\0' )
{
return 1;
}

if( *tarstr == '\0' )
{
return 0;
}

if( !is_fit( *tarstr, *substr ) )
{
return 0;
}

tarstr ++;
substr ++;

}

return 1;
}


int find_sub_str( char tarstr[], char substr[] )
{
int tarpos = 0;

while( 1 )
{
if( tarstr[tarpos] == '\0' )
{
return -1;
}

if( handler( &tarstr[tarpos], substr ) )
{
return tarpos;
}

tarpos ++;
}

return tarpos;
}

char* test = "faiewajfwjfwfj";
char* testsub = "eWaJ";

int main()
{
printf( "%d\n", find_sub_str( test, testsub ) );
system( "PAUSE" );
return 0;
}
danscort2000 2007-03-30
  • 打赏
  • 举报
回复
谢谢各位,楼上的这个函数好象比较可行
因为我是比较上万个字符串,但都是比较短
加载更多回复(6)

16,473

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧