88 votes

Comparaison de chaînes de caractères sans tenir compte de la casse en C

J'ai deux codes postaux char* que je veux comparer, en ignorant la casse. Existe-t-il une fonction permettant de faire cela ?

Ou dois-je passer en boucle chaque fois que j'utilise la fonction tolower et ensuite faire la comparaison ?

Une idée de la façon dont cette fonction réagira avec des chiffres dans la chaîne de caractères.

Merci

0 votes

Je pense que j'ai mal écrit cela, postcode n'est pas un type, juste la valeur réelle que le char* va contenir.

3 votes

Sur quelle plateforme êtes-vous ? De nombreuses plateformes disposent d'une fonction spécifique pour ce faire.

0 votes

Si vous comparez un chiffre avec une lettre, vous savez que les chaînes ne sont pas équivalentes, quelle que soit la casse.

67voto

larsmans Points 167484

Il n'y a aucune fonction qui fait cela dans le standard C. Les systèmes Unix qui se conforment à la norme POSIX doivent disposer de la fonction strcasecmp dans l'en-tête strings.h ; les systèmes Microsoft ont stricmp . Pour être du côté portable, écrivez le vôtre :

int strcicmp(char const *a, char const *b)
{
    for (;; a++, b++) {
        int d = tolower((unsigned char)*a) - tolower((unsigned char)*b);
        if (d != 0 || !*a)
            return d;
    }
}

Mais notez qu'aucune de ces solutions ne fonctionne avec les chaînes UTF-8, seulement avec les chaînes ASCII.

6 votes

Cette implémentation n'est pas correcte ; elle renvoie incorrectement 0 lorsque b est une sous-chaîne de a. Par exemple, elle renvoie 0 pour strcicmp("another", "an") alors qu'elle devrait renvoyer 1.

0 votes

Cela semble également très inefficace.

36 votes

C'est un mauvais conseil. Il n'y a aucune raison d'"écrire vos propres" fonctions texte standard C pour gérer une simple différence de nom. Faites #ifdef _WINDOWS ... #define strcasecmp stricmp ... #endif et mettez-le dans un en-tête approprié. Les commentaires ci-dessus, où l'auteur a dû corriger la fonction pour qu'elle fonctionne correctement, montrent pourquoi la réécriture de fonctions C standard est contre-productive si une solution beaucoup plus simple est disponible.

45voto

Mihran Hovsepyan Points 4644

Jetez un coup d'œil à strcasecmp() sur strings.h .

4 votes

Je pense que vous voulez dire int strcasecmp(const char *s1, const char *s2); dans strings.h

0 votes

Oui, c'est ce que je voulais dire :) mais peut-être avez-vous fait une erreur de frappe, pas dans stringS.h mais dans string.h.

2 votes

Cette fonction n'est pas standard ; Microsoft l'appelle stricmp . @entropo : strings.h est un en-tête pour la compatibilité avec les systèmes Unix des années 1980.

9voto

Zohar81 Points 1928

J'ai trouvé une méthode intégrée nommée from qui contient des fonctions de chaîne supplémentaires à l'en-tête standard.

Voici les signatures pertinentes :

int  strcasecmp(const char *, const char *);
int  strncasecmp(const char *, const char *, size_t);

J'ai également trouvé son synonyme dans le noyau xnu (osfmk/device/subrs.c) et il est implémenté dans le code suivant, donc vous ne vous attendez pas à avoir un changement de comportement en nombre par rapport à la fonction strcmp originale.

tolower(unsigned char ch) {
    if (ch >= 'A' && ch <= 'Z')
        ch = 'a' + (ch - 'A');
    return ch;
 }

int strcasecmp(const char *s1, const char *s2) {
    const unsigned char *us1 = (const u_char *)s1,
                        *us2 = (const u_char *)s2;

    while (tolower(*us1) == tolower(*us2++))
        if (*us1++ == '\0')
            return (0);
    return (tolower(*us1) - tolower(*--us2));
}

1 votes

Félicitations pour avoir mentionné la sécurité strncasecmp() fonction !

1 votes

strcasecmp() et strncasecmp() ne font pas partie de la bibliothèque C standard, mais sont des ajouts courants dans *nix.

0 votes

Notez qu'il n'y a aucune raison d'implémenter votre propre tolower() si vous compilez avec une implémentation conforme aux normes de compilateur/C - tolower() est une fonction obligatoire dans toutes les versions du standard C.

7voto

chux Points 13185

Autres pièges à éviter lorsque vous effectuez des comparaisons sans tenir compte de la casse :


Comparer en minuscules ou en majuscules ? (question assez courante)

