strcasecmp 알고리즘에 결함이 있습니까?


34

strcasecmpC 에서 함수 를 다시 구현하려고하는데 비교 과정에서 불일치하는 것으로 나타났습니다.

에서 man strcmp

strcmp () 함수는 두 문자열 s1과 s2를 비교합니다. 로케일은 고려되지 않습니다 (로케일 인식 비교는 strcoll (3) 참조). s1이 각각 s2보다 작거나 일치하거나 s2보다 크면 0보다 작거나 같거나 0보다 큰 정수를 리턴합니다.

에서 man strcasecmp

strcasecmp () 함수는 문자열의 경우를 무시하고 문자열 s1 및 s2를 바이트 단위로 비교합니다. s1이 각각 s2보다 작거나 일치하거나 s2보다 크면 0보다 작거나 같거나 0보다 큰 정수를 리턴합니다.

int strcmp(const char *s1, const char *s2);
int strcasecmp(const char *s1, const char *s2);

이 정보가 주어지면 다음 코드의 결과를 이해하지 못합니다.

#include <stdio.h>
#include <string.h>

int main()
{
    // ASCII values
    // 'A' = 65
    // '_' = 95
    // 'a' = 97

    printf("%i\n", strcmp("A", "_"));
    printf("%i\n", strcmp("a", "_"));
    printf("%i\n", strcasecmp("A", "_"));
    printf("%i\n", strcasecmp("a", "_"));
    return 0;
}

출력 :

-1  # "A" is less than "_"
1   # "a" is more than "_"
2   # "A" is more than "_" with strcasecmp ???
2   # "a" is more than "_" with strcasecmp

현재 문자 s1가 문자이면 현재 문자 가 문자 인지 여부에 관계없이 항상 소문자로 변환됩니다 s2.

누군가이 행동을 설명 할 수 있습니까? 첫 번째 줄과 세 번째 줄이 동일하지 않아야합니까?

미리 감사드립니다!

추신 :
저는 gcc 9.2.0Manjaro를 사용 하고 있습니다.
또한 -fno-builtin플래그로 컴파일 하면 대신 얻을 수 있습니다.

-30
2
2
2

프로그램이 gcc의 최적화 된 기능을 사용하지 않기 때문이지만 질문은 남아 있습니다.


2
다른 테스트 사례를 세트에 추가하십시오. printf("%i\n", strcasecmp("a", "_"));이는 아마도 동일한 결과를 가져야 printf("%i\n", strcasecmp("A", "_"));하지만 대소 문자를 구분하지 않는 두 통화 중 하나 가 대소 문자를 구분하지 않는 것을 의미합니다 .
anton.burger

strcasecmp당신이 말하는 설명이 정확하지 않은 것 같습니다 . upvoted 답변에 대한 자세한 내용.
Jabberwocky

9
말이되는 유일한 것입니다. A < _ && a > _ && A == a너무 많은 문제를 일으킨다 는 기능 .
ikegami

따로 : "C에서 strcasecmp 함수를 다시 구현하려고합니다"-> 코드가 표시되지는 않지만 "as as"를 비교하십시오 unsigned char. C17 / 18 "문자열 처리 <string.h>"-> "이 하위 절의 모든 기능에 대해 각 문자는 unsigned char" 유형을 갖는 것처럼 해석됩니다 . char값이 ASCII 범위 0-127을 벗어나면 차이가 발생 합니다.
chux-복직 모니카

1
빌트인이 있거나없는 출력의 차이점 : 결과는 <0과> 0이며, == 0에 대한 예가 없기 때문에 둘 다 동일하게 말합니다. 그러나 알고리즘이 빛을 발하는 것을 볼 수 있습니다. 반환 된 값 중 일부는 같지 않은 첫 번째 문자의 차이점입니다.
바쁜 꿀벌

답변:


31

동작이 정확합니다.

는 POSIX의 str\[n\]casecmp()사양 :

LC_CTYPE사용되는 로케일 의 카테고리가 POSIX 로케일의 것이라면,이 함수는 문자열이 소문자로 변환 된 다음 바이트 비교가 수행 된 것처럼 작동합니다. 그렇지 않으면 결과가 지정되지 않습니다.

그것도의 일부입니다 NOTES의 리눅스 man 페이지의 섹션 :

POSIX.1-2008 표준은 다음 기능을 말합니다.

사용되는 로케일의 LC_CTYPE 범주가 POSIX 로케일에서 온 경우,이 함수는 문자열이 소문자로 변환 된 후 바이트 비교가 수행 된 것처럼 작동합니다. 그렇지 않으면 결과가 지정되지 않습니다.

왜?

@HansOlsson이 그의 답변에서 지적했듯이 문자 만 대소 문자를 구분하지 않고 비교하면 다른 모든 비교에서 "자연스러운"결과를 얻을 수 있으므로 strcmp()정렬이 어려워 집니다 .

경우 'A' == 'a'(대소 문자를 구분하지 않는 비교의 정의는) 다음 '_' > 'A''_' < 'a'(ASCII 문자 세트의 "자연"결과는) 모두 사실이 될 수 없습니다.


문자 만 대소 문자를 구분하지 않으면 '_' > 'A' && '_' < 'a'; 가장 좋은 예가 아닌 것 같습니다.
날개를 가진 소행성

1
@AsteroidsWithWings 질문에 사용 된 문자입니다. 그리고 경우 'a' == 'A' 정의에 의해 당신의 "자연"값 사이의 비교 할 경우 'a', 'A''_'를, 당신은 할 수없는 사이 대소 문자를 구분하지 않고 비교를 수행 'A'하고 'a'평등을 얻을하고 일관된 정렬 결과를 얻을 수 있습니다.
Andrew Henle

