Programing

C의 대/소문자 구분 문자열 비교

c10106 2022. 4. 22. 20:50
반응형

C의 대/소문자 구분 문자열 비교

나는 두 개의 포스트코드를 가지고 있다.char*내가 비교하고 싶은 것, 사건을 무시하는 것.이것을 할 수 있는 기능이 있는가?

아니면 각각의 사용법을 반복해서tolower기능하고 비교를 하는가?

이 함수가 문자열의 숫자와 어떻게 반응하는지 알기

고마워요.

안을 살펴보다.strings.h.

C표준에는 이렇게 하는 기능이 없다.POSIX를 준수하는 Unix 시스템은 헤더에 있어야 함strings.h; 마이크로소프트 시스템에는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 문자열만 사용할 수 있다는 점에 유의하십시오.

대소문자를 구분하지 않는 작업을 수행할 때 주의해야 할 추가 함정 비교:


소문자로 비교하십니까, 대문자로 비교하십니까?(공통적으로 충분히 발행됨)

아래 둘 다 0으로 반환되며strcicmpL("A", "a")그리고strcicmpU("A", "a").
아직strcicmpL("A", "_")그리고strcicmpU("A", "_")다음과 같이 서로 다른 서명된 결과를 반환할 수 있음'_'대문자와 소문자 사이에 있는 경우가 많다.

이것은 와 함께 사용될 때 정렬 순서에 영향을 미친다.qsort(..., ..., ..., strcicmp). 일반적으로 사용 가능한 것과 같은 비표준 라이브러리 C 기능stricmp()또는strcasecmp()잘 규정되어 있고 소문자를 통해 비교하는 것을 선호하는 경향이 있다.그러나 변주곡은 존재한다.

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음의 값을 가질 수 있다.(귀찮음)

touppper(int)그리고tolower(int)에 대해 지정되다unsigned char의 부정적EOF더 나아가서strcmp()마치 각각처럼 결과를 반환하다.char로 개종되었다.unsigned char, 을 불문하고char서명되거나 서명되지 않은 경우.

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

char2의 보완이 아닌 음의 값을 가질 수 있다. (1998년)

위와 같은 것은 다루지 않는다.-0비트 패턴이 다음과 같이 해석되어야 하는 다른 음수 값unsigned char 모든 모든 정수 인코딩을 올바르게 처리하려면 먼저 포인터 유형을 변경하십시오.

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

로케일(일반적이지 않음)

ASCII 코드(0-127)를 사용하는 문자 집합은 어디에나 있지만, 나머지 코드에는 로케일별 문제가 있는 경향이 있다.그렇게strcasecmp("\xE4", "a")한 시스템에서는 0을 반환하고 다른 시스템에서는 0을 반환하지 않을 수 있다.


유니코드(미래 방식)

로 하는 , ASCII를 한다.unicode_strcicmp()C 라이브러리에서 코드화된 C lib는 그러한 기능을 제공하지 않기 때문에, 일부 대체 라이브러리에서 미리 코딩된 기능을 권장한다.너만의 글쓰기unicode_strcicmp()벅찬 작업이다.


모든 글자가 1에서 1까지 매핑되나?(페디컬한)

[A-Z]는 [a-z]와 일대일로 매핑되지만, 다양한 로케일은 다양한 소문자를 하나의 상부 및 비자 대용으로 매핑한다.또한 일부 대문자에는 소문자가 없을 수 있으며, 다시 말해 비자 대소문자를 구분할 수 없다.

이 경우 코드는 두 가지 모두를 통해 은닉해야 한다.tolower()그리고tolower().

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

다시 말하지만, 코드가 정렬할 경우 다른 결과가 나타날 수 있음tolower(toupper(*a))toupper(tolower(*a)).


휴대성

@B. 나돌슨은 나돌슨은 자신의 몸을 굴리는 것을 피하라고 권한다.strcicmp()코드에 동급의 휴대용 기능이 필요한 경우를 제외하고, 이는 합리적이다.

아래는 일부 시스템이 제공하는 기능보다 더 빠르게 수행된 접근방식이다.서로 다른 두 개의 테이블을 사용하여 두 개의 루프가 아닌 루프당 하나의 비교를 한다.'\0'당신의 결과는 다를 수 있다.

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

나는 표준 헤더에 대한 추가 문자열 함수를 포함하는 그러한 내장 방법을 발견했다.

관련 서명:

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

다른 사람들이 말했듯이, 모든 시스템에서 작동하는 휴대용 기능은 없다.단순하게 이것을 부분적으로 우회할 수 있다.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"));
}

나는 여기서 가장 많이 제시된 대답을 별로 좋아하지 않는다(일부적으로는 그래야 하기 때문에 정확하지 않은 것 같기 때문이다).continue만약 그것이 양쪽 문자열에서 모두 동시에 두 문자열로 무효 종료자를 읽는다면, 그리고 이렇게 하지 않을 것이다. 그래서 나는 내 것을 썼다.

