web-dev-qa-db-ja.com

Cの大文字と小文字を区別しない文字列comp

2つの郵便番号char*大文字と小文字を区別せずに比較したい。これを行う機能はありますか?

または、tolower関数を使用するたびにループしてから比較を行う必要がありますか?

この関数が文字列内の数値とどのように反応するかについての考え

ありがとう

61
bond425

C標準にはこれを行う機能はありません。 POSIXに準拠するUnixシステムでは、ヘッダーに strcasecmp が必要ですstrings.h; Microsoftシステムにはstricmpがあります。移植性を高めるには、独自に記述してください。

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

ただし、これらのソリューションはいずれもUTF-8ストリングでは機能せず、ASCIIの1つのみです。

59
Fred Foo

strings.hstrcasecmp() を見てください。

31

stricmp()を使用します。大文字小文字を区別せずに2つの文字列を比較します。

場合によっては、文字列を小文字に変換する方が高速になる場合があることに注意してください。

4
Jonathan Wood

標準ヘッダーに追加の文字列関数を含むfromという名前の組み込みメソッドが見つかりました。

関連する署名は次のとおりです。

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

また、xnuカーネル(osfmk/device/subrs.c)の同義語であり、次のコードに実装されているので、元のstrcmp関数と比較して、振る舞いの数が変わることはありません。

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));
}
4
Zohar81

大文字小文字を区別せずに比較する際に注意すべき追加の落とし穴:


小文字と大文字の比較? (十分に一般的な問題)

以下の両方は、strcicmpL("A", "a")strcicmpU("A", "a")で0を返します。
まだstrcicmpL("A", "_")strcicmpU("A", "_")は、'_'が大文字と小文字の間にあることが多いため、異なる署名結果を返すことができます。

これは、qsort(..., ..., ..., strcicmp)で使用される場合のソート順に影響します。一般的に利用可能なstricmp()strcasecmp()のような非標準ライブラリC関数は、明確に定義される傾向があり、小文字での比較に適しています。まだバリエーションがあります。

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には負の値を指定できます。 (まれではない)

touppper(int)tolower(int)は、unsigned char値と負のEOFに対して指定されます。さらに、strcmp()は、charsignedまたはであるかどうかに関係なく、各charunsigned charに変換されたかのように結果を返します。符号なし

tolower(*a); // Potential UB
tolower((unsigned char) *a); // Correct

ロケール(あまり一般的ではない)

ASCIIコード(0-127)を使用した文字セットはいたるところにありますが、残りのコードにはlocale特定の問題がある傾向があります。したがって、strcasecmp("\xE4", "a")は1つのシステムでは0で、別のシステムではゼロ以外です。


Unicode(未来の道)

ソリューションがASCII以上を処理する必要がある場合、unicode_strcicmp()を検討してください。Clibはそのような関数を提供しないため、代替ライブラリの事前コード化関数が推奨されます。独自のunicode_strcicmp()は困難な作業です。


すべての文字は下から上にマッピングされますか? (pedantic)

[A-Z]は[a-z]で1対1にマッピングしますが、さまざまなlocalesさまざまな小文字の文字を1つの大文字にマッピングします。さらに、一部の大文字には、同等の小文字、さらにはビザが欠けている場合があります。

これにより、コードはtolower()tolower()の両方を変換する必要があります。

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

繰り返しますが、コードがtolower(toupper(*a))toupper(tolower(*a))を実行した場合、ソートの結果が異なる可能性があります。


移植性

@ B。Nadolson 独自のstrcicmp()のロールを避けることをお勧めします。これは、コードに高度な同等のポータブル機能が必要な場合を除き、合理的です。

以下は、システムが提供する機能よりも高速に実行されるアプローチです。 '\0'と異なる2つの異なるテーブルを使用して、2つではなくループごとに1つの比較を行います。結果は異なる場合があります。

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]);
}
2
chux

ライブラリに何もなければ、効率的なものを実装する方法のアイデアを得ることができます here

256文字すべてのテーブルを使用します。

  • その表では、文字を除くすべての文字について、そのASCIIコードを使用しました。
  • 大文字のコードの場合-小文字の記号の表リストコード。

次に、文字列をトラバースし、指定された文字のテーブルセルを比較するだけです。

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]);
0
Andrey Suvorov

私は実際には ここで最も支持されている答え の一部ではありません一度-そしてそれはこれを行いません)、私は自分で書いた。

これは、 strncmp() の直接ドロップイン置換であり、以下に示すように、多数のテストケースで完全にテストされています。

コードのみ:

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

完全コメント版:

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

テストコード:(ここでオンラインで実行します): https://onlinegdb.com/B1Qoj0W_N

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

サンプル出力:

こんにちは世界

strncmpci(hey、HEY、3)= 0
strncmp(hey、HEY、3)= 32

strncmpci(heY、HeY、3)= 0
strncmp(heY、HeY、3)= 32

strncmpci(hey、HEdY、3)= 21
strncmp(hey、HEdY、3)= 32

strncmpci(heY、HeYd、3)= 0
strncmp(heY、HeYd、3)= 32

strncmpci(heY、HeYd、6)= -100
strncmp(heY、HeYd、6)= 32

strncmpci(hey、hey、6)= 0
strncmp(hey、hey、6)= 0

strncmpci(hey、heyd、6)= -100
strncmp(hey、heyd、6)= -100

strncmpci(hey、heyd、3)= 0
strncmp(hey、heyd、3)= 0

参照:

  1. ここでのこの質問と他の回答は、インスピレーションとして役立ち、いくつかの洞察を与えました( CのIncase String comp comp
  2. http://www.cplusplus.com/reference/cstring/strncmp/
  3. https://en.wikipedia.org/wiki/ASCII
  4. https://en.cppreference.com/w/c/language/operator_precedence
0
Gabriel Staples
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;
}

参照

0
S. M. AMRAN

他の人が述べたように、すべてのシステムで機能する移植可能な機能はありません。単純な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"));
}
0
Miljen Mikic