2008-10-08 4 views
7

C++에서 UTF-8 문자열을 비교하고 정렬하는 방법을 찾고 있는데, 대/소문자를 구분하지 않고 custom collation function in SQLite에서 사용합니다.SQLite (C/C++)에 대/소문자를 구분하지 않는 UTF-8 문자열 데이터 정렬

  1. 이상적으로은 로캘과 무관해야합니다. 그러나 나는 숨을 멈추지 않을 것입니다. 내가 아는 한 데이터 정렬은 언어에 매우 의존적입니다. 따라서 로케일 전환을 의미한다고해도 영어가 아닌 다른 언어에서도 작동하는 것은 무엇이든 할 것입니다.
  2. 표준 C 또는 C++ 라이브러리 또는 (임베디드 시스템에 적합) 및 비 GPL (독점 시스템에 적합) 타사 라이브러리를 사용하는 옵션이 있습니다.
  3. 내가 지금까지 무엇을 가지고

: C 로케일과

  1. strcollstd::collate/std::collate_byname은 대소 문자를 구분합니다.
  2. 은 내가 POSIX의 strcasecmp를 사용하려고 (이들의 대소 문자를 구별하지 버전은? 있는가),하지만 "POSIX"는 POSIX 로케일에서

    , strcasecmp 이외의 로케일에 대한 not defined 것 같다()와 strncasecmp()는 변환을 위 아래로 수행 한 다음 바이트 비교를 수행합니다. 다른 로캘에서는 결과가 지정되지 않습니다.

    그리고, 참으로, strcasecmp의 결과는 GLIBC 리눅스에 로케일 사이에 변경되지 않습니다.

    strcasecmp('Äaa', 'äaa') == -32 
    strcoll('Äaa', 'äaa') == -32 
    strcasecmp('Äaa', 'äaa') == -32 
    strcoll('Äaa', 'äaa') == 7 
    strcasecmp('Äaa', 'äaa') == -32 
    strcoll('Äaa', 'äaa') == 7 
    

PS

을 그리고 네, 나는 ICU에 대해 알고 있지만, 우리는 인해 enormous size에 임베디드 플랫폼에서 사용할 수 없습니다 :

#include <clocale> 
#include <cstdio> 
#include <cassert> 
#include <cstring> 

const static char *s1 = "Äaa"; 
const static char *s2 = "äaa"; 

int main() { 
    printf("strcasecmp('%s', '%s') == %d\n", s1, s2, strcasecmp(s1, s2)); 
    printf("strcoll('%s', '%s') == %d\n", s1, s2, strcoll(s1, s2)); 
    assert(setlocale(LC_ALL, "en_AU.UTF-8")); 
    printf("strcasecmp('%s', '%s') == %d\n", s1, s2, strcasecmp(s1, s2)); 
    printf("strcoll('%s', '%s') == %d\n", s1, s2, strcoll(s1, s2)); 
    assert(setlocale(LC_ALL, "fi_FI.UTF-8")); 
    printf("strcasecmp('%s', '%s') == %d\n", s1, s2, strcasecmp(s1, s2)); 
    printf("strcoll('%s', '%s') == %d\n", s1, s2, strcoll(s1, s2)); 
} 

이 인쇄됩니다 .

답변

7

정말로 원하는 것은 논리적으로 불가능합니다. 로케일에 의존하지 않고, 캐릭터 라인을 소트하는 대문자와 소문자를 구별하지 않는다. 간단한 반대 예제는 "i"<> "나"입니까? 순수한 대답은 '아니요'이지만 터키에서는이 문자열이 불평등합니다. "i"는 "İ"로 대문자로 표시됩니다 (위의 점으로 U + 130 라틴어 캐피털 I)

UTF-8 문자열은 질문에 복잡성을 추가합니다. 적절한 로케일을 가지고 있다면 완벽하게 유효한 멀티 바이트 char * 문자열입니다. 그러나 C 나 C++ 표준 모두 그러한 로케일을 정의하지는 않습니다. 벤더 (너무 많은 임베디드 벤더, 미안, 여기에 genearl 대답 없음)를 확인하십시오. 따라서 멀티 바이트 인코딩이 UTF-8 인 로케일을 선택해야 mbscmp 함수가 작동합니다. 이것은 물론 로켈에 따라 정렬 순서에 영향을줍니다. const char *가 UTF-8 인 로케일이 없다면이 트릭을 전혀 사용할 수 없습니다. (내가 알기에 마이크로 소프트의 CRT는 멀티 바이트 코드가 2 바이트까지만 처리 할 수 ​​있고 UTF-8은 3 개가 필요하다)

