C ++ 11에서 유니 코드는 얼마나 잘 지원됩니까?


183

C ++ 11이 유니 코드를 지원한다고 읽었습니다. 그것에 대한 몇 가지 질문 :

  • C ++ 표준 라이브러리는 유니 코드를 얼마나 잘 지원합니까?
  • 합니까는 std::string무엇을해야합니까?
  • 어떻게 사용합니까?
  • 잠재적 인 문제는 어디에 있습니까?

19
"std :: string은 어떻게해야합니까?" 어떻게해야한다고 생각하십니까?
R. Martinho Fernandes

2
내 utf8 요구에 utfcpp.sourceforge.net 을 사용 합니다. 유니 코드 문자열의 반복자를 제공하는 간단한 헤더 파일입니다.
fscan

2
std :: string은 바이트, 즉 UTF-8 인코딩의 코드 단위 시퀀스를 저장해야합니다. 예, 처음부터 그렇게합니다. utf8everywhere.org
Pavel Radzivilovsky 2016 년

3
유니 코드 지원의 가장 큰 잠재적 문제는 유니 코드와 정보 기술 자체에서의 사용에 있습니다. 유니 코드는 용도에 적합하지 않으며 설계되지 않았습니다. 유니 코드는 3 ~ 4 가지의 다른 의미와 3 ~ 4 가지의 동일한 글리프를 구성하는 방법을 포함하여 모든 사람이 어딘가에 작성한 모든 가능한 글리프를 재현 할 수 있도록 설계되었습니다. 일상 언어에 사용되는 데 유용하지 않으며, 적용 가능하거나 쉽게 또는 모호하지 않게 처리 할 수 ​​없습니다.
데이먼

11
예, 일상 언어에 사용하도록 설계되었습니다. 적어도 내 꺼야 그리고 아마도 당신도 마찬가지입니다. 인간의 텍스트를 일반적인 방식으로 처리하는 것은 매우 어려운 작업이라는 것이 밝혀졌습니다. 캐릭터가 무엇인지 명확하게 정의하는 것은 불가능합니다. 일반적인 글리프 재현은 실제로 유니 코드 헌장의 일부가 아닙니다.
Jean-Denis Muys

답변:


267

C ++ 표준 라이브러리는 유니 코드를 얼마나 잘 지원합니까?

몹시.

유니 코드 지원을 제공 할 수있는 라이브러리 기능을 통한 빠른 스캔은 다음 목록을 제공합니다.

  • 문자열 라이브러리
  • 현지화 라이브러리
  • 입출력 라이브러리
  • 정규식 라이브러리

첫 번째를 제외한 모든 것이 끔찍한 지원을 제공한다고 생각합니다. 다른 질문을 빠르게 우회 한 후 더 자세히 설명하겠습니다.

합니까는 std::string무엇을해야합니까?

예. C ++ 표준에 따르면, 이것이 std::string형제 자매가해야 할 일입니다.

클래스 템플릿 basic_string은 위치 0에 시퀀스의 첫 번째 요소가 포함 된 다양한 임의의 문자형 객체로 구성된 시퀀스를 저장할 수있는 객체를 설명합니다.

글쎄, std::string괜찮아. 이것이 유니 코드 특정 기능을 제공합니까? 아니.

그래야합니까? 아마 아닙니다. std::string일련의 char객체 로 괜찮습니다 . 유용합니다. 유일한 성가심은 텍스트에 대한 매우 낮은 수준의 견해이며 표준 C ++은 더 높은 수준의 내용을 제공하지 않는다는 것입니다.

어떻게 사용합니까?

일련의 char객체 로 사용하십시오 . 다른 척하는 것은 고통으로 끝날 수밖에 없다.

잠재적 인 문제는 어디에 있습니까?

여기 저기? 보자 ...

문자열 라이브러리

문자열 라이브러리는 basic_string표준을 "char-like objects"라고 부르는 시퀀스 일뿐입니다. 코드 단위라고합니다. 높은 수준의 텍스트보기를 원한다면 이것이 원하는 것이 아닙니다. 직렬화 / 직렬화 / 저장에 적합한 텍스트보기입니다.

또한 좁은 세계와 유니 코드 세계 사이의 간극을 연결하는 데 사용할 수있는 C 라이브러리의 일부 도구 인 c16rtomb/ mbrtoc16c32rtomb/를 제공 mbrtoc32합니다.

현지화 라이브러리

지역화 라이브러리는 여전히 "char-like objects"중 하나가 "character"와 같다고 생각합니다. 이것은 물론 어리석은 일이며 ASCII와 같은 작은 유니 코드 하위 집합을 넘어 많은 작업을 제대로 수행 할 수 없습니다.

