2023. 1. 12. 00:53ㆍC언어 복습
1. C11 유니코드
이 글부터 C11에서 들어온 기능들을 소개할 예정이다. 이전 글에서 유니코드와 wchar_t에 대해서 알아봤는데 여기에 나오는 문자열 관련 내용들은 전부 C11에서 들어온 내용이다. 우선 이전 글과 이어지는 내용이라 이전 글에서 어떤 내용을 다뤘는지 간단하게 알아보자면 wchar_t에 대해서 다뤘고 단점이 뭐였는지도 알아보았다. 하지만 결론은 ICU를 사용하거나 wchar_t로 데이터를 주고 받는 데 있어서 두 기계가의 포팅이 가능하도록 변환을 할 수 있는 방법을 마련해야 한다는 것으로 끝났었다.
그래서 C11로 넘어오면서 wcahr_t을 대체할만한 것으로 유티코드가 떠오르게 된 것이다. UTF-32, UTF-16같은 것들은 크기가 고정되어 있기 때문에 멀티 바이트로 변환시킬 때 wchar_t와 같은 문제가 생기지 않기 때문이다. 단, C11 표준에서 지원하는 것은 아니고 컴파일러마다 지원을 할지 말지를 선택하게 했기 때문에 지원을 하지 않을 수 있다. 그럼에도 컴파일러가 지원하지 않을 이유는 없으니 어지간하면 지원을 할 것이라고 강의에서 이야기했다.
// 이 헤더파일을 인클루드해야 한다.
#include <uchar.h>
// 아래의 두 매크로가 선언되어 있다면 사용이 가능하다.
#define __STDC_UTF_16__
#define __STDC_UTF_32__
// 각각 char16_t, char32_t를 사용할 수 있게 해준다.
char16_t msg1[] = u"포큐";
char16_t msg2[] = u"\uD3EC\uD050";
char32_t msg3[] = U"포큐";
char32_t msg4[] = U"\U0000d3ec\U0000d050";
const char* utf16_str = u"포프"; // 컴파일 오류
const char16_t utf16_str[] = u"포프"; // 컴파일 됨
이제 위와 같이 코드를 짜고 printf와 같은 함수로 "%s"같은 서식 문자를 써서 출력을 하려고 하면 출력이 안된다. 당연한 것이 멀티바이트가 아니기 때문이다. 위의 UTF를 멀티바이트로 변환을 시켜서 출력을 시켜야 제대로 출력이 된다. 여기서 주의해야 할 것이 문자열을 변환하는 함수는 없고 문자만 변환시키는 함수만 존재하기 때문에 일일히 반복문을 돌려야 한다... 멀티바이트 문자와 UTF끼리 변환하는 함수는 아래의 4개가 있다.
size_t c16rtomb(char* restrict s, char16_t c16, mbstate_t* restrict ps);
size_t mbrtoc16(char16_t* restrict pc16, const char* restrict s, size_t n, mbstate_t* restrict ps);
size_t c32rtomb(char* restrict s, char32_t c32, mbstate_t* restrict ps);
size_t mbrtoc32(char32_t* restrict pc32, const char* restrict s, size_t n, mbstate_t* restrict ps);
아래는 c16rtomb함수의 사용 예제인데 utf16_str[i]에 해당하는 UTF-16으로 문자를 읽어서 p에 멀티바이트로 변환하도록 작동한다. 그리고 변환시키는 과정에서 mbstate_t가 필요하기 때문에 0으로 초기화시킨 후에 주소로 넘겨준 모습이다.
const char16_t utf16_str[] = u"포프";
mbstate_t state = { 0 };
char buffer[64];
char* p = buffer;
for (size_t i = 0; i < ARRAY_LENGTH(utf16_str); i++)
{
size_t num_bytes = c16rtomb(p, utf16_str[i], &state);
if ((size_t)-1 == num_bytes)
{
break;
}
p += num_bytes;
}
사용법은 이 정도로만 알아도 사용하는 것 자체는 큰 문제가 없다. 여기서 -1과 비교 연산을 하는 부분이 나오는데 더 이상 변환할 문자가 없을 경우에 -1을 반환시키기 때문이다. 문자를 변환시켰다면 멀티바이트로 몇 바이트만큼 변환시켰는지 반환된다.
그럼 이제 UTF-16, UTF-32와 같은 유니코드만 사용하면 문자열 관련 문제는 모두 해결되는 것일까? 아쉽게도 그렇지는 않다. wchar_t를 사용해야 하는 경우가 존재하기 때문인데 운영체제에서 지원하는 몇몇 함수들은 매개 변수로 wchar_t 문자열을 요구하기 때문에 가끔씩 쓰긴 써야한다고 한다.
여담으로 강의에 따르면 요즘은 UTF-8이 많이 쓰이는 상태라고 하는데 아쉽게도 C11에는 UTF-8이 들어오지 않아서 C22에서 넣자고 제안된 상태라고 한다. 강의가 나올 당시가 약 2018~2019년도 쯤이니 이 때 당시에는 C에 UTF-8이 없었을 것이다. 물론 아래와 같이 코드를 짜는 것은 가능하지만 멀티바이트로 바꾸는 함수는 아직 없다.
const char* utf8_str = u8"포프";
const char* utf8_str2 = u8"\U0000d3ec\U0000d504";
// char8_t*가 없어서 char*로 저장되며 멀티바이트로 변환은 못함...
2. 베스트 프랙티스
지금까지 C에서 다국어 문자열을 처리하기 위한 방법들이 어떤 것이 존재하는지 알아봤다. 그럼 이제 단계별로 베스트 프랙티스를 알아볼 차례이다. 자신의 환경에 맞는 방법을 잘 골라보도록 하자.
0 - 기본 원칙
0순위로 지켜져야 하는 기본 원칙이다. 본인의 상황이 아래와 같다면 이 기본 원칙만 지켜도 굉장히 편하게 다국어 문자열을 처리할 수 있다.
- 사용자에게 보여주지 않을 문자열은 전부 아스키로 저장한다.
- 다국어 지원은 사용자에게 보여줄 문자열만 한다.
- 파일로 저장할 때는 공통된 인코딩이 좋다. (요즘은 UTF-8)
- 가능하다면 가장 편한 방법은 ICU 라이브러리를 쓰는 것이다.
1 - 최상의 시나리오 (C89 이상)
사용자 환경을 모두 UTF-8로 만드는 방법이다. 읽고 쓸 때, 멀티바이트와 UTF-8로 변환만 시키면 되기 때문에 아무런 문제가 없다. 단, 사실상 모든 사용자 환경을 똑같이 만든다는 것은 사실상 같은 회사 내에서 사용할 때나 가능한 방법이다.
2 - wchar_t가 UTF-32인 경우 (C89 이상)
이 경우도 꽤 괜찮은 경우다. 요즘은 거의 다 UTF-8을 사용하기 때문에 파일을 저장하고 읽고 쓰는 데 있어서 UTF-32를 UTF-8로 변환을 시킬 수 있다면 1에서 이야기했던 것처럼 그대로 UTF-8과 멀티바이트를 서로 변환시켜 가면서 사용하면 되는 것이다.
3 - char32_t가 UTF-32인 경우 (C11 이상)
이 경우부터는 C89에서 통하지 않는 방법이다. C89에는 char32_t같은 자료형이 없기 때문이다. 대신 C11부터는 char32_t같은 자료형을 지원하기 때문에 이것을 이용해서 UTF-8로 변환한 이후에 그것을 멀티바이트로 변환하는 방법을 사용하면 된다.
'C언어 복습' 카테고리의 다른 글
POCU C언어 정주행 - 마치며 (2) | 2023.01.14 |
---|---|
POCU C언어 정주행 20회차 - Type-Generic, 정적 어서트, 메모리 정렬, 멀티 스레딩 (2) | 2023.01.12 |
POCU C언어 정주행 18회차 - 멀티바이트, wchar_t (0) | 2023.01.09 |
POCU C언어 정주행 17회차 - restrict, C99 기능 (0) | 2023.01.07 |
POCU C언어 정주행 16회차 - inline 함수, inline 주의점, 해결 방법 (1) | 2023.01.07 |