C 언어 | 고급 기능 | 와이드 문자 - wprintf(), wscanf(), setlocale()


C 언어로 1문자에 2바이트 이상을 요구하는 문자 집합을 처리하는 와이드 문자에 대해 설명한다. 와이드 문자에 사용되는 대표적인 문자는 Unicode이고, C 언어에서 Unicode와 같은 국제화 대응의 문자 집합을 처리하기 위해 와이드 문자가 사용된다.

국제화 대응

지금까지의 시대는 모든 컴퓨터에서 안정적으로 지원되는 문자 코드는 ASCII 코드 뿐이며, 영어 이외의 언어는 시스템에 의존하는 문제였다. 그러나 글로벌화가 진행되는 가운데 국제적인 소프트웨어 개발이 빈번하게 이루어지게 되면서, 지역마다 서로 다른 정보를 효율적으로 관리하는 방법이 요구되고 있다.

그래서 국제적인 응용 프로그램을 개발하는 경우는 C 언어로 와이드 문자를 처리하는 방법을 학습해야 한다. char 형의 문자는 1바이트로 구성되기 때문에, 고유하게 표현할 수 있는 255개의 문자이다. 알파벳과 기호 정도라면 분명히 1바이트로 표현할 수 있으므로 효과적이지만, 한글과 같은 많은 문자가 있는 언어를 다루는 경우는 255개로는 분명히 부족하다.

한글과 같은 표준에서 규정되지 않은 시스템 고유의 확장 문자 세트를 이용하려면 와이드 문자를 사용한다. 와이드 문자는 1문자의 표현이 2바이트 이상의 문자이다. 와이드 문자 stddef.h 헤더 파일에 정의된 wchar_t 형으로 표시된다.

와이드 문자는 char 형의 문자와는 다르기 때문에, 와이드 문자형의 상수를 작성하려면 문자 상수의 앞에 L을 붙인다. wchar_t 형의 리터럴 문자열을 작성하는 경우도 마찬가지로, 인용 부호 앞에 L을 지정한다. 이것은 예를 들어 다음과 같이 변수에 저장할 수 있다.

wchar_t wc = L'개';
wchar_t wstr[] = L"당신의 무릎에 개";

다만, 와이드 문자형은 우리가 지금까지 사용한 문자나 문자열과는 이질적인 것이라는 것을 인식해야 한다. 문자 수를 계산하면 경우, 와이드 문자는 1문자 1바이트가 아니다. 따라서 strlen() 함수를 사용할 수 없다. 와이드 문자를 사용하는 경우는 지금까지 사용해 온 문자열 관련 함수를 사용할 수 없다.

코드1

#include <stdio.h>
#include <stddef.h>

int main() {
  wchar_t wc = L'개';
  printf("wchar_t size = %d\n" , sizeof wc); 
  return 0;
}

코드1은 wchar_t 형의 변수를 선언하고, 이를 와이드 문자 상수로 초기화한다. sizeof 연산자를 사용하여 변수의 크기를 확인하고 있는데, 각 프로그램의 결과를 보면 와이드 문자열이 1바이트가 아닌 것을 확인할 수 있을 것이다.

하지만, 와이드 문자 및 와이드 문자 배열은 지금까지처럼 문자열 관련 함수 편집하거나 입출력을 할 수 없다. 와이드 문자를 다루는 프로그램은 와이드 문자 전용 함수를 사용해야 한다. 와이드 문자 전용의 함수는 기존의 문자열 관련 함수에 w를 붙이는 명명 규칙을 가지고 있다. 예를 들어 printf() 함수의 와이드 문자 버전은 wprintf() 함수에 대응하고, 동일하게 scanf() 함수는 wscanf() 함수에 대응한다.

wprintf() 함수

int wprintf( const wchar_t *format [, argument]... );

wscanf() 함수

int wscanf( const wchar_t *format [,argument]... );

wprintf() 함수도 wscanf() 함수도, 문자나 문자열 wchar_t 형을 사용할 수 있다는 점을 제외하고 printf() 함수와 scanf() 함수와는 동일하다. 그러나 기대하듯이 wprintf() 함수를 사용하여 와이드 문자열을 표준 출력에 표시하려고 해도, 놀랍게도 wprintf() 함수는 아무것도 하지 않는 것이다. 와이드 문자를 사용하는 경우는 로케일 설정을 하여야 한다.

로케일(locale)은 특정 지리적 지역에서 지방 특유의 규칙과 언어를 반영하는 규칙을 나타낸다.한글의 와이드 문자를 사용하려면, 일본어가 지원되는 시스템에서 로케일을 “korean"으로 설정해야 한다. 로케일 설정은 locale.h 헤더 파일에서 선언된 setlocale() 함수를 사용한다.

setlocale() 함수

char *setlocale( int category, const char *locale );

category에는 프로그램의 로케일 정보의 어떤 부분을 변경할지 여부를 정수로 지정한다. locale에는 로케일을 지정한다. 함수는 유효한 로케일 및 카테고리를 지정하면, 로케일에 대한 문자열에 대한 포인터를 반환한다. 잘못된 경우 NULL 포인터를 반환하고 설정을 변경하지 않는다.

category는 다음 중 하나를 지정한다.

표1 - 로케일 카테고리를 나타내는 상수

상수 의미
LC_ALL 모든 카테고리
LC_COLLATE strcoll(), _ stricoll(), wcscoll() _ wcsicoll(), strxfrm()의 각 함수
LC_CTYPE 문자 처리 함수
LC_MONETARY localeconv() 함수가 반환하는 통화 형식 정보
LC_NUMERIC 형식이 지정된 출력 루틴 및 데이터 변환 루틴의 소수점 문자, localeconv() 함수가 반환하는 비화폐 형식 정보의 소수점 문자
LC_TIME strftime() 함수와 wcsftime () 함수

로케일 설정이 실제로 값 얻기 영향은 실행 환경에 따라 달라진다. 로케일 문자는 예를 들어 “korean"등 구현에 정의되어 있는 값을 지정하여 언어를 한글로 설정할 수 있다. locale에 NULL 문자열을 지정하면 그 로케일은 처리계 정의의 네이티브 환경이다. 보다 확실한 프로그램을 만들려면, setlocale() 함수의 반환 값을 확인하고, 로케일 설정이 실패했을 경우의 처리도 작성해야 한다.

코드2

#include <stdio.h>
#include <stddef.h>
#include <locale.h>

int main() {
  wchar_t wcat = L'개';
  wchar_t *wstr = L"당신의 무릎에";

 setlocale(LC_ALL , "");
 wprintf(L"%s%c\n" , wstr , wcat);

  return 0;
}

코드2는 setlocale() 함수를 사용하여 모든 카테고리의 로케일을 한글로 설정한다. 그 후 wprintf() 함수를 사용하여 와이드 문자 상수와 와이드 문자 배열을 표준 출력에 표시하고 있다. 이렇게 함으로써, 한글 문자를 처리할 수 있다. 물론, 실행하는 시스템이 한글를 지원하지 않으면 setlocale() 함수는 실패한다.