엄격한 유니 코드 프로그래밍을 수행하려면 어떤 전제 조건이 필요합니까?
이 내 코드는 사용하지 말아야 것을 의미합니까 char
어디서나 종류와 그 기능을 처리 할 수있는 사용해야 wint_t
하고 wchar_t
?
그리고이 시나리오에서 멀티 바이트 문자 시퀀스가 수행하는 역할은 무엇입니까?
답변
이것은 “엄격한 유니 코드 프로그래밍”자체가 아니라 실제적인 경험에 관한 것입니다.
우리 회사에서 한 일은 IBM의 ICU 라이브러리를 중심으로 래퍼 라이브러리를 만드는 것이 었습니다. 래퍼 라이브러리에는 UTF-8 인터페이스가 있으며 ICU를 호출해야 할 때 UTF-16으로 변환됩니다. 우리의 경우 성능 저하에 대해 너무 걱정하지 않았습니다. 성능이 문제가되었을 때 우리는 자체 데이터 유형을 사용하여 UTF-16 인터페이스도 제공했습니다.
응용 프로그램은 일부 경우 특정 문제를 인식해야하지만 대부분있는 그대로 (char 사용) 유지 될 수 있습니다. 예를 들어, strncpy () 대신 UTF-8 시퀀스를 자르지 않는 래퍼를 사용합니다. 우리의 경우에는 이것으로 충분하지만 문자 결합에 대한 검사도 고려할 수 있습니다. 또한 코드 포인트 수, 자소 수 등을 세는 래퍼도 있습니다.
다른 시스템과 인터페이스 할 때 때때로 사용자 지정 문자 구성을 수행해야하므로 응용 프로그램에 따라 유연성이 필요할 수 있습니다.
wchar_t를 사용하지 않습니다. ICU를 사용하면 이식성에서 예상치 못한 문제를 피할 수 있습니다 (물론 다른 예상치 못한 문제는 아님 :-).
답변
C99 이하
C 표준 (C99)은 와이드 문자와 멀티 바이트 문자를 제공하지만 와이드 문자가 무엇을 보유 할 수 있는지에 대한 보장이 없기 때문에 값이 다소 제한됩니다. 주어진 구현에 대해 유용한 지원을 제공하지만 코드가 구현간에 이동할 수 있어야한다면 유용 할 것이라는 보장이 충분하지 않습니다.
결과적으로 Hans van Eck (ICU-International Components for Unicode-library를 둘러싼 래퍼를 작성하는 것)가 제안한 접근 방식은 IMO입니다.
UTF-8 인코딩에는 많은 장점이 있습니다. 그 중 하나는 데이터를 엉망으로 만들지 않으면 (예를 들어 잘라내어) UTF-8의 복잡성을 완전히 인식하지 못하는 함수로 복사 할 수 있다는 것입니다. 부호화. 이것은 wchar_t
.
전체 유니 코드는 21 비트 형식입니다. 즉, 유니 코드는 U + 0000에서 U + 10FFFF까지의 코드 포인트를 예약합니다.
(UTF 유니 코드 변환 형식을 의미합니다 – 참조 UTF-8, UTF-16, UTF-32 형식에 대한 유용한 것들 중 하나는 유니 코드 )은 정보의 손실없이 세 가지 표현 사이의 변환을 할 수 있다는 것입니다. 각각은 다른 사람이 나타낼 수있는 모든 것을 나타낼 수 있습니다. UTF-8과 UTF-16은 모두 다중 바이트 형식입니다.
UTF-8은 멀티 바이트 형식으로 잘 알려져 있으며, 신중한 구조로 인해 문자열의 모든 지점에서 시작하여 안정적으로 문자열의 문자 시작을 찾을 수 있습니다. 1 바이트 문자는 상위 비트가 0으로 설정됩니다. 멀티 바이트 문자는 비트 패턴 110, 1110 또는 11110 (2 바이트, 3 바이트 또는 4 바이트 문자의 경우) 중 하나로 시작하는 첫 번째 문자를 가지며 후속 바이트는 항상 10으로 시작합니다. 연속 문자는 항상 범위 0x80 .. 0xBF. UTF-8 문자가 가능한 최소 형식으로 표시되어야한다는 규칙이 있습니다. 이러한 규칙의 한 가지 결과는 바이트 0xC0 및 0xC1 (또한 0xF5..0xFF)이 유효한 UTF-8 데이터에 나타날 수 없다는 것입니다.
U+0000 .. U+007F 1 byte 0xxx xxxx
U+0080 .. U+07FF 2 bytes 110x xxxx 10xx xxxx
U+0800 .. U+FFFF 3 bytes 1110 xxxx 10xx xxxx 10xx xxxx
U+10000 .. U+10FFFF 4 bytes 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
원래는 유니 코드가 16 비트 코드 세트이고 모든 것이 16 비트 코드 공간에 맞기를 바랐습니다. 불행히도 현실 세계는 더 복잡하며 현재의 21 비트 인코딩으로 확장되어야했습니다.
따라서 UTF-16은 ‘Basic Multilingual Plane’에 대한 단일 단위 (16 비트 단어) 코드 세트입니다. 즉, 유니 코드 코드 포인트 U + 0000 .. U + FFFF가있는 문자를 의미하지만 두 단위 (32 비트)를 사용합니다. 이 범위를 벗어난 문자. 따라서 UTF-16 인코딩과 함께 작동하는 코드는 UTF-8과 마찬가지로 가변 너비 인코딩을 처리 할 수 있어야합니다. 이중 단위 문자에 대한 코드를 서로 게이트라고합니다.
서로 게이트는 UTF-16에서 쌍을 이루는 코드 단위의 선행 및 후행 값으로 사용하도록 예약 된 두 가지 특수 유니 코드 값 범위의 코드 포인트입니다. 선행 (높음이라고도 함) 서로 게이트는 U + D800에서 U + DBFF까지이고 후행 (낮음) 서로 게이트는 U + DC00에서 U + DFFF까지입니다. 문자를 직접 나타내지 않고 한 쌍으로 만 나타 내기 때문에 서로 게이트라고합니다.
물론 UTF-32는 단일 저장소 단위로 모든 유니 코드 코드 포인트를 인코딩 할 수 있습니다. 계산에는 효율적이지만 저장에는 적합하지 않습니다.
ICU 및 유니 코드 웹 사이트 에서 더 많은 정보를 찾을 수 있습니다 .
C11 및 <uchar.h>
C11 표준은 규칙을 변경했지만 모든 구현이 지금 (2017 년 중반)에도 변경 사항을 따라 잡은 것은 아닙니다. C11 표준은 유니 코드 지원에 대한 변경 사항을 다음과 같이 요약합니다.
- 유니 코드 문자 및 문자열 (
<uchar.h>
) (원래 ISO / IEC TR 19769 : 2004에 지정됨)
다음은 기능에 대한 최소한의 개요입니다. 사양에는 다음이 포함됩니다.
6.4.3 범용 문자 이름
구문
universal-character-name :
\u
hex-quad
\U
hex-quad hex-quad
hex-quad :
16 진수 숫자 16 진수 16 진수 16 진수 16 진수7.28 유니 코드 유틸리티
<uchar.h>
헤더
<uchar.h>
는 유니 코드 문자를 조작하기위한 유형과 함수를 선언합니다.선언 된 유형은
mbstate_t
(7.29.1에 설명 됨) 및size_t
(7.19에 설명 됨)입니다.
char16_t
16 비트 문자에 사용되는 부호없는 정수 유형이며,
uint_least16_t
7.20.1.2에 설명 된 것과 동일한 유형입니다 . 과
char32_t
32 비트 문자에 사용되는 부호없는 정수 유형이며 동일한 유형입니다
uint_least32_t
(7.20.1.2에서도 설명 됨).
(상호 참조 번역 : <stddef.h>
define size_t
,
<wchar.h>
define mbstate_t
, 및 <stdint.h>
정의 uint_least16_t
및 uint_least32_t
.) <uchar.h>
헤더는 또한 최소한의 (다시 시작 가능) 변환 함수 세트를 정의합니다.
mbrtoc16()
c16rtomb()
mbrtoc32()
c32rtomb()
\unnnn
또는 \U00nnnnnn
표기법을 사용하여 식별자에 유니 코드 문자를 사용할 수있는 규칙이 있습니다 . 식별자에서 이러한 문자에 대한 지원을 적극적으로 활성화해야 할 수 있습니다. 예를 들어, GCC는 -fextended-identifiers
식별자에서이를 허용 해야 합니다.
macOS Sierra (10.12.5)는 하나의 플랫폼이지만 <uchar.h>
.
답변
이 FAQ 는 풍부한 정보입니다. 해당 페이지와 Joel Spolsky의이 기사 사이 에서 좋은 출발을 할 수 있습니다.
나는 그 과정에서 한 가지 결론을 내렸다.
-
wchar_t
Windows에서는 16 비트이지만 다른 플랫폼에서는 반드시 16 비트는 아닙니다. Windows에서 필요한 악이라고 생각하지만 다른 곳에서는 피할 수 있습니다. Windows에서 중요한 이유는 이름에 비 ASCII 문자가 포함 된 파일 (함수의 W 버전과 함께)을 사용해야하기 때문입니다. -
wchar_t
문자열 을받는 Windows API는 UTF-16 인코딩을 예상합니다. 이것은 UCS-2와 다릅니다. 서로 게이트 쌍을 기록해 둡니다. 이 테스트 페이지 에는 계몽 테스트가 있습니다. -
당신이 윈도우에있어 프로그래밍, 당신이 사용할 수없는 경우
fopen()
,fread()
,fwrite()
, 등 그들은 단지 걸릴 이후char *
와 UTF-8 인코딩을 이해하지 않습니다. 휴대 성을 어렵게 만듭니다.
답변
엄격한 유니 코드 프로그래밍을 수행하려면 :
- 만있는 문자열 API를 사용하는 유니 코드 인식 ( NOT
strlen
,strcpy
… 그러나 그들의 WideString으로 대응wstrlen
,wsstrcpy
…) - 텍스트 블록을 다룰 때는 유니 코드 문자 (utf-7, utf-8, utf-16, ucs-2, …)를 손실없이 저장할 수있는 인코딩을 사용하십시오.
- OS 기본 문자 집합이 유니 코드와 호환되는지 확인합니다 (예 : utf-8).
- 유니 코드와 호환되는 글꼴 사용 (예 : arial_unicode)
멀티 바이트 문자 시퀀스는 UTF-16 인코딩 (와 함께 일반적으로 사용되는 인코딩)보다 이전의 인코딩이며 wchar_t
나에게는 오히려 Windows 전용 인 것 같습니다.
나는 들어 본 적이 없다 wint_t
.
답변
가장 중요한 것은 항상 텍스트와 이진 데이터를 명확하게 구분하는 것입니다 . 의 모델에 따라 시도 파이썬 3.x를 str
대bytes
또는 SQL TEXT
대를 BLOB
.
불행히도 C char
는 “ASCII 문자”와 int_least8_t
. 다음과 같이 할 수 있습니다.
typedef char UTF8; // for code units of UTF-8 strings
typedef unsigned char BYTE; // for binary data
UTF-16 및 UTF-32 코드 단위에 대한 typedef도 원할 수 있지만 인코딩이 wchar_t
정의되지 않았기 때문에 더 복잡합니다 . 전처리기만 있으면됩니다 #if
. C 및 C ++ 0x의 유용한 매크로는 다음과 같습니다.
__STDC_UTF_16__
— 정의 된 경우 유형_Char16_t
이 존재하며 UTF-16입니다.__STDC_UTF_32__
— 정의 된 경우 유형_Char32_t
이 존재하며 UTF-32입니다.__STDC_ISO_10646__
— 정의 된 경우wchar_t
UTF-32입니다._WIN32
— Windows에서는wchar_t
표준을 위반하더라도 UTF-16입니다.WCHAR_MAX
—의 크기를 결정하는 데 사용할 수wchar_t
있지만 OS에서 유니 코드를 나타내는 데 사용하는지 여부는 확인할 수 없습니다.
이것은 내 코드가 어디에서나 char 유형을 사용하지 않아야하고 wint_t 및 wchar_t를 처리 할 수있는 함수를 사용해야 함을 의미합니까?
또한보십시오:
아니요. UTF-8은 char*
문자열 을 사용하는 완벽하게 유효한 유니 코드 인코딩입니다 . 그것은 장점이 프로그램이 비 ASCII 바이트에 투명 중일 경우 (예를 들어,에 작용 계산기 끝나는 라인 \r
및 \n
하지만 변하지 다른 문자 통과), 당신은 전혀 변경하지해야합니다을!
UTF-8을 사용하는 경우 char
= 문자 (예 : toupper
루프에서 호출하지 않음 ) 또는 char
= 화면 열 (예 : 텍스트 줄 바꿈) 이라는 모든 가정을 변경해야합니다 .
UTF-32를 사용하면 고정 너비 문자의 단순성을 갖게됩니다 (고정 너비 graphemes 는 아니지만 모든 문자열의 유형을 변경해야 함).
당신이 UTF-16와 함께 갈 경우, 고정 폭 문자의 가정을 모두 폐기해야 하고 이 단일 바이트 인코딩에서 가장 어려운 업그레이드 경로하게 8 비트 코드 단위의 가정을,.
크로스 플랫폼이 아니기 때문에 적극적으로 피하는 것이 좋습니다 wchar_t
. 때로는 UTF-32이고 때로는 UTF-16이며 때로는 유니 코드 이전의 동아시아 인코딩입니다. 나는 사용하는 것이 좋습니다typedefs
더욱 중요한 것은, 피TCHAR
.
답변
나는 표준 라이브러리 구현을 신뢰하지 않을 것입니다. 고유 한 유니 코드 유형을 롤링하십시오.
#include <windows.h>
typedef unsigned char utf8_t;
typedef unsigned short utf16_t;
typedef unsigned long utf32_t;
int main ( int argc, char *argv[] )
{
int msgBoxId;
utf16_t lpText[] = { 0x03B1, 0x0009, 0x03B2, 0x0009, 0x03B3, 0x0009, 0x03B4, 0x0000 };
utf16_t lpCaption[] = L"Greek Characters";
unsigned int uType = MB_OK;
msgBoxId = MessageBoxW( NULL, lpText, lpCaption, uType );
return 0;
}
답변
기본적으로 메모리의 문자열 wchar_t
을 char 대신 배열 로 처리하고 싶습니다 . 어떤 종류의 I / O (파일 읽기 / 쓰기 등)를 수행 할 때 구현하기에 충분히 간단한 UTF-8 (가장 일반적인 인코딩)을 사용하여 인코딩 / 디코딩 할 수 있습니다. RFC를 Google로 검색하세요. 따라서 메모리 내 어떤 것도 멀티 바이트가 아니어야합니다. 하나 wchar_t
는 하나의 문자를 나타냅니다. 그러나 직렬화에 관해서는 일부 문자가 여러 바이트로 표시되는 UTF-8과 같은 것으로 인코딩해야 할 때입니다.
또한 strcmp
넓은 문자열에 대해 새 버전 등 을 작성해야 하지만 이는 큰 문제가 아닙니다. 가장 큰 문제는 문자 배열 만 허용하는 라이브러리 / 기존 코드와의 상호 운용성입니다.
그리고 sizeof(wchar_t)
(올바르게하고 싶다면 4 바이트가 필요 합니다) 필요한 경우 typedef
/ macro
hacks 를 사용하여 항상 더 큰 크기로 재정의 할 수 있습니다 .