나는 논쟁의 여지가 없지만 제공 한 특정 반례는 관련이없는 것 같습니다.
날개를 가진 소행성

에서 이진 트리를 구축하는 정신 운동을 통해 @AsteroidsWithWings 이동 'a', 'A''_'문자 만 변환 "제안 트리에 삽입의 모든 (6) 주문을 통과하고, 질문의 지정된 그대로"항상 소문자 "의 결과를 비교 문자 대 문자 비교 일 때 " 예를 들어, 후자의 알고리즘을 사용하여로 시작 '_', 'a'그리고 'A'나무의 반대편에 바람 아직 그들은 같은 것으로 정의하고있다. "문자-문자 비교에서 문자 만 소문자로 변환"알고리즘이 졌으며이 3 개의 문자가이를 보여줍니다.
Andrew Henle

좋아, 그때 우리가 왜 그렇게 생각해야했는지 우리에게 말하지 않고 " '_' > 'A' 그리고 '_' < 'a'둘 다 사실이 될 수 없다 "는 점을 지적하기 때문에 대답에서 그것을 증명할 것을 제안 한다. (그것은 수백만의 독자 중 한 사람이 아닌 응답자에게 맡겨진 과제입니다.)
날개

21

strcasecmp에 대한 다른 링크 http://man7.org/linux/man-pages/man3/strcasecmp.3p.html 에 따르면 소문자로 변환하는 것이 올바른 동작이라고합니다 (적어도 POSIX 로켈에서).

그 동작의 이유는 strcasecmp를 사용하여 문자열 배열을 정렬하면 합리적인 결과를 얻는 것이 필요하기 때문입니다.

그렇지 않으면 예를 들어 qsort를 사용하여 "A", "C", "_", "b"를 정렬하려고하면 결과는 비교 순서에 따라 달라집니다.


3
그렇지 않으면 예를 들어 qsort를 사용하여 "A", "C", "_", "b"를 정렬하려고하면 결과는 비교 순서에 따라 달라집니다. 좋은 지적. POSIX가 동작을 지정하는 이유 일 수 있습니다.
앤드류 헨리

6
보다 구체적으로, 정렬을 위해서는 총 주문이 필요합니다. 이는 질문에서와 같이 비교를 정의하는 경우에는 해당되지 않습니다 (전 이적이지 않기 때문에).
Dukeling

8

s1의 현재 문자가 문자이면 s2의 현재 문자가 문자인지 여부에 관계없이 항상 소문자로 변환됩니다.

맞습니다. 그리고 strcasecmp()함수 가해야 할 일입니다! 이 POSIX기능은 C표준의 일부가 아니라 " 공개 그룹 기본 사양, 문제 6 "의 기능입니다.

POSIX 로케일에서 strcasecmp () 및 strncasecmp ()는 문자열이 소문자로 변환 된 후 바이트 비교가 수행 된 것처럼 동작합니다. 다른 로케일에서는 결과가 지정되지 않습니다.

또한이 동작은 _stricmp()Visual Studio / MSCV에서 사용되는 함수와 도 관련이 있습니다 .

_stricmp 함수는 일반적으로 각 문자를 소문자로 변환 한 후 string1과 string2를 비교하고 관계를 나타내는 값을 반환합니다.


2

대한 ASCII 진수 코드 A입니다 65에 대한 _것입니다 95및 위해 a이다 97, 그래서 strcmp()할 가정 해 무엇을하고있어. 사 전적으로 말하기 _는 다음보다 작고 a큽니다 A.

strcasecmp()* A로 간주 되며 출력이 크기보다 크므로 정확합니다.aa_

* POSIX.1-2008 표준에 따르면 이러한 함수 (strcasecmp () 및 strncasecmp ())에 대해 설명합니다.

사용되는 로케일의 LC_CTYPE 범주가 POSIX 로케일에서 온 경우,이 함수는 문자열이 소문자로 변환 된 후 바이트 비교가 수행 된 것처럼 작동합니다. 그렇지 않으면 결과가 지정되지 않습니다.

출처 : http://man7.org/linux/man-pages/man3/strcasecmp.3.html


3
OP의 요점은 대소 문자를 구분하지 않고 비교할 A때보 다 "더 큰"것이며 _대소 문자를 구분할 때와 결과가 왜 다른지 궁금합니다.
anton.burger

6
명령문 Since A`되는 것은 잘못된 공제와 같이 그것은 간주됩니다 문자를 구분 strcasecmp ()는`경우입니다. 대소 문자를 구분하지 않는 루틴은 모든 대문자를 소문자 인 것처럼 취급하거나, 모든 소문자를 대문자 인 것처럼 취급하거나, 각 대문자를 해당하는 소문자와 동일하게 취급 할 수 있지만 그 반대도 여전히 비교할 수 있습니다 원래 값을 가진 문자가 아닌 문자에. 이 답변에는 그러한 가능성을 선호하는 이유가 나와 있지 않습니다 (문서가 소문자를 사용한다고 말하는 정확한 이유).
Eric Postpischil

@EricPostpischil POSIX.1-2008 표준은 이러한 함수 (strcasecmp () 및 strncasecmp ())에 대해 말합니다 : 사용되는 로케일의 LC_CTYPE 범주가 POSIX 로케일에서 온 경우,이 함수는 마치 문자열이로 변환 된 것처럼 작동합니다 소문자와 바이트 비교가 수행되었습니다. 그렇지 않으면 결과가 지정되지 않습니다.
anastaciu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.