web-dev-qa-db-fra.com

Comment comparer les extrémités des chaînes en C?

Je veux m'assurer que ma chaîne se termine par ".foo". J'utilise C, un langage que je ne connais pas totalement. La meilleure façon que j'ai trouvée de le faire est ci-dessous. Tous les gourous du C veulent s’assurer que je le fais avec élégance et sagesse?

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

N'appelez pas strlen plus d'une fois par chaîne.

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: ajout d'une vérification NULL pour le pédant. Pour l'ultra pédant, discutez s'il doit renvoyer non nul si str et le suffixe sont tous deux NULL.

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

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

  return( -1 );
}

Retourne 0 si se termine par ".foo".

8
user82238

Je n'ai pas accès à un compilateur pour le moment, alors quelqu'un pourrait-il me dire si cela fonctionne?

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

Quoi qu'il en soit, veillez à qualifier le paramètre par const, il indique à tout le monde (y compris le compilateur) que vous n'avez pas l'intention de modifier la chaîne.

7
Bastien Léonard

Si vous pouvez modifier la signature de votre fonction, essayez de la remplacer par

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

Cela se traduira par un code plus sûr, plus réutilisable et plus efficace:

  1. Les qualificatifs const ajoutés veilleront à ne pas altérer par erreur les chaînes en entrée. Cette fonction est un prédicat, donc je suppose qu’elle n’est jamais censée avoir d’effets secondaires.
  2. Le suffixe à comparer est transmis en tant que paramètre. Vous pouvez donc enregistrer cette fonction pour une utilisation ultérieure avec d'autres suffixes.
  3. Cette signature vous donnera l’occasion de passer les longueurs des chaînes si vous les connaissez déjà. Nous appelons cela programmation dynamique .

Nous pouvons définir la fonction comme suit:

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

Le contre-argument évident pour les paramètres supplémentaires est qu'ils impliquent plus de bruit dans le code, ou un code moins expressif.

3
wilhelmtell

Les strlen(".foo")s ne sont pas nécessaires. Si vous voulez vraiment le rendre flexible, vous pouvez utiliser sizeof ".foo" - 1 - une constante de temps de compilation.

En outre, une vérification de chaîne nulle serait bien.

2
dirkgently

Le code testé inclut le test:

#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;
}
2
stesch

Désolé, je suis un peu en retard à la fête. Ne pourriez-vous pas faire quelque chose avec un calcul mathématique simple?

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

Les pointeurs de caractères fonctionnent en pointant sur le premier caractère de leur tableau. Ainsi, lorsque vous faites cela, vous définissez le premier caractère du test comme le "." dans '.foo' (si c'est ce qu'il contient). C'est aussi pourquoi vous n'avez pas besoin d'allouer de mémoire car c'est juste le tableau de caractères existant.

1
bluemoongem

Peut être...

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");

Soit dit en passant, la solution proposée dans la question initiale n’est pas différente des appels strlen répétés.

0
Dan Olson

Je vérifie toujours les fonctions de chaîne de Glib, elles ont toutes sortes de bits utiles. Une fonction de vérification de suffixe existe déjà.

gchar * str;

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

Je suis un peu nouveau chez C, alors je m'excuse si ce n'est pas à 100% ... mais ça ressemble à une solide clause de garde!

0
J. M. Becker

S'il y a toujours quelque chose au-delà du point, nous pourrions nous livrer à une arithmétique de pointeur:

int EndsWithFoo (char *str)
{
   int iRetVal = 0;
   char * pchDot = strrchr (str, '.');

   if (pchDot)
   {
      if (strcmp (pchDot+1, "foo") == 0)
      {
         iRetVal = 1;
      }
   }
   return iRetVal;
}

Bien sûr, vous voudrez probablement ajouter un peu de strlen à vérifier ici est quelque chose au-delà du point :-)

NB - Je n'ai pas vérifié cela, mais ça me semble correct.

0
Bob Moore

Vous pouvez aussi généraliser comme ceci:

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

Pas si différent de cette question, à propos du test d’un suffixe de chaîne:

Comment puis-je vérifier si une chaîne se termine par ".csv" en C

0
MikeW

Solution générale avec un strlen (aiguille), strstr () et testez '\ 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
lumpidu
#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
Echo Liao

C'est la réponse la plus efficace (pour un ordinateur) que vous trouverez ici.

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

Je voudrais utiliser ma version:

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
wqwu