65.9K
CodeProject 正在变化。 阅读更多。
Home

不区分大小写的字符串搜索

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (13投票s)

1999 年 12 月 2 日

3分钟阅读

viewsIcon

154853

downloadIcon

1926

一个不需要更改字符串大小写,并且对DBCS(双字节字符集)友好的函数。

C运行时包含一个名为strstr的函数,当你想在一个主字符串中查找一个精确的子字符串时可以使用它。 要执行不区分大小写的搜索,必须首先强制将两个字符串的大小写都转换为小写或大写,然后再调用strstr。 我想要一个不需要更改字符串大小写,并且对DBCS(双字节字符集)友好的函数。

目前,Windows 2000只针对Intel平台,当然Windows 95/98也只在Intel平台上运行。 这使得汇编语言的使用比过去 NT 在 PowerPC 或 ALPHA CPU 上运行时更具吸引力。 Visual Studio 不包含单独的汇编器,如果您添加汇编器,调试器在调试汇编文件方面做得不太好。 因此,将汇编语言包含到您的项目中的最简单方法是使用 _asm 块。 在字符串搜索函数中,我让 C 编译器处理外层。

char* __fastcall stristrA(const char* pszMain, const char* pszSub)
{
}

函数内的所有内容都在 _asm 块内处理。 请注意,MSDN 有一篇文章说你不应该使用__fastcall调用约定来使用_asm块的函数,因为编译器可以使用任何寄存器作为函数参数。 MSDN 中还有另一篇文章指出,前两个变量总是放在 ECX 和 EDX 寄存器中。 我将相信第二篇文章而忽略第一篇,但如果您担心,您可以更改调用约定并从堆栈中加载我们使用的 ESI 和 EDI 寄存器。 __fastcall 调用约定的优点是不必从堆栈中推送或弹出任何内容,从而使函数执行速度更快。

我们做的第一件事是保存字符串第一个字符的小写和大写版本。 如果第一个字符是非字母,那么我们将最终检查它两次。 但是,这只会使我们每个字符额外花费 3 个时钟周期,但我们不必为每个字符调用 CharLower 的节省非常显着。

一旦我们设置了第一个字符,我们将 CharNext 函数的地址存储在 EDI 寄存器中。 这减少了实际调用的开销一半 - 并且我们为主字符串中的每个字符调用此函数(我们使用 CharNext 是因为它会正确地将指针移动到 DBCS 字符上)。 然后,我们遍历主字符串,针对子字符串的大写和小写第一个字符检查每个字符。 只要我们找到第一个字符匹配,我们就切换到一个循环,该循环针对主字符串中的当前位置检查子字符串中的每个字符。 如果我们不匹配,那么我们将主字符串和子字符串中的字符都更改为小写并再次比较。 在示例项目中,我们在字符串“What hath God wrought?”中搜索单词“god”。 我们找到了 'G' 的匹配项而无需调用 CharLower,并且能够匹配字符串的其余部分(“od”)而无需调用 CharLower。 当然,如果主字符串是“What hath GOD wrought?”,那么我们会为 'O' 和 'D' 调用 CharLower

源代码提供了 ANSI 和 Unicode 版本,分别称为 stristrAstristrW。 在你的头文件中,你可以指定你想要使用哪个版本,使用以下方式

#ifdef UNICODE
#define stristr stristrW
#else
#define stristr stristrA
#endif

char*  __fastcall stristrA(const char* psz1, const char* psz2);
WCHAR* __fastcall stristrW(const WCHAR* pszMain, const WCHAR* pszSub);

在你的代码中,你只需指定 stristr,并且会根据你是否正在为 Unicode 编译来选择正确的函数。

示例项目没有做任何有用的事情,但它确实调用了该函数的 ANSI 和 Unicode 版本,以便您可以在调试器中单步执行代码以查看其工作原理。 在 ANSI 函数中,设置以下观察点将更容易看到发生了什么

al
lowerch,c
(char*) esi,s
(char*) edi,s
pszTmp1,s
© . All rights reserved.