멀티바이트캐릭터와 와이트캐릭터는 뭐가 다른가요 ?

질문맨의 이미지

gcc 메뉴얼을 보니 멀티바이트캐릭터와 와이드캐릭터가 나오는데
둘간의 개념상의 차이가 어떻게 되는지요 ?
한마디로 한글문자열을 c에서 다룰때 어떻게 해야 하는지요 ?

그리고 wchar_t 는 어느경우에 사용이 되어 지는지 ?

그리고 아래두라인의 코드는 어떤점에서 차이가 있는지요 ?

char *name = "질문맨";
unsigned char* name = 질문맨";

감사합니다.

cjh의 이미지

C상의 char는 8-bit를 나타내는 자료형으로 기능합니다. "문자열"이라는 것은 \0으로 끝나고 " "안에 표현되는 바이트 열이 되고요.

US-ASCII의 경우 signed char(=char), 즉 부호 있는 7비트 자료형으로 표현이 가능합니다(범위 0x20-0x7f)이것이 ISO-8859-1과 같은 8비트가 모두 필요한 자료형이 된다면 unsigned char가 필요하게 되고요(두번째의 답변). 하지만 둘 다 8비트 바이트 단위의 자료형입니다. 그리고 기본적으로 1바이트 = 1문자 또는 알파벳, 기호라는 인식에 근거하고 있습니다.

멀티바이트와 와이드 바이트는 이러한 자료형으로 표현할 수 없는 다른 나라의 언어(한중일은 물론 다른 아시아, 인도, 일부 유럽 등)를 표현하기 위한 방법입니다. 차이점이라고 한다면
- 멀티 바이트는 "한 문자가 1 또는 그 이상의 바이트로 표현됨"
- 와이드 바이트는 "한 문자가 일정 크기의 바이트의 배열로 표현됨"
입니다. 간단한 예를 들어 보면, "C언어"라는 문자열을 멀티바이트로 표현하면

"C"(1바이트), "언"(2바이트) "어"(2바이트) = 총 5바이트

와 같이 문자에 따라 바이트 수가 다릅니다. 와이드바이트에서 한 문자를 표시하기 위해 필요한 바이트 수는 여러가지가 있습니다만 glibc에서는 4바이트를 쓰므로 가정하면,

"C" (4바이트), "언"(4바이트), "어"(4바이트) = 총 12바이트

로 표현이 됩니다. 표준으로 따지만 멀티바이트는 ISO-2022에 가깝고(euc-kr 등), 와이드 바이트는 ISO-10646(유니코드)에 가깝습니다. wchar_t는 와이드 바이트를 나타내는 자료형인데, glibc라면 unsigned long(32비트)로 선언되어 있고 유니코드 정보를 담게 됩니다. 멀티바이트의 경우 특별한 자료형 보다는 그냥 unsigned/signed char의 포인터를 사용하게 됩니다. C99에는 와이드바이트 지원이 언어 수준에서 포함된 것으로 알고 있고, VC++의 경우도 와이드바이트 문자열과 멀티바이트 문자열을 구분하고 있습니다(상수 문자열에 L을 붙이면 와이드바이트 상수 문자열이 됩니다. e.g. L"C언어"와 "C언어"의 차이).

--
익스펙토 페트로눔

pynoos의 이미지

아래와 같은 용어를 사용하겠습니다. 주로 함수에서 사용되는 표기법입니다.
mb : multi byte character
wc : wide char
mbs : multibyte string
wcs : wide char string

mb와 wc 는 상호 변환 함수가 있습니다. 이때는, locale에 관련된 환경변수가 영향을 주게 되지요. 환경변수에 따라 mbs중에 어떤 경계로 mb들을 를 해석하는 지 내부 shared object(dll)들이 다르게 불려집니다.

wide character가 사용되는 것은 다음과 같은 이유때문입니다.

if ( strstr( "프린터", "존") != 0 )
{
printf("프린터에서 존이 발견됩니다.");
}

이런 쓸 데 없는 코드(?)가 있고, 한글이 euc-kr, cp949 같은 code로 작성되어 있다면, strstr 이 예상하지 않은 결과를 보내게 됩니다.
이런 것을 방지하려면, strstr 대신 wcswcs, wcsstr 등의 함수들(platform마다 다릅니다)을 사용해야합니다. 그러기 위해서는 mbs를 wcs 로 바꿔야하지요.

-------------