예를 들어, <locale>헤더 에서 표준이 "편의 인터페이스"라고 부르는 것을 고려하십시오 .

template <class charT> bool isspace (charT c, const locale& loc);
template <class charT> bool isprint (charT c, const locale& loc);
template <class charT> bool iscntrl (charT c, const locale& loc);
// ...
template <class charT> charT toupper(charT c, const locale& loc);
template <class charT> charT tolower(charT c, const locale& loc);
// ...

어떻게 이런 기능 중 하나는 같이 제대로 분류합니다, 말, U +의 1F34C의 ʙᴀɴᴀɴᴀ에 기대 u8"🍌"거나 u8"\U0001F34C"? 이러한 함수는 하나의 코드 단위 만 입력으로 사용하므로 작동 할 방법이 없습니다.

UTF-32의 단일 코드 단위 인 경우 char32_t에만 적절한 로케일로 작업 할 수 있습니다 U'\U0001F34C'.

그러나, 여전히 당신은 단지 간단한 케이스로 변환 얻을 것을 의미 toupper하고 tolower"SS"를 "ß"uppercases ☦하지만, 예를 들어, 독일의 일부 로케일에 대해 좋은 충분하지 않습니다, toupper단지 하나 개의 반환 할 수 문자 코드 단위를.

다음은, wstring_convert/ wbuffer_convert표준 코드 변환면.

wstring_convert주어진 인코딩의 문자열을 다른 주어진 인코딩의 문자열로 변환하는 데 사용됩니다. 이 변환에는 두 가지 문자열 유형이 있으며 표준은 바이트 문자열과 넓은 문자열을 호출합니다. 이러한 용어는 실제로 오해의 소지가 있으므로, 대신 "직렬화"및 "직렬화 해제"를 각각 선호합니다. †.

변환 할 인코딩은에 템플릿 유형 인수로 전달 된 codecvt (코드 변환 패싯)에 의해 결정됩니다 wstring_convert.

wbuffer_convert바이트 직렬화 된 스트림 버퍼 를 감싸는 넓은 역 직렬화 된 스트림 버퍼 와 유사한 기능을 수행합니다 . 모든 입출력은 codecvt 인수에 의해 주어진 인코딩과의 변환과 함께 기본 바이트 직렬 스트림 버퍼를 통해 수행됩니다 . 쓰기는 해당 버퍼로 직렬화 한 다음 버퍼에서 읽고 읽기는 버퍼로 읽은 후 직렬화 해제합니다.

표준은 이러한 시설에 사용하기위한 몇 가지 codecvt 클래스 템플릿을 제공 : codecvt_utf8, codecvt_utf16, codecvt_utf8_utf16, 일부 codecvt전문. 이러한 표준 패싯을 함께 사용하면 다음과 같은 변환이 모두 제공됩니다. (참고 : 다음 목록에서 왼쪽의 인코딩은 항상 직렬화 된 문자열 / streambuf이고 오른쪽의 인코딩은 항상 역 직렬화 된 문자열 / streambuf입니다. 표준은 양방향으로 변환 할 수 있습니다).

  • UTF-8 ↔ UCS-2 codecvt_utf8<char16_t>, 및 codecvt_utf8<wchar_t>여기서 sizeof(wchar_t) == 2;
  • UTF-8과 UTF-32 ↔ codecvt_utf8<char32_t>, codecvt<char32_t, char, mbstate_t>및 ;codecvt_utf8<wchar_t>sizeof(wchar_t) == 4
  • UTF-16 ↔ UCS-2 codecvt_utf16<char16_t>와 ;codecvt_utf16<wchar_t>sizeof(wchar_t) == 2
  • UTF-16 ↔ UTF-32 with codecvt_utf16<char32_t>, 및 codecvt_utf16<wchar_t>여기서 sizeof(wchar_t) == 4;
  • UTF-8과 UTF-16 ↔ codecvt_utf8_utf16<char16_t>, codecvt<char16_t, char, mbstate_t>및 ;codecvt_utf8_utf16<wchar_t>sizeof(wchar_t) == 2
  • 좁은 ↔ 넓은 codecvt<wchar_t, char_t, mbstate_t>
  • 와 no-op codecvt<char, char, mbstate_t>.

이것들 중 몇 가지는 유용하지만 여기에는 많은 어색한 것들이 있습니다.

첫째로, 거룩한 대리자! 그 명명 체계가 지저분합니다.

