Je ne suis pas vraiment un fan de la la réponse la plus votée ici (en partie parce qu'il semble que ce n'est pas correct puisqu'il devrait continue
s'il lit un terminateur nul dans l'une ou l'autre chaîne - mais pas dans les deux chaînes à la fois - et il ne le fait pas), alors j'ai écrit le mien.
Il s'agit d'un remplacement direct pour strncmp()
et a été testé avec de nombreux cas de test, comme indiqué ci-dessous.
Il est identique à strncmp()
sauf :
- Il est insensible à la casse.
- Le comportement n'est PAS indéfini (il est bien défini) si l'une des chaînes est un ptr nul. Régulier
strncmp()
a un comportement indéfini si l'une des deux chaînes est un ptr nul (voir : https://en.cppreference.com/w/cpp/string/byte/strncmp ).
- Il retourne
INT_MIN
comme une valeur d'erreur sentinelle spéciale si l'une des chaînes d'entrée est un NULL
ptr.
_LIMITATIONS : Notez que ce code fonctionne sur l'original Jeu de caractères ASCII 7 bits uniquement (valeurs décimales de 0 à 127, inclusivement), PAS sur unicode les caractères, tels que les codages de caractères unicode UTF-8 (les plus populaires), UTF-16 et UTF-32 ._
Voici le code uniquement (pas de commentaires) :
int strncmpci(const char * str1, const char * str2, size_t num)
{
int ret_code = 0;
size_t chars_compared = 0;
if (!str1 || !str2)
{
ret_code = INT_MIN;
return ret_code;
}
while ((chars_compared < num) && (*str1 || *str2))
{
ret_code = tolower((int)(*str1)) - tolower((int)(*str2));
if (ret_code != 0)
{
break;
}
chars_compared++;
str1++;
str2++;
}
return ret_code;
}
Version entièrement commentée :
/// \brief Perform a case-insensitive string compare (`strncmp()` case-insensitive) to see
/// if two C-strings are equal.
/// \note 1. Identical to `strncmp()` except:
/// 1. It is case-insensitive.
/// 2. The behavior is NOT undefined (it is well-defined) if either string is a null
/// ptr. Regular `strncmp()` has undefined behavior if either string is a null ptr
/// (see: https://en.cppreference.com/w/cpp/string/byte/strncmp).
/// 3. It returns `INT_MIN` as a special sentinel value for certain errors.
/// - Posted as an answer here: https://stackoverflow.com/a/55293507/4561887.
/// - Aided/inspired, in part, by `strcicmp()` here:
/// https://stackoverflow.com/a/5820991/4561887.
/// \param[in] str1 C string 1 to be compared.
/// \param[in] str2 C string 2 to be compared.
/// \param[in] num max number of chars to compare
/// \return A comparison code (identical to `strncmp()`, except with the addition
/// of `INT_MIN` as a special sentinel value):
///
/// INT_MIN (usually -2147483648 for int32_t integers) 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.
int strncmpci(const char * str1, const char * str2, size_t num)
{
int ret_code = 0;
size_t chars_compared = 0;
// Check for NULL pointers
if (!str1 || !str2)
{
ret_code = INT_MIN;
return ret_code;
}
// Continue doing case-insensitive comparisons, one-character-at-a-time, of `str1` to `str2`, so
// long as 1st: we have not yet compared the requested number of chars, and 2nd: the next char
// of at least *one* of the strings is not zero (the null terminator for a C-string), meaning
// that string still has more characters in it.
// Note: you MUST check `(chars_compared < num)` FIRST or else dereferencing (reading) `str1` or
// `str2` via `*str1` and `*str2`, respectively, is undefined behavior if you are reading one or
// both of these C-strings outside of their array bounds.
while ((chars_compared < num) && (*str1 || *str2))
{
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++;
}
return ret_code;
}
Code de test :
Téléchargez l'intégralité de l'exemple de code, avec les tests unitaires, à partir de mon site web eRCaGuy_hello_world le dépôt ici : " strncmpci.c" :
(ce n'est qu'un extrait)
int main()
{
printf("-----------------------\n"
"String Comparison Tests\n"
"-----------------------\n\n");
int num_failures_expected = 0;
printf("INTENTIONAL UNIT TEST FAILURE to show what a unit test failure looks like!\n");
EXPECT_EQUALS(strncmpci("hey", "HEY", 3), 'h' - 'H');
num_failures_expected++;
printf("------ beginning ------\n\n");
const char * str1;
const char * str2;
size_t n;
// NULL ptr checks
EXPECT_EQUALS(strncmpci(NULL, "", 0), INT_MIN);
EXPECT_EQUALS(strncmpci("", NULL, 0), INT_MIN);
EXPECT_EQUALS(strncmpci(NULL, NULL, 0), INT_MIN);
EXPECT_EQUALS(strncmpci(NULL, "", 10), INT_MIN);
EXPECT_EQUALS(strncmpci("", NULL, 10), INT_MIN);
EXPECT_EQUALS(strncmpci(NULL, NULL, 10), INT_MIN);
EXPECT_EQUALS(strncmpci("", "", 0), 0);
EXPECT_EQUALS(strncmp("", "", 0), 0);
str1 = "";
str2 = "";
n = 0;
EXPECT_EQUALS(strncmpci(str1, str2, n), 0);
EXPECT_EQUALS(strncmp(str1, str2, n), 0);
str1 = "hey";
str2 = "HEY";
n = 0;
EXPECT_EQUALS(strncmpci(str1, str2, n), 0);
EXPECT_EQUALS(strncmp(str1, str2, n), 0);
str1 = "hey";
str2 = "HEY";
n = 3;
EXPECT_EQUALS(strncmpci(str1, str2, n), 0);
EXPECT_EQUALS(strncmp(str1, str2, n), 'h' - 'H');
str1 = "heY";
str2 = "HeY";
n = 3;
EXPECT_EQUALS(strncmpci(str1, str2, n), 0);
EXPECT_EQUALS(strncmp(str1, str2, n), 'h' - 'H');
str1 = "hey";
str2 = "HEdY";
n = 3;
EXPECT_EQUALS(strncmpci(str1, str2, n), 'y' - 'd');
EXPECT_EQUALS(strncmp(str1, str2, n), 'h' - 'H');
str1 = "heY";
str2 = "hEYd";
n = 3;
EXPECT_EQUALS(strncmpci(str1, str2, n), 0);
EXPECT_EQUALS(strncmp(str1, str2, n), 'e' - 'E');
str1 = "heY";
str2 = "heyd";
n = 6;
EXPECT_EQUALS(strncmpci(str1, str2, n), -'d');
EXPECT_EQUALS(strncmp(str1, str2, n), 'Y' - 'y');
str1 = "hey";
str2 = "hey";
n = 6;
EXPECT_EQUALS(strncmpci(str1, str2, n), 0);
EXPECT_EQUALS(strncmp(str1, str2, n), 0);
str1 = "hey";
str2 = "heyd";
n = 6;
EXPECT_EQUALS(strncmpci(str1, str2, n), -'d');
EXPECT_EQUALS(strncmp(str1, str2, n), -'d');
str1 = "hey";
str2 = "heyd";
n = 3;
EXPECT_EQUALS(strncmpci(str1, str2, n), 0);
EXPECT_EQUALS(strncmp(str1, str2, n), 0);
str1 = "hEY";
str2 = "heyYOU";
n = 3;
EXPECT_EQUALS(strncmpci(str1, str2, n), 0);
EXPECT_EQUALS(strncmp(str1, str2, n), 'E' - 'e');
str1 = "hEY";
str2 = "heyYOU";
n = 10;
EXPECT_EQUALS(strncmpci(str1, str2, n), -'y');
EXPECT_EQUALS(strncmp(str1, str2, n), 'E' - 'e');
str1 = "hEYHowAre";
str2 = "heyYOU";
n = 10;
EXPECT_EQUALS(strncmpci(str1, str2, n), 'h' - 'y');
EXPECT_EQUALS(strncmp(str1, str2, n), 'E' - 'e');
EXPECT_EQUALS(strncmpci("nice to meet you.,;", "NICE TO MEET YOU.,;", 100), 0);
EXPECT_EQUALS(strncmp( "nice to meet you.,;", "NICE TO MEET YOU.,;", 100), 'n' - 'N');
EXPECT_EQUALS(strncmp( "nice to meet you.,;", "nice to meet you.,;", 100), 0);
EXPECT_EQUALS(strncmpci("nice to meet you.,;", "NICE TO UEET YOU.,;", 100), 'm' - 'u');
EXPECT_EQUALS(strncmp( "nice to meet you.,;", "nice to uEET YOU.,;", 100), 'm' - 'u');
EXPECT_EQUALS(strncmp( "nice to meet you.,;", "nice to UEET YOU.,;", 100), 'm' - 'U');
EXPECT_EQUALS(strncmpci("nice to meet you.,;", "NICE TO MEET YOU.,;", 5), 0);
EXPECT_EQUALS(strncmp( "nice to meet you.,;", "NICE TO MEET YOU.,;", 5), 'n' - 'N');
EXPECT_EQUALS(strncmpci("nice to meet you.,;", "NICE eo UEET YOU.,;", 5), 0);
EXPECT_EQUALS(strncmp( "nice to meet you.,;", "nice eo uEET YOU.,;", 5), 0);
EXPECT_EQUALS(strncmpci("nice to meet you.,;", "NICE eo UEET YOU.,;", 100), 't' - 'e');
EXPECT_EQUALS(strncmp( "nice to meet you.,;", "nice eo uEET YOU.,;", 100), 't' - 'e');
EXPECT_EQUALS(strncmpci("nice to meet you.,;", "nice-eo UEET YOU.,;", 5), ' ' - '-');
EXPECT_EQUALS(strncmp( "nice to meet you.,;", "nice-eo UEET YOU.,;", 5), ' ' - '-');
if (globals.error_count == num_failures_expected)
{
printf(ANSI_COLOR_GRN "All unit tests passed!" ANSI_COLOR_OFF "\n");
}
else
{
printf(ANSI_COLOR_RED "FAILED UNIT TESTS! NUMBER OF UNEXPECTED FAILURES = %i"
ANSI_COLOR_OFF "\n", globals.error_count - num_failures_expected);
}
assert(globals.error_count == num_failures_expected);
return globals.error_count;
}
Exemple de sortie :
$ gcc -Wall -Wextra -Werror -ggdb -std=c11 -o ./bin/tmp strncmpci.c && ./bin/tmp
-----------------------
String Comparison Tests
-----------------------
INTENTIONAL UNIT TEST FAILURE to show what a unit test failure looks like!
FAILED at line 250 in function main! strncmpci("hey", "HEY", 3) != 'h' - 'H'
a: strncmpci("hey", "HEY", 3) is 0
b: 'h' - 'H' is 32
------ beginning ------
All unit tests passed!
Références :
- Cette question et d'autres réponses ici ont servi d'inspiration et ont donné un aperçu ( Calcul des chaînes de caractères en C sans tenir compte de la casse )
- http://www.cplusplus.com/reference/cstring/strncmp/
- https://en.wikipedia.org/wiki/ASCII
- https://en.cppreference.com/w/c/language/operator_precedence
-
Comportement indéfini recherche que j'ai faite pour corriger une partie de mon code ci-dessus (voir les commentaires ci-dessous) :
- Recherche Google pour "c comportement indéfini lecture en dehors des limites du tableau"
- L'accès à un tableau global en dehors de sa limite est-il un comportement non défini ?
-
https://en.cppreference.com/w/cpp/language/ub - voir aussi les nombreux "Liens externes" très intéressants en bas de page !
- 1/3 : http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
- 2/3 : https://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
- 3/3 : https://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html
- https://blog.regehr.org/archives/213
- https://www.geeksforgeeks.org/accessing-array-bounds-ccpp/
Sujets à approfondir
- (Note : il s'agit de C++, pas de C) Minuscule du caractère Unicode
-
tolower_tests.c sur OnlineGDB : https://onlinegdb.com/HyZieXcew
TODO :
- Réalisez une version de ce code qui fonctionne également avec le système Unicode. UTF-8 (codage des caractères) !
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.
0 votes
Je suppose que vous voulez simplement parler de la comparaison de chaînes de caractères ASCII ? Il ne s'agit pas d'une comparaison générique à l'échelle mondiale dans plusieurs langues ?
0 votes
La comparaison pourrait aboutir à la comparaison d'un chiffre et d'une lettre. Je dois tester si deux codes postaux sont égaux l'un à l'autre, si l'un est supérieur ou inférieur à l'autre. La partie supérieure à, inférieure à est déroutante, je ne suis pas sûr de savoir comment cela va fonctionner.