아래 질문에는 두가지 문제가 있는 것 같군요.
하나는 char *는 unsigned char *냐 signed char * 이냐 이구요.
다른 하나는 " " 는 const char * 이냐, char * 이냐입니다.

컴파일러마다 다르다라고만 알려드릴뿐 자세한 것은
찾아봐야 할 것같습니다.

cjh의 이미지

질문맨 wrote:

char *name = "질문맨";
unsigned char* name = 질문맨";

" "안에 들어있는 상수는 char* (=signed char*)이지만 각 상수를 가리키는 포인터는 signed/unsigned char* 이므로, 위 동작으로는 다른 것이 없지만 안의 내용을 문자단위로 비교하려 할 때에는 signed나 unsigned냐에 따라서 달라집니다. 가령 편의상 다음과 같이 하였다면,

char *name1 = "질문맨";
unsigned char* name2 = "질문맨";

두 문자열을 printf()하는 정도로는 다른게 없지만, 비교하면 문제가 생깁니다.
다음 코드를 직접 실행해 보시고 왜 그런 결과가 나오는지 생각해 보시면 쉽게 알 수 있을 것입니다.

#include <stdio.h>

main()
{
        char* name1 = "질문맨";
        unsigned char* name2 = "질문맨";

        if (name1[0] == name2[0]) {
                printf("equal!\n");
        }else{
                printf("not equal!\n");
        }
}

p.s. wc관련 함수는 일부 다를 수 있지만 UNIX나 Windows나 거의 동일하게 제공하고 있습니다. 윈도우에서는 별도의 함수군을 제공하기는 합니다만.

--
익스펙토 페트로눔

질문맨의 이미지

glibc에서 wcs는 32비트(4바이트) 이더군요
그렇다면 mbs를 wcs 로 변환할때 몇바이트를 한문자로 간주할것인지는
어떻게 결정이 되나요 ? 현재 locale 에 따라서 결정이 되나요 ?
mbs에 저장된 문자열이 한글이라면 프로그램을 짤때

setlocale(LC_ALL, "ko_KR");

를 꼭 줘야 하는지요 ?

그리고 추가적인 질문 하나만 더드리자면
제가 UTF-8로 인코딩된 txt문서를 처리하려고 하는데
이 문서를 읽어들일떄
char*에 저장해서 처리하는것이 맞는지
wchar_t*에 저장해서 처리하는것이 맞는지
아니면 char*에 저장한구 wchar_t로 변환을 해야 하는지
아니면 iconv로 euc_kr로 변환한후 char*에 저장해야 하는지.

에고 정말 복잡하군요.. 조언 부탁드립닏.ㅏ

cjh wrote:
C상의 char는 8-bit를 나타내는 자료형으로 기능합니다. "문자열"이라는 것은 \0으로 끝나고 " "안에 표현되는 바이트 열이 되고요.

US-ASCII의 경우 signed char(=char), 즉 부호 있는 7비트 자료형으로 표현이 가능합니다(범위 0x20-0x7f)이것이 ISO-8859-1과 같은 8비트가 모두 필요한 자료형이 된다면 unsigned char가 필요하게 되고요(두번째의 답변). 하지만 둘 다 8비트 바이트 단위의 자료형입니다. 그리고 기본적으로 1바이트 = 1문자 또는 알파벳, 기호라는 인식에 근거하고 있습니다.

멀티바이트와 와이드 바이트는 이러한 자료형으로 표현할 수 없는 다른 나라의 언어(한중일은 물론 다른 아시아, 인도, 일부 유럽 등)를 표현하기 위한 방법입니다. 차이점이라고 한다면
- 멀티 바이트는 "한 문자가 1 또는 그 이상의 바이트로 표현됨"
- 와이드 바이트는 "한 문자가 일정 크기의 바이트의 배열로 표현됨"
입니다. 간단한 예를 들어 보면, "C언어"라는 문자열을 멀티바이트로 표현하면

"C"(1바이트), "언"(2바이트) "어"(2바이트) = 총 5바이트

와 같이 문자에 따라 바이트 수가 다릅니다. 와이드바이트에서 한 문자를 표시하기 위해 필요한 바이트 수는 여러가지가 있습니다만 glibc에서는 4바이트를 쓰므로 가정하면,

"C" (4바이트), "언"(4바이트), "어"(4바이트) = 총 12바이트

