J'ai deux codes postaux char*
que je souhaite comparer, en ignorant le cas . Y a-t-il une fonction pour faire cela
Ou dois-je parcourir en boucle chaque utilisation de la fonction tolower et ensuite faire la comparaison?
Toute idée de la manière dont cette fonction réagira avec les nombres dans la chaîne
Merci
Il n'y a pas de fonction qui fait cela dans le standard C. Les systèmes Unix conformes à POSIX doivent obligatoirement avoir strcasecmp
dans l'en-tête strings.h
; Les systèmes Microsoft ont stricmp
. Pour être sur le côté portable, écrivez votre propre:
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;
}
}
Notez toutefois qu'aucune de ces solutions ne fonctionnera avec les chaînes UTF-8, mais uniquement ASCII.
Jetez un coup d'œil à strcasecmp () in strings.h
.
Je voudrais utiliser stricmp()
. Il compare deux chaînes sans tenir compte de la casse.
Notez que, dans certains cas, la conversion de la chaîne en minuscules peut être plus rapide.
J'ai trouvé la méthode intégrée nommée à partir de laquelle 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 qu'il est implémenté dans le code suivant, vous ne devriez donc pas avoir de changement de comportement en nombre par rapport à la fonction strcmp d'origine.
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));
}
Comparaison en minuscule ou en majuscule? (problème assez commun)
Les deux éléments ci-dessous renverront 0 avec strcicmpL("A", "a")
et strcicmpU("A", "a")
.
Pourtant, strcicmpL("A", "_")
et strcicmpU("A", "_")
peuvent renvoyer différents résultats signés car '_'
est souvent compris entre les majuscules et les minuscules.
Cela affecte l'ordre de tri lorsqu'il est utilisé avec qsort(..., ..., ..., strcicmp)
. Les fonctions de bibliothèque C non standard, telles que les fonctions stricmp()
ou strcasecmp()
couramment disponibles, ont tendance à être bien définies et favorisent la comparaison via des minuscules. Pourtant, des variations existent.
int strcicmpL(char const *a, char const *b) {
while (*a) {
int d = tolower(*a) - tolower(*b);
if (d) {
return d;
}
a++;
b++;
}
return 0;
}
int strcicmpU(char const *a, char const *b) {
while (*a) {
int d = toupper(*a) - toupper(*b);
if (d) {
return d;
}
a++;
b++;
}
return 0;
}
char
peut avoir une valeur négative. (pas rare)
touppper(int)
et tolower(int)
sont spécifiés pour les valeurs unsigned char
et la valeur négative EOF
. De plus, strcmp()
renvoie les résultats comme si chaque char
était convertie en unsigned char
, que char
soit signé ou non signé.
tolower(*a); // Potential UB
tolower((unsigned char) *a); // Correct
Locale (moins commun)
Bien que les jeux de caractères utilisant le code ASCII (0-127) soient omniprésents, les codes restants ont tendance à avoir des problèmes spécifiques à locale. Donc, strcasecmp("\xE4", "a")
peut renvoyer un 0 sur un système et un zéro sur un autre.
Unicode (la voie du futur)
Si une solution doit gérer plus de ASCII, envisagez une unicode_strcicmp()
. C lib ne fournissant pas une telle fonction, une fonction pré-codée à partir d'une autre bibliothèque est recommandée. Écrire votre propre unicode_strcicmp()
est une tâche ardue.
Est-ce que toutes les lettres mappent un bas sur un haut? (pédant)
[A-Z] mappe un-à-un avec [a-z], mais divers locales mappent divers caractères minuscules en un supérieur et inversement. De plus, certains caractères majuscules peuvent ne pas avoir d’équivalent minuscule et inversement.
Cela oblige le code à se convertir à la fois par tolower()
et tolower()
.
int d = tolower(toupper(*a)) - tolower(toupper(*b));
Encore une fois, les résultats possibles pour le tri sont différents si le code avait tolower(toupper(*a))
contre toupper(tolower(*a))
.
Portabilité
@B. Nadolson recommande d'éviter de lancer votre propre strcicmp()
et ceci est raisonnable, sauf lorsque le code nécessite une fonctionnalité portable équivalente élevée.
Vous trouverez ci-dessous une approche qui a même fonctionné plus rapidement que certaines fonctions fournies par le système. Il effectue une comparaison simple par boucle plutôt que deux en utilisant 2 tables différentes qui diffèrent par '\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[(unsigned char)*a] == low2[(unsigned char)*b]) {
a++;
b++;
}
// Either strings differ or null character detected.
// Perform subtraction using same table.
return (low1[(unsigned char)*a] - low1[(unsigned char)*b]);
}
static int ignoreCaseComp (const char *str1, const char *str2, int length)
{
int k;
for (k = 0; k < length; k++)
{
if ((str1[k] | 32) != (str2[k] | 32))
break;
}
if (k != length)
return 1;
return 0;
}
Vous pouvez avoir une idée, comment en implémenter une efficace, si vous n'en avez pas dans la bibliothèque, depuis ici
Il utilise une table pour tous les 256 caractères.
alors nous avons juste besoin de parcourir une chaîne de caractères et de comparer nos cellules de tableau pour un caractère donné:
const char *cm = charmap,
*us1 = (const char *)s1,
*us2 = (const char *)s2;
while (cm[*us1] == cm[*us2++])
if (*us1++ == '\0')
return (0);
return (cm[*us1] - cm[*--us2]);
Je ne suis pas vraiment fan de la réponse la plus votée ici } (en partie parce que ce n'est pas correct, car elle devrait continuer si elle lit un terminateur nul dans l'une des chaînes - mais pas les deux chaînes en même temps --et ça ne fait pas ça), alors j'ai écrit le mien.
strncmp()
, et a été entièrement testé avec de nombreux cas de test, comme indiqué ci-dessous:Le code uniquement:
#include <ctype.h> // for `tolower()`
#include <limits.h> // for `INT_MIN`
// Case-insensitive `strncmp()`
static inline int strncmpci(const char * str1, const char * str2, size_t num)
{
int ret_code = INT_MIN;
size_t chars_compared = 0;
if (!str1 || !str2)
{
goto done;
}
while ((*str1 || *str2) && (chars_compared < num))
{
ret_code = tolower((int)(*str1)) - tolower((int)(*str2));
if (ret_code != 0)
{
break;
}
chars_compared++;
str1++;
str2++;
}
done:
return ret_code;
}
Version entièrement commentée:
#include <ctype.h> // for `tolower()`
#include <limits.h> // for `INT_MIN`
/*
Case-insensitive string compare (strncmp case-insensitive)
- Identical to strncmp except case-insensitive. See: http://www.cplusplus.com/reference/cstring/strncmp/
- Aided/inspired, in part, by: https://stackoverflow.com/a/5820991/4561887
str1 C string 1 to be compared
str2 C string 2 to be compared
num max number of chars to compare
return:
(essentially identical to strncmp)
INT_MIN invalid arguments (one or both of the input strings is a NULL pointer)
<0 the first character that does not match has a lower value in str1 than in str2
0 the contents of both strings are equal
>0 the first character that does not match has a greater value in str1 than in str2
*/
static inline int strncmpci(const char * str1, const char * str2, size_t num)
{
int ret_code = INT_MIN;
size_t chars_compared = 0;
// Check for NULL pointers
if (!str1 || !str2)
{
goto done;
}
// Continue doing case-insensitive comparisons, one-character-at-a-time, of str1 to str2,
// as long as at least one of the strings still has more characters in it, and we have
// not yet compared num chars.
while ((*str1 || *str2) && (chars_compared < num))
{
ret_code = tolower((int)(*str1)) - tolower((int)(*str2));
if (ret_code != 0)
{
// The 2 chars just compared don't match
break;
}
chars_compared++;
str1++;
str2++;
}
done:
return ret_code;
}
int main()
{
printf("Hello World\n\n");
const char * str1;
const char * str2;
size_t n;
str1 = "hey";
str2 = "HEY";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "heY";
str2 = "HeY";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "hey";
str2 = "HEdY";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "heY";
str2 = "HeYd";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "heY";
str2 = "HeYd";
n = 6;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "hey";
str2 = "hey";
n = 6;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "hey";
str2 = "heyd";
n = 6;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
str1 = "hey";
str2 = "heyd";
n = 3;
printf("strncmpci(%s, %s, %u) = %i\n", str1, str2, n, strncmpci(str1, str2, n));
printf("strncmp(%s, %s, %u) = %i\n", str1, str2, n, strncmp(str1, str2, n));
printf("\n");
return 0;
}
Bonjour le monde
strncmpci (hey, HEY, 3) = 0
strncmp (hé, HEY, 3) = 32strncmpci (heY, heY, 3) = 0
strncmp (hé, hé, 3) = 32strncmpci (hey, HEdY, 3) = 21
strncmp (hé, HEdY, 3) = 32strncmpci (heY, HeYd, 3) = 0
strncmp (hé, hé, 3) = 32strncmpci (heY, HeYd, 6) = -100
strncmp (hé, hé, 6) = 32strncmpci (hé, hé, 6) = 0
strncmp (hé, hé, 6) = 0strncmpci (hey, heyd, 6) = -100
strncmp (hé, hé, 6) = -100strncmpci (hé, hé, 3) = 0
strncmp (hé, hé, 3) = 0
Comme d'autres l'ont dit, aucune fonction portable ne fonctionne sur tous les systèmes. Vous pouvez partiellement contourner ceci avec un simple ifdef
:
#include <stdio.h>
#ifdef _WIN32
#include <string.h>
#define strcasecmp _stricmp
#else // assuming POSIX or BSD compliant system
#include <strings.h>
#endif
int main() {
printf("%d", strcasecmp("teSt", "TEst"));
}