Как сравнить концы строк в C? - PullRequest
33 голосов
/ 13 апреля 2009

Я хочу убедиться, что моя строка заканчивается на ".foo". Я использую C, язык, с которым я не совсем знаком. Лучший способ сделать это - ниже. Любой гуру хочет убедиться, что я делаю это элегантно и мудро?

int EndsWithFoo(char *str)
{
    if(strlen(str) >= strlen(".foo"))
    {
        if(!strcmp(str + strlen(str) - strlen(".foo"), ".foo"))
        {
            return 1;
        }
    }
    return 0;
}

Ответы [ 18 ]

0 голосов
/ 08 июля 2015
#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
0 голосов
/ 11 сентября 2014

Я хотел бы использовать мою версию:

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;
}
0 голосов
/ 23 апреля 2015

Общее решение с одним 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;
}
0 голосов
/ 08 мая 2014

Я бы сделал это так:

/**
  * 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;
}
0 голосов
/ 13 апреля 2009

Я бы предложил лучший способ сделать это - перевернуть строку и сравнить первые n символов.

Существует множество примеров использования функций перестановки строк (даже Джоэл называет это стандартным вопросом для интервью), поэтому просто реализуйте одну из них, а затем выполните шаг, чтобы сравнить перевернутые строки.

РЕДАКТИРОВАТЬ в ответ на downvotes. Хорошо, да, этот подход требует дополнительного ЦП или памяти для реализации, но спрашивающий не указывает никаких таких ограничений, и он явно попросил элегантное решение. Перевернуть струны, сравнив их спереди, гораздо элегантнее, чем возиться с поиском конца струн и работать задом наперед. И следующему программисту будет намного легче понять и поддерживать тоже.

0 голосов
/ 13 апреля 2009

Если всегда есть что-то за точкой, мы могли бы потворствовать некоторой арифметике указателей:

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. Я не запускал его, чтобы проверить, но для меня все в порядке.

0 голосов
/ 13 апреля 2009

Может быть ...

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.

0 голосов
/ 13 апреля 2009

Вы также можете обобщить так:

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...