그런 다음 UCS-2가 많이 지원됩니다. UCS-2는 기본 다국어 평면 만 지원하므로 1996 년에 대체 된 Unicode 1.0의 인코딩입니다. 위원회가 20 년 전에 대체 한 인코딩에 중점을 두는 것이 바람직한 이유는 모르겠습니다. ‡ 더 많은 인코딩에 대한 지원이 나쁘거나 다른 것과 같지 않지만 UCS-2가 너무 자주 나타납니다.

나는 그 말을 char16_t분명히 UTF-16 코드 단위를 저장하기위한 것입니다. 그러나 이것은 다르게 생각하는 표준의 일부입니다. codecvt_utf8<char16_t>UTF-16과는 아무런 관련이 없습니다. 예를 들어, wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")잘 컴파일되지만 무조건 실패합니다. 입력은 UCS-2 문자열로 처리되며 u"\xD83C\xDF4C"UTF-8은 0xD800-0xDFFF 범위의 값을 인코딩 할 수 없으므로 UTF-8로 변환 할 수 없습니다.

여전히 UCS-2에서 UTF-16 바이트 스트림에서 이러한 패싯이있는 UTF-16 문자열로 읽을 수있는 방법이 없습니다. UTF-16 바이트 시퀀스가 ​​있으면 문자열을로 직렬화 해제 할 수 없습니다 char16_t. 이것은 신원 전환이기 때문에 놀랍습니다. 그럼에도 불구하고 UTF-16 스트림에서 UCS-2 문자열로 역 직렬화를 지원한다는 사실이 더 놀랍습니다 codecvt_utf16<char16_t>.

UTF-16-as-bytes 지원은 BOM에서 엔디안 감지 또는 코드에서 명시 적으로 선택 지원합니다. 또한 BOM이 있거나없는 결과물 생성을 지원합니다.

더 흥미로운 전환 가능성이 없습니다. UTF-8은 역 직렬화 된 형식으로 지원되지 않으므로 UTF-16 바이트 스트림 또는 문자열에서 UTF-8 문자열로 역 직렬화하는 방법은 없습니다.

그리고 여기서 좁고 넓은 세계는 UTF / UCS 세계와 완전히 분리되어 있습니다. 이전 스타일의 좁은 / 와이드 인코딩과 유니 코드 인코딩 간에는 변환이 없습니다.

입출력 라이브러리

I / O 라이브러리는 위에서 설명한 wstring_convertwbuffer_convert기능을 사용하여 유니 코드 인코딩으로 텍스트를 읽고 쓰는 데 사용할 수 있습니다 . 표준 라이브러리 의이 부분에서 지원해야 할 것이 많지 않다고 생각합니다.

정규식 라이브러리

전에 스택 오버플로에서 C ++ 정규식 및 유니 코드 관련 문제에 대해 설명했습니다 . 여기서는 모든 점을 반복하지는 않지만 C ++ 정규 표현식에는 레벨 1 유니 코드 지원이 없다고 말하면 UTF-32를 사용하지 않고도 사용할 수있는 최소한의 수준입니다.

그게 다야?

그래 그거야. 이것이 기존 기능입니다. 정규화 나 텍스트 분할 알고리즘처럼 보이지 않는 유니 코드 기능이 많이 있습니다.

U + 1F4A9 . C ++에서 더 나은 유니 코드 지원을 얻을 수있는 방법이 있습니까?

일반적인 용의자 : ICUBoost.Locale .


† 바이트 문자열은 당연히 바이트 문자열, 즉 char객체입니다. 그러나 항상 객체 배열 인 와이드 문자열 리터럴 과 달리이 wchar_t컨텍스트에서 "와이드 문자열"은 반드시 wchar_t오브젝트 문자열 일 필요는 없습니다 . 실제로이 표준은 "와이드 문자열"의 의미를 명시 적으로 정의하지 않으므로 사용법에서 의미를 추측해야합니다. 표준 용어는 조잡하고 혼동 스럽기 때문에 명확성을 위해 고유 한 용어를 사용합니다.

UTF-16과 같은 인코딩은의 시퀀스로 저장 될 수 있으며 char16_t엔디안이 없습니다. 또는 엔디안을 갖는 바이트 시퀀스로 저장 될 수 있습니다 (각 연속되는 바이트 쌍은 char16_t엔디안에 따라 다른 값을 나타낼 수 있음 ). 표준은이 두 가지 형식을 모두 지원합니다. char16_t프로그램의 내부 조작에 시퀀스 가 더 유용합니다. 일련의 바이트는 그러한 문자열을 외부 세계와 교환하는 방법입니다. "바이트"와 "와이드"대신에 사용할 용어는 "직렬화"및 "직렬화 해제"입니다.