wchar_t도 표준 해결책이 아니다. 그것은 아마도 당신이 멀티 바이트 인코딩을 다룰 필요가 없을 정도로 넓지 만 당신의 데이터 정렬은 여전히 ​​로케일 (LC_COLLATE)에 의존 할 것입니다. 그러나 wchar_t를 사용하면 const char *에 UTF-8을 사용하지 않는 로케일을 선택할 수 있습니다.

이렇게하면 기본적으로 문자열을 소문자로 변환하고 비교하여 순서를 작성할 수 있습니다. 완벽하지는 않습니다. L "ß"== L "ss"라고 기대합니까? 그것도 같은 길이가 아닙니다. 그러나 독일인은 평등하다고 생각해야합니다. 그걸로 살 수 있습니까?

:

+2

: 이들은 "해결"또는 기타 UTF-8 또는 더 전에 수천 번 처리되지해야합니다. MS Word에는 항상 "토글 케이스"기능이 있습니다. 유니 코드 이전 버전에서는 어떻게 작동합니까? WordPerfect는 어떻게 운영 되었습니까? 델파이에서 일하는 것을 제외하면 OP와 동일한 문제가 발생합니다. 영어, 독일어 또는 (내 경우에는) 폴란드어 로켈에 설치되어 있는지 여부에 관계없이 대/소문자를 구분하지 않는 SELECT (및 ORDER BY)를 수행하는 여러 Windows sqlite 기반 응용 프로그램을 보았습니다. 파이어 폭스를 시도해보십시오 :) 어떻게 그 일을합니까? –

+0

보통 부정확하다 : 폴란드 인에는 IIRC가 단단한 케이스가 없다; 폴란드어로 사용 된 모든 비 ASCII 문자는 ASCII 문자를 기반으로합니다. – MSalters

+0

터키어 I 문제를 제외하고는 유니 코드 케이스 접기 알고리즘 (http://www.unicode.org/reports/tr44/)이 매우 잘 작동합니다. – dalle

0

표준 C/C++ 라이브러리 기능을 사용할 수 있다고 생각하지 않습니다. 직접 롤백하거나 제 3 자 라이브러리를 사용해야합니다. 로캘 별 데이터 정렬에 대한 전체 유니 코드 사양은 여기에서 확인할 수 있습니다. http://www.unicode.org/reports/tr10/ (경고 : 문서)

0

Windows에서는 OS 함수 CompareStringW에서 폴백을 호출하고 NORM_IGNORECASE 플래그를 사용할 수 있습니다. 먼저 UTF-8 문자열을 UTF-16으로 변환해야합니다. 그렇지 않은 경우 IBM International Components for Unicode을 살펴보십시오.

0

직접 롤백하거나 제 3 자 라이브러리를 사용해야합니다. 진정한 국제 지원을 받으려면 따라야 할 규칙이 많기 때문에 제 3 자 라이브러리를 권합니다. 전문가가 전문가와 상담하는 것이 가장 좋습니다.

0

예제 코드의 형태로 답을 찾을 수는 없지만 UTF-8 바이트 스트림에는 실제로 유니 코드 문자가 포함되어 있으며 C/C++ 런타임 라이브러리의 wchar_t 버전을 사용해야한다는 점을 지적해야합니다.

먼저 UTF-8 바이트를 wchar_t 문자열로 변환해야합니다. 이것은 UTF-8 인코딩 표준이 very well documented이기 때문에 그리 어렵지 않습니다. 내가이 일을했기 때문에 이것을 알지만, 그 코드를 당신과 공유 할 수는 없습니다.

0

당신이 검색을 할 그것을 사용하고 만 로케일 정렬하는 경우, 당신의 함수는 간단한 같은 테이블을 사용하여 문자의 사람 당 하나의 바이트로 모두 멀티 바이트 문자열을 변환 기능을 대신 호출하는 것이 좋습니다 A ->를
A ->를
A ->를
ß -> SS
Ç -> C

에 그리고 간단하게 strcmp와 호출하고 결과를 반환합니다. 독일의 "ß"문자 (모두 같은 풍부한 경우) 귀하의 예를 소개