이것은 의 직접 드롭인 대체품이며, 아래와 같이 수많은 시험 케이스로 시험되었다.

와 동일하다.strncmp()다음을 제외한다.

  1. 대소문자를 구분하지 않는다.
  2. 두 문자열 중 하나가 null ptr일 경우 동작이 정의되지 않음(잘 정의되어 있음)정규strncmp()두 문자열 중 하나가 null ptr일 경우 정의되지 않은 동작이 있음(https://en.cppreference.com/w/cpp/string/byte/strncmp) 참조).
  3. 돌아온다.INT_MIN입력 문자열 중 하나가 a인 경우 특수 Sentinel 오류 값으로 사용NULL삐걱삐걱거리다

제한사항: 이 코드는 유니코드 문자 인코딩 UTF-8(가장 인기 있음), UTF-16UTF-32와 같은 유니코드 문자에서는 작동하지 않으며 원본 7비트 ASCII 문자 집합에서만 작동한다는 점에 유의하십시오.

다음은 코드만(주석 없음):

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

완전하게 컴파일된 버전:

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

테스트 코드:

eRCaGuy_hello_world 리포지토리에서 단위 테스트와 함께 전체 샘플 코드를 다운로드하십시오. "strncmpci.c":

(이것은 단지 작은 조각일 뿐이다)

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

샘플 출력:

$ 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!

참조:

  1. 이 질문 및 기타 답변은 영감을 주었고 통찰력을 제공했다(C의 Case Insensible String complex)
  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
  5. 위에서 코드 일부를 수정하기 위해 수행한 정의되지 않은 동작 연구(아래 주석 참조):
    1. Google "배열 한계를 벗어난 c 정의되지 않은 동작 읽기" 검색
    2. 글로벌 어레이에 액세스하는 것이 바인딩되지 않은 동작 외부에 있는가?
    3. https://en.cppreference.com/w/cpp/language/ub - 아래쪽에 있는 정말 훌륭한 "외부 링크"도 많이 보십시오!
    4. 1/3: http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
    5. 2/3: https://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
    6. 3/3: https://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html
    7. https://blog.regehr.org/archives/213
    8. https://www.geeksforgeeks.org/accessing-array-bounds-ccpp/

추가 조사 주제

  1. (참고: 이것은 C가 아닌 C++) 유니코드 문자의 소문자
  2. OnlineGDB에서 tolower_labor.c: https://onlinegdb.com/HyZieXcew

작업:

  1. 유니코드의 UTF-8 구현(문자 인코딩)에도 사용할 수 있는 이 코드의 버전을 만드십시오!

나는 할 것이다stricmp()의 끈을 케이스와 관계 없이 두 줄을 비교한다.

경우에 따라 문자열을 소문자로 변환하는 것이 더 빠를 수 있다는 점에 유의하십시오.

효율적인 아이디어를 얻을 수 있다. 도서관, 도서관, 여기서 효율적인 아이디어를 구현할 수 있다.

그것은 256자 모두를 위한 테이블을 사용한다.

  • 해당 표에서 문자를 제외한 모든 문자 - 해당 아스키 코드를 사용했다.
  • 대문자 코드의 경우 - 표 목록 코드(하단 케이싱 기호의 경우).

끈을 가로지르고 차자와 테이블 세포를 비교하면 돼

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]);

간단한 솔루션:

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

행운을 빌다

Edit-lowerCaseWord 함수는 이 문자의 소문자 값을 사용하여 char* 변수를 가져오고 반환한다.예를 들어 char* 값에 대한 "AbCdE"는 "Abcde"를 반환한다.

기본적으로 두 개의 char* 변수를 소문자로 전송한 후 그 변수에 strcmp 함수를 사용하는 것이다.

예를 들어, 만약 우리가 스트램프를 부르면"AbCdE"와 "ABCDE"의 값에 대한 무감각 함수로, 먼저 두 값을 모두 소문자("abcDE")로 반환한 다음, 그 위에 strcmp 기능을 수행한다.

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

참조

Null 종료된 문자가 있는 경우:

   bool striseq(const char* s1,const char* s2){ 
     for(;*s1;){ 
       if(tolower(*s1++)!=tolower(*s2++)) 
         return false; 
      } 
      return *s1 == *s2;
    }

또는 비트 연산 기능을 사용하는 이 버전:

    int striseq(const char* s1,const char* s2)
       {for(;*s1;) if((*s1++|32)!=(*s2++|32)) return 0; return *s1 == *s2;}

이게기호로 작동하는지 모르겠네, 거기서 테스트한 적은 없지만 글자로는 잘 작동해.

참조URL: https://stackoverflow.com/questions/5820810/case-insensitive-string-comparison-in-c

반응형