로 표현이 됩니다. 표준으로 따지만 멀티바이트는 ISO-2022에 가깝고(euc-kr 등), 와이드 바이트는 ISO-10646(유니코드)에 가깝습니다. wchar_t는 와이드 바이트를 나타내는 자료형인데, glibc라면 unsigned long(32비트)로 선언되어 있고 유니코드 정보를 담게 됩니다. 멀티바이트의 경우 특별한 자료형 보다는 그냥 unsigned/signed char의 포인터를 사용하게 됩니다. C99에는 와이드바이트 지원이 언어 수준에서 포함된 것으로 알고 있고, VC++의 경우도 와이드바이트 문자열과 멀티바이트 문자열을 구분하고 있습니다(상수 문자열에 L을 붙이면 와이드바이트 상수 문자열이 됩니다. e.g. L"C언어"와 "C언어"의 차이).

cjh의 이미지

질문맨 wrote:
glibc에서 wcs는 32비트(4바이트) 이더군요
그렇다면 mbs를 wcs 로 변환할때 몇바이트를 한문자로 간주할것인지는
어떻게 결정이 되나요 ? 현재 locale 에 따라서 결정이 되나요 ?

맞습니다. multibyte의 경우도 OS에 따라 내부 표현방식이 달라질 수 있습니다.

Quote:
mbs에 저장된 문자열이 한글이라면 프로그램을 짤때

setlocale(LC_ALL, "ko_KR");

를 꼭 줘야 하는지요 ?

locale-dependent한 함수를 쓰신다면 그렇게 해야 겠지요. 정확한 카테고리로 치면 LC_CTYPE입니다.

Quote:

그리고 추가적인 질문 하나만 더드리자면
제가 UTF-8로 인코딩된 txt문서를 처리하려고 하는데
이 문서를 읽어들일떄
char*에 저장해서 처리하는것이 맞는지
wchar_t*에 저장해서 처리하는것이 맞는지
아니면 char*에 저장한구 wchar_t로 변환을 해야 하는지
아니면 iconv로 euc_kr로 변환한후 char*에 저장해야 하는지.

UTF-8은 어느쪽인가 하면 multibyte에 가깝습니다. 한글자가 1-6바이트 정도로 표현될 수 있으니까요. 보통은 unsigned char*에 넣으시고 필요하면 iconv()로 변환해서 쓰시면 됩니다.

--
익스펙토 페트로눔

질문맨의 이미지

실력이 딸려서 참 힘들군요. 마지막으로 이론이 아니라 예제로서 여쭤보겟습니다.
한마디로 제가 하려는일은 UTF-8로 인코딩되어 있는 파일을읽어서 EUC-KR 로 변환해서 콘솔로 출력하는 일입니다.

먼저

$vi euc.txt
질문맨

$ iconv -f EUC-KR -t UTF-8 > utf.txt

이렇게 "질문맨" 이라는 글자가 들어있는 utf-8 인코딩 파일을 만들었습니다
이제 저는 이 utf.txt를 읽어들여서 콘솔에서 다시 euc-kr로 변한시켜 출력하고 싶습니다. 아래와 같이 프로그램을 짜봤습니다.

char inary[128];
char outary[128];
char *inbuffer = inary;
char *outbuffer = outary;
int insize = 128;
int outsize = 128;

FILE* fp = open("utf.txt", "r");
fread(inary, sizeof(char), 128, fp);
cd = iconv_open("EUC-KR", "UTF-8");

iconv(cd, &inbuffer, &insize, &outbuffer, &outsize);
printf("%s\n", outbuffer);

이렇게 하면제대로 다시 디코딩된 "질문맨" 이라는 글짜가 나올줄 알았는데
깨진 글자들이 나오네요
제 코드에서 잘못된 부분을지적해 주실수 있으세요 ?

그리고 iconv() 에서 3, 5 번째 아규먼트인
inbytesleft, outbytesleft에 어떠한 값을 넣어야 하는지 간단한설명도부탁좀
제가영어가 짧아서 잘 이해가 안되네요

감사합니다.

pynoos의 이미지

printf("%s\n", outbuffer); 

대신에

printf("%s\n", outary); 

이렇게 해야 맞습니다.
테스트 안해봐서리 문법error가 나는지는 모르겠군요.

iconv에 넣을 때, 원본 변수와 번역본 변수를 직접넣지 않고
pointer를 취해서 넣는 것은, iconv가 그 값을 shift해서 이동시키기 때문입니다.
따라서, 위에서 outbuffer 값은 최종적으로는 outary의 비어있는 부분을 가리키게 됩니다.

