Ich habe zwei Postleitzahlen char*
, die ich vergleichen möchte, ohne die Groß- und Kleinschreibung zu beachten. Gibt es eine Funktion, um dies zu tun?
Oder muss ich bei jeder Verwendung die Tolower-Funktion durchlaufen und dann den Vergleich durchführen?
Jede Idee, wie diese Funktion mit Zahlen in der Zeichenfolge reagiert
Vielen Dank
c
string
standard-library
bond425
quelle
quelle
Antworten:
Im C-Standard gibt es keine Funktion, die dies tut. Unix-Systeme, die POSIX entsprechen, müssen
strcasecmp
im Header enthalten seinstrings.h
. Microsoft-Systeme habenstricmp
. Um auf der tragbaren Seite zu sein, schreiben Sie Ihre eigenen: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; } }
Beachten Sie jedoch, dass keine dieser Lösungen mit UTF-8-Zeichenfolgen funktioniert, nur mit ASCII-Zeichenfolgen.
quelle
a
oderb
sindNULL
.a
und / oderb
wieNULL
allgemein üblich als Nullzeiger zeigt nicht auf eine Zeichenfolge . Kein schlechter Scheck zum Hinzufügen, aber was zurückgeben? Solltecmp("", NULL)
0, INT_MIN zurückgeben? Hier besteht kein Konsens. Hinweis: C erlaubt UB mitstrcmp(NULL, "abc");
.Schauen Sie sich strcasecmp () in an
strings.h
.quelle
int strcasecmp(const char *s1, const char *s2);
in Strings.hstricmp
. @entropo:strings.h
ist ein Header für die Kompatibilität mit Unix-Systemen der 1980er Jahre.strings.h
. Es wurde auch definiertstrcasecmp
, in diesem Header deklariert zu werden. ISO C hat es jedoch nicht.string.h
. Siehe z. B. GlibcIch habe eine eingebaute solche Methode mit dem Namen gefunden, die zusätzliche Zeichenfolgenfunktionen zum Standardheader enthält.
Hier sind die relevanten Unterschriften:
int strcasecmp(const char *, const char *); int strncasecmp(const char *, const char *, size_t);
Ich habe auch festgestellt, dass es ein Synonym im xnu-Kernel (osfmk / device / subrs.c) ist und im folgenden Code implementiert ist, sodass Sie keine Änderung des Verhaltens in Bezug auf die Anzahl im Vergleich zur ursprünglichen strcmp-Funktion erwarten würden.
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)); }
quelle
strncasecmp()
Funktion!strcasecmp()
undstrncasecmp()
sind nicht Teil der Standard-C-Bibliothek, sondern häufige Ergänzungen in * nix.Ich würde verwenden
stricmp()
. Es werden zwei Zeichenfolgen ohne Rücksicht auf den Fall verglichen.Beachten Sie, dass in einigen Fällen die Konvertierung der Zeichenfolge in Kleinbuchstaben schneller erfolgen kann.
quelle
Zusätzliche Fallstricke, auf die Sie achten müssen, wenn Sie Vergleiche ohne Berücksichtigung der Groß- und Kleinschreibung durchführen:
Vergleichen als Klein- oder Großbuchstaben? (häufig genug Problem)
Beide unten geben 0 mit
strcicmpL("A", "a")
und zurückstrcicmpU("A", "a")
.Dennoch
strcicmpL("A", "_")
undstrcicmpU("A", "_")
können verschiedene signierte Ergebnisse zurück , wie'_'
oft zwischen den oberen und Kleinbuchstaben.Dies wirkt sich bei Verwendung mit auf die Sortierreihenfolge aus
qsort(..., ..., ..., strcicmp)
. Nicht standardmäßige Funktionen der Bibliothek C wie die allgemein verfügbarenstricmp()
oderstrcasecmp()
sind in der Regel gut definiert und begünstigen den Vergleich über Kleinbuchstaben. Es gibt jedoch Variationen.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
kann einen negativen Wert haben. (nicht selten)touppper(int)
undtolower(int)
sind fürunsigned char
Werte und das Negative angegebenEOF
. Fernerstrcmp()
liefern Ergebnisse , als wenn jedechar
konvertierte zuunsigned char
, unabhängig davon , obchar
sie signiert oder unsigniert .tolower(*a); // Potential UB tolower((unsigned char) *a); // Correct
Gebietsschema (seltener)
Obwohl Zeichensätze ASCII Code (0-127) allgegenwärtig sind, neigen die Rest - Codes haben locale spezifische Fragen. Könnte also
strcasecmp("\xE4", "a")
auf einem System eine 0 und auf einem anderen eine Nicht-Null zurückgeben.Unicode (der Weg der Zukunft)
Wenn eine Lösung mehr als ASCII verarbeiten muss, ziehen Sie a
unicode_strcicmp()
. Da C lib eine solche Funktion nicht bietet, wird eine vorcodierte Funktion aus einer alternativen Bibliothek empfohlen. Das eigene zu schreibenunicode_strcicmp()
ist eine entmutigende Aufgabe.Ordnen alle Buchstaben einen unteren einem oberen zu? (pedantisch)
[AZ] ordnet eins zu eins mit [az] zu, doch verschiedene Gebietsschemas ordnen verschiedene Kleinbuchstaben einem Großbuchstaben zu und umgekehrt. Außerdem fehlt einigen Großbuchstaben möglicherweise ein Kleinbuchstabenäquivalent und umgekehrt.
Dies verpflichtet den Code, sowohl
tolower()
als auch zu verbergentolower()
.int d = tolower(toupper(*a)) - tolower(toupper(*b));
Auch mögliche unterschiedliche Ergebnisse für die Sortierung , wenn Code tut
tolower(toupper(*a))
vs.toupper(tolower(*a))
.Portabilität
@B. Nadolson empfiehlt, das Rollen Ihrer eigenen zu vermeiden.
strcicmp()
Dies ist sinnvoll, es sei denn, Code benötigt eine gleichwertige tragbare Funktionalität.Im Folgenden finden Sie einen Ansatz, der sogar schneller ausgeführt wird als einige vom System bereitgestellte Funktionen. Es wird ein einzelner Vergleich pro Schleife anstelle von zwei durchgeführt, indem zwei verschiedene Tabellen verwendet werden, die sich von unterscheiden
'\0'
. Ihre Ergebnisse können variieren.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]); }
quelle
Ich bin nicht wirklich ein Fan der am besten bewerteten Antwort hier (zum Teil, weil es so aussieht, als ob es nicht korrekt ist, da es sollte,
continue
wenn es einen Null-Terminator in einer der beiden Zeichenfolgen liest - aber nicht in beiden Zeichenfolgen gleichzeitig - und es macht das nicht), also habe ich mein eigenes geschrieben.Dies ist ein direkter Ersatz für
strncmp()
und wurde mit zahlreichen Testfällen getestet, wie unten gezeigt.Es ist identisch mit
strncmp()
Ausnahme von:strncmp()
hat ein undefiniertes Verhalten, wenn eine der Zeichenfolgen null ptr ist (siehe: https://en.cppreference.com/w/cpp/string/byte/strncmp ).INT_MIN
als spezieller Sentinel-Fehlerwert zurückgegeben, wenn eine der Eingabezeichenfolgen einNULL
ptr ist.EINSCHRÄNKUNGEN: Beachten Sie, dass dieser Code nur mit dem ursprünglichen 7-Bit-ASCII-Zeichensatz (Dezimalwerte 0 bis einschließlich 127) funktioniert , NICHT mit Unicode- Zeichen, wie z. B. Unicode-Zeichencodierungen UTF-8 (am beliebtesten), UTF-16 , und UTF-32 .
Hier ist nur der Code (keine Kommentare):
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 ((*str1 || *str2) && (chars_compared < num)) { ret_code = tolower((int)(*str1)) - tolower((int)(*str2)); if (ret_code != 0) { break; } chars_compared++; str1++; str2++; } return ret_code; }
Vollständig kommentierte Version:
/// \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`, // 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++; } return ret_code; }
Testcode:
Laden Sie den gesamten Beispielcode mit Komponententests aus meinem eRCaGuy_hello_world- Repository hier herunter : " strncmpci.c" :
(Dies ist nur ein Ausschnitt)
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; }
Beispielausgabe:
Verweise:
Themen zur weiteren Forschung
MACHEN:
quelle
in part because it isn't correct since ...
Ihr Code ist auch nicht korrekt. Es macht keinen Sinn, tolower zu verwenden , es wird bei weitem der langsamste Teil der Funktion sein. Wenn Sie wirklich möchten, dass Ihre Funktion das Gebietsschema kennt und nicht-ASCII-Zeichen verarbeitet, müssen Sie Ihre Zeichen zuerst in nicht signierte Zeichen umwandeln. Andernfalls führt Ihr Code zu UBtolower()
, wenn wir auf diese Weise den Effekt erhalten, bei dem die Groß- und Kleinschreibung nicht berücksichtigt wird. Darum geht es bei dieser Frage? Außerdem haben Sie eine Verknüpfung mit der C ++ - Referenz anstelle der C-Referenz erstellt. Ändert das nicht die Dinge? Ich habe nie gesagt, dass meine Funktion das Gebietsschema kennt oder Nicht-ASCII-Zeichen verarbeiten kann, aber ich sehe wirklich nicht, wie das Casting zumunsigned char
ersten Mal etwas löst. Alle Zeichen können in vorzeichenlose Zeichen umgewandelt werden. Ich verstehe deinen Kommentar nicht."C"
Gebietsschema, das lautsetlocale()
Standard beim Programmstart ist ) oder Unicode-Erfahrung in C oder C ++. Ich bitte Sie, bei jeder Antwort, die Sie schreiben, gründlich genug zu sein, um diese Punkte und Dinge zu klären.static int tolower(char c){ return (c >= 'A' && c <= 'Z') ? (c | ' ') : c; }
. std :: tolower ist sehr langsam, da es das Gebietsschema kennt.ret_code
auf0
anstatt aufINT_MIN
(oder-9999
wie in dem von Ihnen getesteten Code) initialisiert und es dann nur auf gesetzt,INT_MIN
wenn eine der Eingabezeichenfolgen einNULL
ptr ist. Jetzt funktioniert es perfekt. Das Problem war einfach, dass fürn
0 keiner der Blöcke eingegeben wurde (weder derif
noch derwhile
), also gab es einfach zurück, was ich initialisiert hatteret_code
. Wie auch immer, es ist jetzt behoben und ich habe meine Unit-Tests eine Tonne aufgeräumt und in dem von Ihnen erwähnten Test hinzugefügt. Hoffentlich stimmen Sie jetzt zu.Wie bereits erwähnt, gibt es keine tragbare Funktion, die auf allen Systemen funktioniert. Sie können dies teilweise umgehen, indem Sie einfach
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")); }
quelle
Von hier aus können Sie sich ein Bild davon machen, wie Sie eine effiziente implementieren können, wenn Sie keine in der Bibliothek haben
Es wird eine Tabelle für alle 256 Zeichen verwendet.
Dann müssen wir nur noch eine Zeichenfolge durchlaufen und unsere Tabellenzellen für bestimmte Zeichen vergleichen:
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]);
quelle
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; }
Referenz
quelle
OR
Idee ist ein bisschen geschickt, aber die Logik ist fehlerhaft. Zum BeispielignoreCaseComp("`", "@", 1)
und vielleicht noch wichtigerignoreCaseComp("\0", " ", 1)
(dh wenn alle anderen Bits als Bit 5 (Dezimal 32) identisch sind) werden beide als0
(Übereinstimmung) ausgewertet .Einfache Lösung:
int str_case_ins_cmp(const char* a, const char* b) { int rc; while (1) { rc = tolower((unsigned char)*a) - tolower((unsigned char)*b); if (rc || !*a) { break; } ++a; ++b; } return rc; }
quelle
int strcmpInsensitive(char* a, char* b) { return strcmp(lowerCaseWord(a), lowerCaseWord(b)); } char* lowerCaseWord(char* a) { char *b=new char[strlen(a)]; for (int i = 0; i < strlen(a); i++) { b[i] = tolower(a[i]); } return b; }
Viel Glück
Die Funktion Edit-lowerCaseWord ruft eine char * -Variable mit ab und gibt den Kleinbuchstabenwert dieses char * zurück. Beispiel: "AbCdE" für den Wert von char * gibt "abcde" zurück.
Grundsätzlich werden die beiden char * -Variablen nach der Übertragung in Kleinbuchstaben verwendet und die strcmp-Funktion für sie verwendet.
Wenn wir beispielsweise die Funktion strcmpInsensitive für Werte von "AbCdE" und "ABCDE" aufrufen, werden zuerst beide Werte in Kleinbuchstaben ("abcde") zurückgegeben und anschließend die Funktion strcmp ausgeführt.
quelle
strcmp()
möglicherweise abstürzt.