developer tip

C 프로그래밍 : 유니 코드 용으로 프로그래밍하는 방법?

copycodes 2020. 10. 8. 08:09
반응형

C 프로그래밍 : 유니 코드 용으로 프로그래밍하는 방법?


엄격한 유니 코드 프로그래밍을 수행하려면 어떤 전제 조건이 필요합니까?

이 내 코드는 사용하지 말아야 것을 의미합니까 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>define uint_least16_tand 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_tWindows에서는 16 비트이지만 다른 플랫폼에서는 반드시 16 비트는 아닙니다. Windows에서 필요한 악이라고 생각하지만 다른 곳에서는 피할 수 있습니다. Windows에서 중요한 이유는 이름에 ASCII가 아닌 문자가 포함 된 파일 (함수의 W 버전과 함께)을 사용해야하기 때문입니다.

  • wchar_t문자열 을 사용하는 Windows API에는 UTF-16 인코딩이 필요합니다. 이것은 UCS-2와 다릅니다. 서로 게이트 쌍을 기록해 둡니다. 테스트 페이지 에는 계몽 테스트가 있습니다.

  • If you're programming on Windows, you can't use fopen(), fread(), fwrite(), etc. since they only take char * and don't understand UTF-8 encoding. Makes portability painful.


To do strict Unicode programming:

  • Only use string APIs that are Unicode aware (NOT strlen, strcpy, ... but their widestring counterparts wstrlen, wsstrcpy, ...)
  • When dealing with a block of text, use an encoding that allows storing Unicode chars (utf-7, utf-8, utf-16, ucs-2, ...) without loss.
  • Check that your OS default character set is Unicode compatible (ex: utf-8)
  • Use fonts that are Unicode compatible (e.g. arial_unicode)

Multi-byte character sequences is an encoding that pre-dates the UTF-16 encoding (the one used normally with wchar_t) and it seems to me it is rather Windows-only.

I've never heard of wint_t.


The most important thing is to always make a clear distinction between text and binary data. Try to follow the model of Python 3.x str vs. bytes or SQL TEXT vs. BLOB.

Unfortunately, C confuses the issue by using char for both "ASCII character" and int_least8_t. You'll want to do something like:

typedef char UTF8; // for code units of UTF-8 strings
typedef unsigned char BYTE; // for binary data

You might want typedefs for UTF-16 and UTF-32 code units too, but this is more complicated because the encoding of wchar_t is not defined. You'll need to just a preprocessor #ifs. Some useful macros in C and C++0x are:

  • __STDC_UTF_16__ — If defined, the type _Char16_t exists and is UTF-16.
  • __STDC_UTF_32__ — If defined, the type _Char32_t exists and is UTF-32.
  • __STDC_ISO_10646__ — If defined, then wchar_t is UTF-32.
  • _WIN32 — On Windows, wchar_t is UTF-16, even though this breaks the standard.
  • WCHAR_MAX — Can be used to determine the size of wchar_t, but not whether the OS uses it to represent Unicode.

Does this imply that my code should not use char types anywhere and that functions need to be used that can deal with wint_t and wchar_t?

See also:

No. UTF-8 is a perfectly valid Unicode encoding that uses char* strings. It has the advantage that if your program is transparent to non-ASCII bytes (e.g., a line ending converter which acts on \r and \n but passes through other characters unchanged), you'll need to make no changes at all!

If you go with UTF-8, you'll need to change all the assumptions that char = character (e.g., don't call toupper in a loop) or char = screen column (e.g., for text wrapping).

If you go with UTF-32, you'll have the simplicity of fixed-width characters (but not fixed-width graphemes, but will need to change the type of all of your strings).

If you go with UTF-16, you'll have to discard both the assumption of fixed-width characters and the assumption of 8-bit code units, which makes this the most difficult upgrade path from single-byte encodings.

I would recommend actively avoiding wchar_t because it's not cross-platform: Sometimes it's UTF-32, sometimes it's UTF-16, and sometimes its a pre-Unicode East Asian encoding. I'd recommend using typedefs

Even more importantly, avoid TCHAR.


You basically want to deal with strings in memory as wchar_t arrays instead of char. When you do any kind of I/O (like reading/writing files) you can encode/decode using UTF-8 (this is probably the most common encoding) which is simple enough to implement. Just google the RFCs. So in-memory nothing should be multi-byte. One wchar_t represents one character. When you come to serializing however, that's when you need to encode to something like UTF-8 where some characters are represented by multiple bytes.

You'll also have to write new versions of strcmp etc. for the wide character strings, but this isn't a big issue. The biggest problem will be interop with libraries/existing code that only accept char arrays.

And when it comes to sizeof(wchar_t) (you will need 4 bytes if you want to do it right) you can always redefine it to a larger size with typedef/macro hacks if you need to.


I wouldn't trust any standard library implementation. Just roll your own unicode types.

#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;
}

From what I know, wchar_t is implementation dependent (as can be seen from this wiki article). And it's not unicode.

참고URL : https://stackoverflow.com/questions/526430/c-programming-how-to-program-for-unicode

반응형