‡ "그러나 Windows!"라고 말하는 경우 보류 🐎🐎을 . Windows 2000 이후의 모든 Windows 버전은 UTF-16을 사용합니다.

☦ 그렇습니다. Grozes Eszett (ẞ) 에 대해 알고 있지만 밤새 ß 대문자를 사용하도록 모든 독일 로케일을 변경하더라도 여전히 실패 할 수있는 다른 경우가 많이 있습니다. 대문자로 바꾸십시오 U + FB00 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟɪɢᴀᴛᴜʀᴇ ғғ. ʟᴀᴛɪɴ ᴄᴀᴘɪᴛᴀʟ ʟɪɢᴀᴛᴜʀᴇ ғғ가 없습니다. 그것은 단지 두 개의 F로 대문자입니다. 또는 U + 01F0 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟᴇᴛᴛᴇʀ ᴊ ᴡɪᴛʜ ᴄᴀʀᴏɴ; 사전 구성된 자본이 없습니다. 그것은 대문자 J와 결합 카론에 대문자입니다.


26
내가 그것에 대해 더 많이 읽을수록, 나는이 모든 것에 대해 이해하지 못하는 느낌을 더 많이 갖게됩니다. 나는 몇 달 전에이 물건을 대부분 읽었지만 여전히 모든 것을 다시 발견하는 것처럼 느낍니다 ... 지금 조금 아프게되는 가난한 뇌를 위해 간단하게 유지하기 위해 utf8의 모든 조언 은 여전히 ​​유효합니다. 권리? 시스템 설정에 관계없이 사용자가 파일을 열고 쓸 수있게하려면 파일 이름을 물어 std :: string에 저장하면 Windows에서도 모든 것이 제대로 작동합니까? (다시)
물어봐서

5
@Uflex std :: string으로 실제로 할 수있는 것은 이진 얼룩으로 처리하는 것입니다. 적절한 유니 코드 구현에서는 내부 (구현 세부 사항에 깊이 숨겨져 있기 때문에) 나 외부 인코딩 문제 (아직도 인코더 / 디코더를 사용할 수 있어야 함)가 없습니다.
고양이 플러스 플러스

3
아마도 Uflex. 이해하지 못하는 조언을 따르는 것이 좋은 생각인지 모르겠습니다.
R. Martinho Fernandes

1
C ++ 2014/17에는 유니 코드 지원 제안이 있습니다. 그러나 그것은 1 년, 아마도 4 년 정도 떨어져 지금은 거의 사용되지 않습니다. open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3572.html
graham.reeds 10

20
@ graham.reeds haha, 감사합니다. 그러나 나는 그것을 알고있었습니다. "알림"섹션을 확인하십시오;)
R. Martinho Fernandes

40

표준 라이브러리 는 유니 코드를 지원하지 않습니다 (합리적인 의미로 지원됨).

std::string더 좋은보다하지 std::vector<char>가 유니 코드로 완전히 망각 (또는 다른 표현 / 인코딩) 단순히 같은 내용 치료 : BLOB 바이트를.

blob 만 저장하고 연결해야하는 경우 에는 잘 작동합니다. 그러나 유니 코드 기능 ( 코드 포인트 수, 그래프 수 등)을 원하면 운이 좋지 않습니다.

내가 아는 유일한 종합 라이브러리는 ICU 입니다. C ++ 인터페이스는 Java 인터페이스에서 파생되었으므로 관용적이지는 않습니다.


2
Boost.Locale어떻 습니까?
Uflex

11
@Uflex : 링크 한 페이지 에서이 목표를 달성하기 위해 Boost.Locale은 최첨단 유니 코드 및 현지화 라이브러리를 사용합니다 : ICU-국제 구성 요소 유니 코드.
Matthieu M.

1
Boost.Locale은 다른 비 ICU 백엔드를 지원합니다. boost.org/doc/libs/1_53_0/libs/locale/doc/html/…
Superfly Jon

@SuperflyJon : 맞습니다. 그러나 같은 페이지에 따르면 비 ICU 백엔드의 유니 코드 지원은 "심각하게 제한됩니다".
Matthieu M.

24

당신은 안전하게에서 UTF-8로 저장할 수 있습니다 std::string(또는에서 char[]또는 char*, 그 문제에 대한) 때문에 유니 코드 NUL (+ 0000 U)는 UTF-8 널 바이트가 있다는 사실과이 유일한 방법 널입니다 바이트는 UTF-8에서 발생할 수 있습니다. 따라서 UTF-8 문자열은 모든 C 및 C ++ 문자열 함수에 따라 올바르게 종료되며 C ++ iostream ( std::coutstd::cerr로케일이 UTF-8 인 경우)을 사용 하여 슬링 할 수 있습니다 .

