【问题标题】:strstr() function like, that ignores upper or lower casestrstr() 函数,忽略大小写
【发布时间】:2014-12-04 20:07:08
【问题描述】:

我有两个字符串。让我们说`

str1="One Two Three";

str2="two";

我想知道是否有任何函数可以检查第一个字符串中的第二个字符串是否匹配,并返回一个指向第一个匹配项的指针,例如 strstr(),但它不处理相同的字母,大写或小写,作为两个不同的字符。

在我的示例中,该函数应该在第一个字符串中找到与 str2 匹配的内容,尽管 "Two" 是大写的 "T"

【问题讨论】:

  • 你为什么不把它们都转换成小写/大写然后比较它们呢?
  • 到目前为止你有什么代码?你用的是什么编程语言?
  • 没有stristr() C 库函数,但您可以自己制作一个...
  • 我想过,但我不允许修改 str1,除了删除找到的给定字符串。我必须保持 str1 只是“一三”,因为它们在这里和如果我将所有字母都设为小写或大写,则会修改结果。
  • 复制字符串。

标签: c string


【解决方案1】:

来自strstr 的手册页:

STRSTR(3)           Linux Programmer's Manual           STRSTR(3)

NAME
       strstr, strcasestr - locate a substring

SYNOPSIS
       #include <string.h>

       char *strstr(const char *haystack, const char *needle);

       #define _GNU_SOURCE

       #include <string.h>

       char *<b><u>strcasestr</u></b>(const char *haystack, const char *needle);

DESCRIPTION
       The  strstr()  function  finds the first occurrence of the substring needle in
       the string haystack.  The terminating '\0' characters are not compared.

       <b>The strcasestr() function is like strstr(3), but  ignores  the  case  of  both
       arguments.</b>

RETURN VALUE
       These functions return a pointer to the beginning of the substring, or NULL if
       the substring is not found.

所以你要找的是strcasestr

【讨论】:

  • 我的手册页说,“strstr() 函数符合 C89 和 C99。strcasestr() 函数是非标准扩展。”
  • 它不在我的 Visual C 库中。
  • 谢谢,这正是我想要的。我没有想过要在男人身上搜索……下次我会从那个开始。 :D
  • @FredLarson :它不是 ISO C 库函数,它是 GNU C 库的一部分。 Microsoft 的不区分大小写的函数使用i 而不是case(例如stricmp()),但没有定义stristr()
  • @Clifford:是的,这对你来说是一个非标准的扩展。
【解决方案2】:

虽然一些编译器的 C 库包含标准字符串函数的不区分大小写版本的扩展,例如 GNU 的 strcasestr(),但即使包含这些函数的命名也不是标准化的。

克服缺乏标准实现的一种方法当然是实现您自己的:

char* stristr( const char* str1, const char* str2 )
{
    const char* p1 = str1 ;
    const char* p2 = str2 ;
    const char* r = *p2 == 0 ? str1 : 0 ;

    while( *p1 != 0 && *p2 != 0 )
    {
        if( tolower( (unsigned char)*p1 ) == tolower( (unsigned char)*p2 ) )
        {
            if( r == 0 )
            {
                r = p1 ;
            }

            p2++ ;
        }
        else
        {
            p2 = str2 ;
            if( r != 0 )
            {
                p1 = r + 1 ;
            }

            if( tolower( (unsigned char)*p1 ) == tolower( (unsigned char)*p2 ) )
            {
                r = p1 ;
                p2++ ;
            }
            else
            {
                r = 0 ;
            }
        }

        p1++ ;
    }

    return *p2 == 0 ? (char*)r : 0 ;
}

下面的测试代码输出:

Two Three
Two Three
NULL
cdefg
CDEFG
CdEfG
NULL
zzzz
NULL

zzzzz
NULL

int main(void) 
{
    char* test = stristr( "One TTwo Three", "two" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "One Two Three", "two" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "One wot Three", "two" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "abcdefg", "cde" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "ABCDEFG", "cde" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "AbCdEfG", "cde" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "1234567", "cde" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "zzzz", "zz" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "zz", "zzzzz" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "", "" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "zzzzz", "" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr( "", "zzzz" ) ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    test = stristr("AAABCDX","AABC") ;
    printf( "%s\n", test == 0 ? "NULL" : test  ) ;

    return 0;
}

【讨论】:

  • 我也陷入了试图表现得像 strstr("","zz"), strstr("zz", ""), strstr("","") 的极端案例中。
  • 更正了 "","""zzzz", "" 测试用例 - 按照标​​准 strstr() 均返回 str1
  • 它失败了stristr("AAABCDX","AABC")
  • @BernardoRamos:谢谢 - 已修复。当检测到不匹配时,p1 现在重置为紧跟在初始匹配 (r + 1) 之后的字符。添加了测试用例。现在也是 const-correct。
  • @Clifford:问题很简单:如果 char 已签名并且字符串包含字符 tolower((unsigned char)*p1)。不幸的是,char 可以被签名,这与getc()strcmp() 的行为不一致,但是这个历史选择不能改变,但它对&lt;ctype.h&gt; 函数的影响是可以避免的。在我的国家,字符串包含非 ASCII 字符是相当普遍的,即使特定于语言环境的行为并不完美,也应避免未定义的行为。
【解决方案3】:

如果您使用的是 Windows,则可以使用 StrStrI。它与此处其他答案中的 GNU strcasestr 或其他手动实现的 stristr 代码相同。

例如:

const char needle[] = "and";
const char haystack[] = "me and you";

const char* pAnd = StrStrIA(haystack, needle); // explicitly call ascii version as windows defaults to wchar
printf("%s\n", pAnd); // Prints "and you";

【讨论】:

  • 好了!花了这么长时间才找到这个?!
【解决方案4】:

接受回答后

@Clifford@Weather Vane 的启发,我想尝试推出一个仅使用标准库函数的解决方案。

char* stristr3(const char* haystack, const char* needle) {
  do {
    const char* h = haystack;
    const char* n = needle;
    while (tolower((unsigned char) *h) == tolower((unsigned char ) *n) && *n) {
      h++;
      n++;
    }
    if (*n == 0) {
      return (char *) haystack;
    }
  } while (*haystack++);
  return 0;
}

strstr() 的极端情况与"x","""","x""","" 等输入相匹配有点棘手

【讨论】:

  • 很好 - 我已经更正了我的输出,以产生与您相同的输出。你的可能更简洁一些。强制转换是不必要的(tolower() 需要 int - 传递 char 是安全且正常的),并且 C 中 strstr() 的签名是 char* strstr( char*, const char* ),因此如果您不需要在返回中进行强制转换使用它。
  • @Clifford C11 7.4 字符处理说“参数是int,其值应表示为unsigned char或应等于宏EOF的值。如果参数有任何其他值,行为未定义”因此,如果 char 已签名且 ch &lt; 0,则将其传递给 tolower(ch) 会导致 ch 保持负数并且未映射到 unsigned char 范围- 因此UB。通过第一次强制转换 (unsigned char) ch,代码确保将非负值传递给 tolower()
  • 我想知道无符号字符的必要性,因为字母都低于 ASCII 值 128。然后我用 "Über" 和 "über" 尝试了它,但它无论如何都不起作用,因为不像英文大写/小写字母,ASCII 值 129 和 154 不相隔 32。
  • @Weather Vane 当char 值是非 ASCII (0-127) 时,各种问题都会发挥作用。当您说“我用“Über”和“über”尝试过,但它仍然不起作用”时,“它”是什么?
  • @Weather Vane 要很好地处理“Über”和“über”,需要字符代码页和匹配的setlocale()。顺便说一句,我的 Üü 值不同,相差 32。坦率地说,C 在这方面很弱,我怀疑 UTF8 最终会接管所有像这样过时的char 代码。
【解决方案5】:

这是一个稍微高效的版本,它不会在 haystack 字符串中的每个字符调用两次 tolower()

#include <ctype.h>

char *stristr4(const char *haystack, const char *needle) {
    int c = tolower((unsigned char)*needle);
    if (c == '\0')
        return (char *)haystack;
    for (; *haystack; haystack++) {
        if (tolower((unsigned char)*haystack) == c) {
            for (size_t i = 0;;) {
                if (needle[++i] == '\0')
                    return (char *)haystack;
                if (tolower((unsigned char)haystack[i]) != tolower((unsigned char)needle[i]))
                    break;
            }
        }
    }
    return NULL;
}

【讨论】:

  • O() 和 that 都是 O(h_len * n_len) 真正更高效的方法是使用 O(h_len + n_len) 方法。
  • @chux:我同意,而且我写的效率略高O(h_len + n_len) 的替代版本具有设置成本,在大多数情况下会使其变慢。 O(h_len * n_len) 是最坏的情况,只发生在病态参数字符串中。
  • 我的测试表明这明显更快(并且功能相同)。
  • 我发现最有趣的是,在测试之前使用 tolower() 值预先填充 int tl[256],可以将速度提高 4 倍。
  • @chux:你运行了多少测试?使用 tolower() 值预先填充数组会将 ctype 表加载到缓存中,但这只会影响少数初始情况的时间。 tolower() 受到您当前语言环境设置的影响。
【解决方案6】:

stristr()的实现

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

char *stristr (const char *str, const char *strSearch) {
    char *sors, *subs, *res = NULL;
    if ((sors = strdup (str)) != NULL) {
        if ((subs = strdup (strSearch)) != NULL) {
            res = strstr (strlwr (sors), strlwr (subs));
            if (res != NULL)
                res = str + (res - sors);
            free (subs);
        }
        free (sors);
    }
    return res;
}

int main()
{
    char *str1 = "One Two Three";
    char *str2 = "two";
    char *sptr = stristr(str1, str2);
    if (sptr)
        printf ("Substring is at index %d\n", sptr - str1);
    return 0;
}

【讨论】:

  • 一个有点“昂贵”的实现。
  • 这是一个完整的实现,为此 +1。由于它适用于尚未完全准备好它的系统,因此仅使用标准 C 函数来实现它也是有意义的,而不是依赖于系统也可能没有的其他函数,例如 strdup()strlwr()
  • 谢谢...也可以写strdup()strlwr() ;-)
  • 我用输入 "One TTwo Three", "two" 测试了(到目前为止)三个实现的性能,调用了 100000 次。显然平台各不相同,所以这些是可比较的:我的:15 毫秒,@chux:16 毫秒,你的:172 毫秒。但是,当我在 str1 前加上 94 个不匹配的字符时,结果分别为 250 毫秒、125 毫秒、218 毫秒,所以你的对输入变化最不敏感,chux 在所有情况下都有最高的性能,而我的可扩展性很差!跨度>
  • @Clifford & Weather Vane 我想知道如果我们使用上而不是下可能会得到的结果,因为我怀疑一旦使用非 AZ 字母它可能会有所不同。我遇到了 stricmp() 的移植问题,其中一个操作系统使用 toupper,而另一个操作系统使用 tolower。当涉及'_' 时,排序混乱。
【解决方案7】:

在不编写任何函数的情况下解决此问题的最佳方法可能是首先使用“tolower”/“toupper”将字符串转换为小写/大写,然后使用“strstr”:)

【讨论】:

  • 但是大写/小写的转换最好在一个函数中完成——所以你将编写一个函数。无论如何写一个函数有什么问题?
  • 是的..我说的是标准库..因为它已经优化了。
  • 转换字符串是不必要的开销——您首先必须复制它们,因此库优化变得无关紧要。您的建议正是 Weather Vane 建议的解决方案,我对此进行了一些性能分析并在 cmets 中添加了结果。无论哪种方式,它都不是一个简单的单线,所以你最好还是像 Weather Vane 的回答那样编写一个函数。 C 字符串库(和任何扩展)确实可以优化,但 C 字符串处理从根本上来说效率低下,strdup() 特别昂贵。
  • 您可以利用小写字符比大写相同字母高 32(十进制)这一事实。所以给定正确的位掩码,它们是相同的。有 1 位不同,这来自于 ASCII 从全大写发展到小写的时候。例如,60 年代的电传打字机都是大写的。
【解决方案8】:

试试这个function

char* stristr(const char* String, const char* Pattern)
{
      char *pptr, *sptr, *start;

      for (start = (char *)String; *start; start++)
      {
            /* find start of pattern in string */
            for ( ; (*start && (toupper(*start) != toupper(*Pattern))); start++)
                  ;
            if (!*start)
                  return 0;

            pptr = (char*)Pattern;
            sptr = (char*)start;

            while (toupper(*sptr) == toupper(*pptr))
            {
                  sptr++;
                  pptr++;
                  /* if end of pattern then pattern was found */
                  if (!*pptr)
                        return (start);
            }
      }
      return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-20
    • 2014-02-08
    • 2021-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多