cjh의 이미지

1. open() -> fopen()
2. fopen()과 iconv_open()을 각각 fclose()와 iconv_close()로 닫으세요.
3. iconv()의 3, 5번째 인수는 각각 입력 버퍼의 크기, 출력 버퍼의 크기입니다. 함수 수행 후에 변환된 만큼(중간에 중단될 수도 있으므로) 입력 버퍼의 크기와 출력 버퍼의 크기가 변환된 만큼 감소합니다.

--
익스펙토 페트로눔

질문맨의 이미지

제 집요한 질문에 귀찮아 하시지 않고 답변해 주신
두분께 정말 감사드리고요. 제 최종코드는 아래와 같이 출력결과물에 성공했습니다.

#include <stdio.h>
#include <string.h>
#include <iconv.h>

int main(void) {
	char inary[128];
	char outary[128];
	const char *inbuf = inary;
	char *outbuf = outary;
	int insize = 0;
	int outsize = 128;
	iconv_t cd;

	FILE* fp = fopen("utf8.txt", "r");
	insize = fread(inary, sizeof(char), 128, fp);
	cd = iconv_open("EUC-KR", "UTF-8");

	iconv(cd, &inbuf, &insize, &outbuf, &outsize);
	*outbuf = '\0';
	printf("%d %d %s\n", insize, outsize, outary);

	iconv_close(cd);
	fclose(fp);

	exit(0);
}

추가로 질문 몇개만 드려도 될까요 ?

1. 위에 제 코드에서처럼 변환문자열의 끝에 NULL Terminator는 수동으로 붙여줘야 하는지요 ?

2. 이건 좀 iconv랑은 별개질문인데, 제 시스템이 FreeBSD4.7입니다.
iconv 라이브러리 헤더랑 라이브러리가 각각 /usr/local/include, /usr/local/lib 밑에 있더라구요
전 이것들도 당연히 표준 라이브러리/헤더 디렉토리인줄 알았는데 포함이 안되는것 같더라구요
그래서 아래와 같이 컴파일 했거든요

$ gcc -o utf2euc utf2euc.c -I/usr/local/incluide -L/usr/local/lib -liconv

제 질문은

2.1. 현재 컴파일러에서 표준헤더/라이브러리 디렉토리를 볼수 있는방법은 ?
2.2. -I -L directive를 쓰지 않고 전역적으로 이 디렉토리들을 링커에 추가해 주는 방법은 ?

이상입니다. 한번만 더 도와주세요 :oops:

cjh의 이미지

질문맨 wrote:
1. 위에 제 코드에서처럼 변환문자열의 끝에 NULL Terminator는 수동으로 붙여줘야 하는지요 ?

2. 이건 좀 iconv랑은 별개질문인데, 제 시스템이 FreeBSD4.7입니다.
iconv 라이브러리 헤더랑 라이브러리가 각각 /usr/local/include, /usr/local/lib 밑에 있더라구요
전 이것들도 당연히 표준 라이브러리/헤더 디렉토리인줄 알았는데 포함이 안되는것 같더라구요
그래서 아래와 같이 컴파일 했거든요

$ gcc -o utf2euc utf2euc.c -I/usr/local/incluide -L/usr/local/lib -liconv

제 질문은

2.1. 현재 컴파일러에서 표준헤더/라이브러리 디렉토리를 볼수 있는방법은 ?
2.2. -I -L directive를 쓰지 않고 전역적으로 이 디렉토리들을 링커에 추가해 주는 방법은 ?

이상입니다. 한번만 더 도와주세요 :oops:

1. null terminator는 자동으로 붙네요.
2.1. 표준 헤더 디렉토리는... gcc 컴파일할때 스펙에 있었겠지만 /usr/include 이고 라이브러리는 /usr/lib입니다.
2.2. 전역적으로 디렉토리를 추가할 필요가 없습니다. 그냥 그렇게 쓰시면 되고요... 정 필요하시면 고쳐서 gcc/binutils를 다시 빌드하면 되겠지만... 그것보다는 해당 파일을 /usr/include와 /usr/lib에 복사하는 수도 있겠지요.

--
익스펙토 페트로눔

pynoos의 이미지

1. 스펙에 없다면 마직막 변환 끝 버퍼엔 \0을 넣는 것은 수동으로 해주어야합니다.

2. 다른 질문에 대한 답변으로 한 것이 있는데..

http://bbs.kldp.org/viewtopic.php?t=4597

참조하세요

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.