代码之家  ›  专栏  ›  技术社区  ›  Joe

如何比较c中字符串的结尾?

  •  33
  • Joe  · 技术社区  · 15 年前

    我想确保我的字符串以“.foo”结尾。我用的是C语言,这是一种我并不完全熟悉的语言。我发现最好的方法是下面的。有谁想让我做得优雅而明智?

    int EndsWithFoo(char *str)
    {
        if(strlen(str) >= strlen(".foo"))
        {
            if(!strcmp(str + strlen(str) - strlen(".foo"), ".foo"))
            {
                return 1;
            }
        }
        return 0;
    }
    
    19 回复  |  直到 6 年前
        1
  •  46
  •   plinth    15 年前

    每个字符串调用strlen的次数不要超过一次。

    int EndsWith(const char *str, const char *suffix)
    {
        if (!str || !suffix)
            return 0;
        size_t lenstr = strlen(str);
        size_t lensuffix = strlen(suffix);
        if (lensuffix >  lenstr)
            return 0;
        return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
    }
    
    int EndsWithFoo(const char *str) { return EndsWith(str, ".foo"); }
    

    编辑:为学究添加了空检查。对于极端学究,如果str和suffix都为空,则讨论是否应该返回非零。

        2
  •  8
  •   user82238    15 年前
    int EndsWithFoo( char *string )
    {
      string = strrchr(string, '.');
    
      if( string != NULL )
        return( strcmp(string, ".foo") );
    
      return( -1 );
    }
    

    如果以“.foo”结尾,则返回0。

        3
  •  7
  •   Bastien Léonard    15 年前

    我现在没有访问编译器的权限,有人能告诉我这是否有效吗?

    #include <stdio.h>
    #include <string.h>
    
    int EndsWithFoo(const char* s);
    
    int
    main(void)
    {
      printf("%d\n", EndsWithFoo("whatever.foo"));
    
      return 0;
    }
    
    int EndsWithFoo(const char* s)
    {
      int ret = 0;
    
      if (s != NULL)
      {
        size_t size = strlen(s);
    
        if (size >= 4 &&
            s[size-4] == '.' &&
            s[size-3] == 'f' &&
            s[size-2] == 'o' &&
            s[size-1] == 'o')
        {
          ret = 1;
        }
      }
    
      return ret;
    }
    

    无论如何,请确保将参数限定为 const ,它告诉每个人(包括编译器)您不打算修改字符串。

        4
  •  3
  •   wilhelmtell    15 年前

    如果您可以更改函数的签名,那么尝试将其更改为

    int EndsWith(char const * str, char const * suffix, int lenstr, int lensuf);
    

    这将产生更安全、更可重用和更高效的代码:

    1. 添加的常量限定符将确保您不会错误地更改输入字符串。这个函数是一个谓词,所以我认为它从来没有副作用。
    2. 将比较反后缀作为参数传递给您,这样您就可以保存这个函数,以便以后重用其他后缀。
    3. 如果您已经知道字符串的长度,这个签名将给您传递字符串长度的机会。我们称之为 dynamic programming .

    我们可以定义这样的函数:

    int EndsWith(char const * str, char const * suffix, int lenstr, int lensuf)
    {
        if( ! str && ! suffix ) return 1;
        if( ! str || ! suffix ) return 0;
        if( lenstr < 0 ) lenstr = strlen(str);
        if( lensuf < 0 ) lensuf = strlen(suffix);
        return strcmp(str + lenstr - lensuf, suffix) == 0;
    }
    

    这些额外参数的明显反论点是,它们意味着代码中有更多的噪声,或者是表达性较差的代码。

        5
  •  2
  •   dirkgently    15 年前

    这个 strlen(".foo") 不需要。如果你真的想让它有弹性,你可以使用 sizeof ".foo" - 1 --编译时常数。

    另外,空字符串检查也很好。

        6
  •  2
  •   stesch    15 年前

    测试代码,包括测试:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int ends_with_foo(const char *str)
    {
        char *dot = strrchr(str, '.');
    
        if (NULL == dot) return 0;
        return strcmp(dot, ".foo") == 0;
    }
    
    int main (int argc, const char * argv[]) 
    {
        char *test[] = { "something", "anotherthing.foo" };
        int i;
    
        for (i = 0; i < sizeof(test) / sizeof(char *); i++) {
            printf("'%s' ends %sin '.foo'\n",
                   test[i],
                   ends_with_foo(test[i]) ? "" : "not ");
        }
        return 0;
    }
    
        7
  •  1
  •   bluemoongem    9 年前

    对不起,我来晚了一点。 你不能用一些简单的指针数学做点什么吗?

    char* str = "hello.foo"; //this would be string given
    
    int x = 4; //.foo has 4 characters
    
    int n = strlen(str)- x; //where x is equal to suffix length
    
    char* test = &str[n]; //do some pointer math to find the last characters
    
    if(strcmp(test, ".foo") == 0){
        //do some stuff
    }// end if
    

    字符指针通过指向数组中的第一个字符来工作。因此,当您这样做时,您将test的第一个字符设置为“.”in.foo(如果它包含这个字符)。这也是为什么不需要为它分配内存,因为它只是指向已经存在的字符数组。

        8
  •  0
  •   Naveen    15 年前

    你也可以这样概括:

    int endsWith(const char* text, const char* extn)
    {
        int result = 1;
        int len = strlen(text);
        int exprLen = strlen(extn);
        int index = len-exprLen;
        int count = 0;
    
        if(len > exprLen)
        {
            for( ; count  < exprLen; ++count)
            {
                if(text[index + count] != extn[count])
                {
                    result = 0;
                    break;
                }
    
            }
        }
        else
        {
            result = 0;
        }
        return result;
    }
    
        9
  •  0
  •   Dan Olson    15 年前

    也许吧。。。

    bool endswith (const char* str, const char* tail)
    {
      const char* foo = strrstr (str, tail);
      if (foo)
      {
         const int strlength = strlen (str);
         const int taillength = strlen (tail);
         return foo == (str + strlength - taillength);
      }
      return false;
    }
    
    endswith (str, ".foo");
    

    顺便说一下,除了重复的strlen调用之外,原问题的解决方案看起来很好。

        10
  •  0
  •   Bob Moore    15 年前

    如果总是有一些超出点的东西,我们可以沉迷于一些指针算法:

    int EndsWithFoo (char *str)
    {
       int iRetVal = 0;
       char * pchDot = strrchr (str, '.');
    
       if (pchDot)
       {
          if (strcmp (pchDot+1, "foo") == 0)
          {
             iRetVal = 1;
          }
       }
       return iRetVal;
    }
    

    当然,你可能想在那里加一点strlen来检查一下 点以外的东西:-)

    NB-我没有运行这个来检查它,但它看起来很好。

        11
  •  0
  •   wqwu    10 年前

    我想使用我的版本:

    bool endsWith(const char *filename, const char *ext) {
        const uint len = strlen(filename);
        const uint extLen = strlen(ext);
        if (len < extLen) {
            return false;
        }
        for (uint index  = 1; index <= extLen; index++) {
            if (filename[len - index] != ext[extLen - index]) {
                return false;
            }
        }
        return true;
    }
    
        12
  •  0
  •   lumpidu    9 年前

    带一个strlen(针)、strstr()和测试“\0”的通用解决方案:

    #include <stdio.h>
    #include <string.h>
    #include <stdbool.h>
    
    bool endsWith(const char* haystack, const char* needle)
    {
        bool rv = false;
        if (haystack && needle)
        {
            size_t needle_size = strlen(needle);
            const char* act = haystack;
            while (NULL != (act = strstr(act, needle)))
            {
                if (*(act + needle_size) == '\0')
                {
                    rv = true;
                    break;
                }
                act += needle_size;
            }
        }
    
        return rv;
    }
    
    int main (int argc, char * argv[])
    {
        char *a = "file1.gz";
        char *b = "1.gz";
        char *c = NULL;
        char *d = "1.gzabc";
        char *e = "1.gzabc1.gz";
    
        printf("endsWith:\n");
        printf("%s %s = %d\n",a,b,endsWith(a,b));
        printf("%s NULL = %d\n",a,endsWith(a,c));
        printf("%s %s = %d\n",d,b,endsWith(d,b));
        printf("%s %s = %d\n",e,b,endsWith(e,b));
    
        return 0;
    }
    
        13
  •  0
  •   Echo Liao    9 年前
    #include <assert.h>
    #include <string.h>
    
    int string_has_suffix(const char *str, const char *suf)
    {
        assert(str && suf);
    
        const char *a = str + strlen(str);
        const char *b = suf + strlen(suf);
    
        while (a != str && b != suf) {
            if (*--a != *--b) break;
        }
        return b == suf && *a == *b;
    }
    
    // Test Unit
    int main (int argc, char *argv[])
    {
        assert( string_has_suffix("", ""));
        assert(!string_has_suffix("", "a"));
        assert( string_has_suffix("a", ""));
        assert( string_has_suffix("a", "a"));
        assert(!string_has_suffix("a", "b"));
        assert(!string_has_suffix("a", "ba"));
        assert( string_has_suffix("abc", "abc"));
        assert(!string_has_suffix("abc", "eeabc"));
        assert(!string_has_suffix("abc", "xbc"));
        assert(!string_has_suffix("abc", "axc"));
        assert(!string_has_suffix("abcdef", "abcxef"));
        assert(!string_has_suffix("abcdef", "abxxef"));
        assert( string_has_suffix("b.a", ""));
        assert( string_has_suffix("b.a", "a"));
        assert( string_has_suffix("b.a", ".a"));
        assert( string_has_suffix("b.a", "b.a"));
        assert(!string_has_suffix("b.a", "x"));
        assert( string_has_suffix("abc.foo.bar", ""));
        assert( string_has_suffix("abc.foo.bar", "r"));
        assert( string_has_suffix("abc.foo.bar", "ar"));
        assert( string_has_suffix("abc.foo.bar", "bar"));
        assert(!string_has_suffix("abc.foo.bar", "xar"));
        assert( string_has_suffix("abc.foo.bar", ".bar"));
        assert( string_has_suffix("abc.foo.bar", "foo.bar"));
        assert(!string_has_suffix("abc.foo.bar", "xoo.bar"));
        assert(!string_has_suffix("abc.foo.bar", "foo.ba"));
        assert( string_has_suffix("abc.foo.bar", ".foo.bar"));
        assert( string_has_suffix("abc.foo.bar", "c.foo.bar"));
        assert( string_has_suffix("abc.foo.bar", "abc.foo.bar"));
        assert(!string_has_suffix("abc.foo.bar", "xabc.foo.bar"));
        assert(!string_has_suffix("abc.foo.bar", "ac.foo.bar"));
        assert( string_has_suffix("abc.foo.foo", ".foo"));
        assert( string_has_suffix("abc.foo.foo", ".foo.foo"));
        assert( string_has_suffix("abcdefgh", ""));
        assert(!string_has_suffix("abcdefgh", " "));
        assert( string_has_suffix("abcdefgh", "h"));
        assert( string_has_suffix("abcdefgh", "gh"));
        assert( string_has_suffix("abcdefgh", "fgh"));
        assert(!string_has_suffix("abcdefgh", "agh"));
        assert( string_has_suffix("abcdefgh", "abcdefgh"));
    
        return 0;
    }
    
    // $ gcc -Wall string_has_suffix.c && ./a.out
    
        14
  •  0
  •   J. M. Becker    8 年前

    我总是检查glib字符串函数,它们有各种有用的位。后缀检查函数已存在。

    gchar * str;
    
    if (!g_str_has_suffix(str)) {
        return FALSE;
    }
    

    我在C有点新,所以如果这不是100%的话,我同意…但在我看来这是一个可靠的保护条款!

        15
  •  0
  •   static_cast    6 年前

    这是(对于计算机)最有效的答案。

    int endsWith(const char *string,const char *tail)
    {
    
        const char *s1;
    
        const char *s2;
    
        if (!*tail)
            return 1;
        if (!*string)
            return 0;
        for (s1 = string; *s1; ++s1);
        for (s2 = tail; *s2; ++s2);
        if (s1 - string < s2 - tail)
            return 0;
        for (--s1, --s2; *s1 == *s2 && s2 >= tail; --s1, --s2);
        if (s2 < tail)
            return 1;
        else
            return 0;
    }
    
        16
  •  -1
  •   Roberto Vera Alvarez    9 年前

    我想这样做:

    /**
      * Return 0 if the string haystack ends with the string needle
      * 
      * @param haystack the string to be analyzed
      * @param needle the suffix string
      * @return 0 if the string haystack ends with the string needle, 1 if not
    */
    int strbcmp(const char *haystack, const char *needle) {
        int length;
        if (haystack && needle && strlen(haystack) >= (length = strlen(needle)) && strlen(strstr(haystack, needle)) == length) return 0;
       return 1;
    }
    

    测试程序为:

    #include <stdio.h>
    #include <string.h>
    
    int strbcmp(const char *haystack, const char *needle) {
        int length;
        if (haystack && needle && strlen(haystack) >= (length = strlen(needle)) && strlen(strstr(haystack,needle)) == length) return 0;
        return 1;
    }
    
    int main (int argc, char * argv[]){
        char *a = "file1.gz";
        char *b = "1.gz";
        char *c = NULL;
        char *d = "1.gzabc";
    
        printf("%s %s = %d\n",a,b,strbcmp(a,b));
        printf("%s %s = %d\n",a,c,strbcmp(a,c));
        printf("%s %s = %d\n",d,b,strbcmp(d,b));
    
        return 0;
    }
    
        17
  •  -1
  •   Bi Ao    6 年前
    int strends(char* str, char* end){
        return strcmp(str + strlen(str) - strlen(end), end) == 0;
    }
    

    我发现这是达到目的最简单的方法。

        18
  •  -3
  •   Cruachan    15 年前

    我建议最好的方法是反转字符串,然后比较前n个字符。

    这里有很多字符串反转函数的例子(甚至joel将其作为一个标准的面试问题引用),所以只需实现其中一个,然后遍历反转的字符串进行比较。

    编辑以回应反对票。好的,是的,这个方法确实需要额外的cpu或内存来实现,但是发问者没有指出任何这样的约束,他明确要求一个优雅的解决方案。把琴弦颠倒过来,然后从前面进行比较,这远比瞎找琴弦的末端和向后工作要优雅得多。而且对于下一个程序员来说,掌握和维护也要容易得多。