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)
char
2의 보완이 아닌 음의 값을 가질 수 있다. (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()
다음을 제외한다.
- 대소문자를 구분하지 않는다.
- 두 문자열 중 하나가 null ptr일 경우 동작이 정의되지 않음(잘 정의되어 있음)정규
strncmp()
두 문자열 중 하나가 null ptr일 경우 정의되지 않은 동작이 있음(https://en.cppreference.com/w/cpp/string/byte/strncmp) 참조). - 돌아온다.
INT_MIN
입력 문자열 중 하나가 a인 경우 특수 Sentinel 오류 값으로 사용NULL
삐걱삐걱거리다
제한사항: 이 코드는 유니코드 문자 인코딩 UTF-8(가장 인기 있음), UTF-16 및 UTF-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!
참조:
- 이 질문 및 기타 답변은 영감을 주었고 통찰력을 제공했다(C의 Case Insensible String complex)
- http://www.cplusplus.com/reference/cstring/strncmp/
- https://en.wikipedia.org/wiki/ASCII
- https://en.cppreference.com/w/c/language/operator_precedence
- 위에서 코드 일부를 수정하기 위해 수행한 정의되지 않은 동작 연구(아래 주석 참조):
- Google "배열 한계를 벗어난 c 정의되지 않은 동작 읽기" 검색
- 글로벌 어레이에 액세스하는 것이 바인딩되지 않은 동작 외부에 있는가?
- https://en.cppreference.com/w/cpp/language/ub - 아래쪽에 있는 정말 훌륭한 "외부 링크"도 많이 보십시오!
- 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/
추가 조사 주제
- (참고: 이것은 C가 아닌 C++) 유니코드 문자의 소문자
- OnlineGDB에서 tolower_labor.c: https://onlinegdb.com/HyZieXcew
작업:
- 유니코드의 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
'Programing' 카테고리의 다른 글
Vue의 런타임 전용 빌드는 정확히 무엇이며 컴파일러 빌드와 어떻게 다른가? (0) | 2022.04.23 |
---|---|
Vue-tables-2(vuex) 반응성이 작동하지 않음 (0) | 2022.04.23 |
Vue: 소품으로서의 구성 요소/템플릿 (0) | 2022.04.22 |
로컬 저장소의 데이터를 사용하여 저장소를 초기화하는 방법? (0) | 2022.04.22 |
페이지를 새로 고친 후 Vuex persistedState가 작동하지 않음 (0) | 2022.04.22 |