Подтвердить что ты не робот

Как сравнить концы строк в C?

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

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

Ответ 1

Не вызывайте 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"); }

EDIT: добавлена ​​проверка NULL для педантичного. Для ультра-педантичного, обсудите, нужно ли ему возвращать ненулевое значение, если обе строки и суффикс равны как NULL.

Ответ 2

int EndsWithFoo( char *string )
{
  string = strrchr(string, '.');

  if( string != NULL )
    return( strcmp(string, ".foo") );

  return( -1 );
}

Вернется 0, если заканчивается на ".foo".

Ответ 3

У меня нет доступа к компилятору прямо сейчас, так может кто-нибудь сказать мне, если это работает?

#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

Если вы можете изменить подпись своей функции, попробуйте изменить ее на

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

Это приведет к более безопасному, более многоразовому и более эффективному коду:

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

Мы можем определить такую ​​функцию:

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

strlen(".foo") не требуется. Если вы действительно хотели иметь его гибким, вы можете использовать sizeof ".foo" - 1 - константу времени компиляции.

Кроме того, проверка нулевой строки будет хорошей.

Ответ 6

Протестированный код включает тест:

#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

Вы также можете обобщить следующее:

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;
}

Ответ 8

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

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.

Ответ 9

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

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

Ответ 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;
}

Ответ 11

Общее решение с одним 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;
}

Ответ 12

Жаль, что я немного опаздываю на вечеринку. Не могли бы вы что-то сделать с помощью простой математики указателя?

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

Указатели символов работают, указывая на первый символ в их массиве. Поэтому, когда вы это делаете, вы устанавливаете первый символ теста как ".". в '.foo' (если это то, что он содержит). Вот почему вам не нужно выделять память для него, поскольку она просто указывает на уже существующий массив символов.

Ответ 13

#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

Я всегда проверяю функции glib string, у них есть всевозможные полезные биты. Функция проверки суффиксов уже существует.

gchar * str;

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

Я немного новичок в C, поэтому я приношу свои извинения, если это не 100%... но для меня это выглядит как твердая оговорка.

Ответ 15

Я бы сделал это следующим образом:

/**
  * 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;
}

Ответ 16

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

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

EDIT в ответ на downvotes. Да, этот подход требует дополнительного процессора или памяти для реализации, но вопроситель не указывает на какие-либо такие ограничения, и он явно попросил элегантное решение. Обратное перемещение строк, тогда сравнение с фронтом гораздо более элегантно, чем возиться с поиском конца строк и работать назад. И это намного проще для следующего программиста, чтобы схватить и поддерживать тоже.