std::stringUTF-8에서 할 수없는 것은 코드 포인트의 길이를 얻는 것입니다. UTF-8의 ASCII 하위 집합에있을 때 코드 std::string::size()길이와 바이트 수와 같은 문자열 길이를 바이트 단위로 알려줍니다 .

코드 포인트 수준 에서 UTF-8 문자열을 처리해야하는 경우 (예 : 저장 및 인쇄가 아닌) 내부 널 바이트가 많은 UTF-16을 처리하는 경우 조사해야합니다. 넓은 문자열 유형


3
std::string포함 된 null을 사용하여 iostream에 던져 넣을 수 있습니다.
R. Martinho Fernandes

3
완전히 의도 된 것입니다. 여전히 작동 c_str()하기 때문에 전혀 깨지지 않습니다 size(). 손상된 API (즉, 대부분의 C 세계와 같이 포함 된 null을 처리 할 수없는 API) 만 중단됩니다.
R. Martinho Fernandes

1
데이터를 null로 끝나는 C 문자열로 반환해야 c_str()하므로 포함 된 null이 끊어집니다. 이는 c_str()C 문자열에 null이 포함될 수 없기 때문에 불가능합니다.
uckelman

4
더 이상은 아닙니다. c_str()이제는 단순히 data()모든 것과 같은 것을 반환합니다 . 크기가 큰 API는이를 사용할 수 있습니다. 그렇지 않은 API는 할 수 없습니다.
R. Martinho Fernandes

6
약간의 차이로 c_str()인해 결과에 NUL char-like 객체가 오는지 확인하지만 그렇게 생각 data()하지 않습니다. 아니, data()지금도 그렇게 보인다 . (물론 터미네이터 검색에서 크기를 추론하는 대신 크기를 소비하는 API에는 필요하지 않습니다.)
Ben Voigt

8

C ++ 11에는 유니 코드를위한 몇 가지 새로운 리터럴 문자열 유형 이 있습니다.

불행하게도 UTF-8과 같은 비 균일 인코딩에 대한 표준 라이브러리의 지원은 여전히 ​​나쁩니다. 예를 들어 UTF-8 문자열의 길이 (코드 포인트)를 얻는 좋은 방법은 없습니다.


비 라틴 언어를 지원하려면 파일 이름으로 std :: wstring을 사용해야합니까? 새로운 문자열 리터럴은 문자열이 일반적으로 사용자로부터 오는 것처럼 실제로 여기에서 도움이되지 않기 때문에 ...
Uflex

7
@Uflex std::string는 문제없이 UTF-8 문자열을 보유 할 수 있지만 length메소드는 코드 포인트 수가 아닌 문자열의 바이트 수를 리턴합니다.
일부 프로그래머 친구

8
솔직히 말해서, 문자열의 코드 포인트 길이를 얻는 것은 많은 용도가 없습니다. 예를 들어, 바이트 단위의 길이를 사용하여 버퍼를 올바르게 사전 할당 할 수 있습니다.
R. Martinho Fernandes

2
UTF-8 문자열의 코드 포인트 수는 그리 흥미로운 숫자가 아닙니다. 하나는 ñ'LATIN SMALL LETTER N WITH TILDE'(U + 00F1) (하나의 코드 포인트) 또는 'LATIN SMALL LETTER N'( U + 006E) 다음에 두 개의 코드 포인트 인 'COMBINING TILDE'(U + 0303)가옵니다.
Martin Bonner는 Monica

"필요하지 않은 코드 포인트 수"등과 같이 "필요하지 않으며 필요하지 않습니다"에 대한 모든 의견은 약간 비린내 들립니다. 일단 utf8 소스 코드를 구문 분석 해야하는 구문 분석기를 작성하면 LATIN SMALL LETTER N' == 고려 여부에 관계없이 구문 분석기의 스펙에 달려 (U+006E) followed by 'COMBINING TILDE' (U+0303)있습니다.
BitTickler

4

그러나 tiny-utf8 이라는 매우 유용한 라이브러리가 있습니다.이 라이브러리 는 기본적으로 / 대신 드롭 인 대체 입니다 . 여전히 누락 된 utf8 문자열 컨테이너 클래스의 간격을 채우는 것을 목표로합니다.std::stringstd::wstring

이것은 utf8 문자열을 사용하는 가장 편안한 방법 일 수 있습니다 (즉, 유니 코드 정규화 및 유사한 것들이 없음). 코드 포인트 에서 편안하게 작업 하는 반면 문자열은 런 길이 인코딩으로 인코딩 된 상태로 유지됩니다 char.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.