Les deux ci-dessous renverront 0 avec strcicmpL("A", "a") et strcicmpU("A", "a") .
Pourtant, strcicmpL("A", "_") et strcicmpU("A", "_") peut retourner des résultats signés différents comme '_' se trouve souvent entre les majuscules et les minuscules.

Cela affecte l'ordre de tri lorsqu'il est utilisé avec qsort(..., ..., ..., strcicmp) . Les fonctions de la bibliothèque C non standard, comme les fonctions courantes de stricmp() ou strcasecmp() ont tendance à être bien définis et favorisent la comparaison avec les minuscules. Pourtant, des variations existent.

int strcicmpL(char const *a, char const *b) {
  while (*b) {
    int d = tolower(*a) - tolower(*b);
    if (d) {
        return d;
    } 
    a++;
    b++;
  } 
  return tolower(*a);
}

int strcicmpU(char const *a, char const *b) {
  while (*b) {
    int d = toupper(*a) - toupper(*b);
    if (d) {
        return d;
    } 
    a++;
    b++;
  } 
  return toupper(*a);
}

char peut avoir une valeur négative. (pas rare)

touppper(int) et tolower(int) sont spécifiés pour unsigned char et les valeurs négatives EOF . Plus loin, strcmp() renvoie les résultats comme si chaque char a été converti en unsigned char indépendamment du fait que char est signé ou non signé .

tolower(*a); // Potential UB
tolower((unsigned char) *a); // Correct (Almost - see following)

char peut avoir une valeur négative et ne pas être un complément à 2. (rare)

Ce qui précède ne traite pas -0 ni d'autres valeurs négatives correctement car le motif binaire doit être interprété comme unsigned char . Pour gérer correctement tous les codages d'entiers, changez d'abord le type de pointeur.

// tolower((unsigned char) *a);
tolower(*(const unsigned char *)a); // Correct

Locale (moins fréquent)

Bien que les jeux de caractères utilisant le code ASCII (0-127) soient omniprésents, les codes restants tendent à avoir locale des questions spécifiques. Ainsi, strcasecmp("\xE4", "a") peut retourner un 0 sur un système et un non-zéro sur un autre.


Unicode (la voie de l'avenir)

Si une solution doit gérer plus que l'ASCII, il faut envisager un module unicode_strcicmp() . Comme la bibliothèque C ne fournit pas une telle fonction, il est recommandé d'utiliser une fonction précodée provenant d'une autre bibliothèque. Écrire votre propre unicode_strcicmp() est une tâche décourageante.


Est-ce que toutes les lettres correspondent à un bas et à un haut ? (pédant)

[A-Z] s'applique de manière univoque à [a-z], pourtant divers locales font correspondre divers caractères minuscules à un caractère supérieur et vice-versa. En outre, certains caractères majuscules peuvent ne pas avoir d'équivalent en minuscules et vice-versa.

Cela oblige le code à couvrir à la fois tolower() et tolower() .

int d = tolower(toupper(*a)) - tolower(toupper(*b));

Encore une fois, des résultats potentiellement différents pour le tri si le code a fait tolower(toupper(*a)) vs. toupper(tolower(*a)) .


Portabilité

@B. Nadolson recommande d'éviter de rouler votre propre strcicmp() et cela est raisonnable, sauf lorsque le code a besoin d'une fonctionnalité portable équivalente élevée.

Vous trouverez ci-dessous une approche qui est même plus rapide que certaines fonctions fournies par le système. Elle ne fait qu'une seule comparaison par boucle au lieu de deux en utilisant 2 tables différentes qui diffèrent avec '\0' . Vos résultats peuvent varier.

static unsigned char low1[UCHAR_MAX + 1] = {
  0, 1, 2, 3, ...
  '@', 'a', 'b', 'c', ... 'z', `[`, ...  // @ABC... Z[...
  '`', 'a', 'b', 'c', ... 'z', `{`, ...  // `abc... z{...
}
static unsigned char low2[UCHAR_MAX + 1] = {
// v--- Not zero, but A which matches none in `low1[]`
  'A', 1, 2, 3, ...
  '@', 'a', 'b', 'c', ... 'z', `[`, ...
  '`', 'a', 'b', 'c', ... 'z', `{`, ...
}

int strcicmp_ch(char const *a, char const *b) {
  // compare using tables that differ slightly.
  while (low1[*(const unsigned char *)a] == low2[*(const unsigned char *)b]) {
    a++;
    b++;
  }
  // Either strings differ or null character detected.
  // Perform subtraction using same table.
  return (low1[*(const unsigned char *)a] - low1[*(const unsigned char *)b]);
}

6voto

Jonathan Wood Points 26443

J'utiliserais stricmp() . Il compare deux chaînes de caractères sans tenir compte de la casse.

Notez que, dans certains cas, la conversion de la chaîne en minuscules peut être plus rapide.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X