UTF-8로 인코딩된 텍스트 파일에서 한글 읽어올때 문제가 발생합니다.

noblepylon의 이미지

오늘 벌써 세번째 질문입니다-_-;

유니코드로 프로그램을 하나 짜고 있습니다.
UTF-8인코딩으로 된 텍스트파일에서 한글을 읽어와야 하는데 자꾸만 엉뚱한 값이 들어가서 골치가 아프네요.

분명히 화면에서 직접 입력을 받으면 제대로 동작을 합니다.

    wchar_t c;
 
    setlocale(LC_CTYPE, "");
 
    wprintf(L"c 입력: ");
    wscanf(L"%lc", &c);
 
    wprintf(L"0x%X: %lc\n", c, c);
 
    return 0;

c 입력: 갉               // 입력
0xAC09: 갉              // 출력

그런데 이것을 파일입력으로 바꾸면 c에 엉뚱한 값이 들어가버리는군요.
    wchar_t c;
 
    setlocale(LC_CTYPE, "");
 
    FILE* fin = fopen("input.txt", "r, ccs=UTF-8");
 
    fwscanf(fin, L"%lc", &c);
 
    wprintf(L"0x%X: %lc\n", c, c);
 
    return 0;

갉           // input.txt
0x5A9B: 媛   // 출력

c에 AC09('갉')가 들어가지 않고 5A9B('媛')가 들어가는지 당최 이유를 알 수 없군요.
이것때문에 현재 더 이상 진행이 되지 않고 있습니다. OTL

ps. input.txt는 분명히 UTF-8로 만들어졌습니다.

오호라의 이미지

byte order mark.

Hello World.

Necromancer의 이미지

wprintf() 등등은 UTF16 처리할때 씁니다.

그리고 UTF8이면 한글은 3바이트가 됩니다. UTF8-UTF16 변환작업이 선행되어야 할 거 같네요.

Written By the Black Knight of Destruction

Written By the Black Knight of Destruction

noblepylon의 이미지

그러면 UTF-8과 UTF-16간에 변환을 따로 해 주어야 하나요?
한다면 어떤 방식으로 해야 하나요?

그러면 프로그램 덩치가 너무 커지지 않을까 약간 우려되기도 합니다만..
---
"The truth will make you free."(John 8:32)
"I am the way, and the truth, and the life: no one comes to the Father but through Me."(John 14:6)

---
“내게 능력주시는 자 안에서 내가 모든 것을 할 수 있느니라.”(빌립보서 4:13)

Necromancer의 이미지

http://www.kristalinfo.com/K-Lab/unicode/Unicode_intro-kr.html

위 페이지에서 UTF-8, UTF-16 인코딩에 대해 잘 설명되어 있습니다.
변환은 간단하게 됩니다. 완성형 분석할때처럼 복잡한 코드테이블 같은건 필요없습니다.

Written By the Black Knight of Destruction

Written By the Black Knight of Destruction

dg의 이미지

잘 되는데요..

#include <stdio.h>
#include <locale.h>
#include <wchar.h>
 
int main(void)
{
        FILE *fp = fopen("input.txt", "r, ccs=UTF-8");
        wchar_t c;
        setlocale(LC_CTYPE, "");
        fwscanf(fp, L"%lc", &c);
        wprintf(L"0x%X: %lc\n", c, c);
        return 0;
}

$ cat input.txt
갉
$ od -t x1 input.txt
0000000 ea b0 89 0a
0000004

출력

0xAC09: 갉
오호라의 이미지

윗분처럼 깨져 나와야 정상입니다. 재현방법에 문제가 있습니다.

Hello World.

dg의 이미지

뭐가 틀렸고 재현방법에 문제는 무엇인가요?

오호라의 이미지

sparc 서버에 하셨나요? 빅인디언이네요. 설마 PowerPC는 아니시겠죠. ^^;

암튼.

> echo "갉" > test

> od -x test && hexdump test

0000000 b0ea 0a89
0000004

> cat test

결국 UTF-8 포맷인 문자 '갉'을 저장한거지 UTF-8파일로 저장한건 아닙니다.

Hello World.

오호라의 이미지

파일을 읽어서 3바이트만 hex 로 찍어보세요.

URL : http://en.wikipedia.org/wiki/Byte_Order_Mark

Encoding 	Representation (hexadecimal)
UTF-8 	EF BB BF†
UTF-16 (BE) 	FE FF
UTF-16 (LE) 	FF FE
UTF-32 (BE) 	00 00 FE FF
UTF-32 (LE) 	FF FE 00 00
UTF-7 	2B 2F 76, and one of the following bytes: [ 38 | 39 | 2B | 2F ]†
UTF-1 	F7 64 4C
UTF-EBCDIC 	DD 73 66 73
SCSU 	0E FE FF†
BOCU-1 	FB EE 28 optionally followed by FF†

Hello World.

dg의 이미지

x86입니다. -_-;; 원래 애당초 UTF-8에서는 엔디안에 따른 차이가 없습니다.
od -x 하면 2바이트씩 묶는데 엔디안에 따라서 출력이 다르게 되는것 뿐입니다.
제가 한것처럼 od -t x1으로 해보시면 똑같이 나올겁니다.

그리고 BOM 얘기하시는데 유닉스 계열에서는 UTF-8에서 BOM을 잘 안씁니다.
왜 안쓰는지는 위키피디아 찾아보면 나옵니다.
그리고 파일 앞에 BOM을 넣고도 똑같이 돌려봤는데 출력은 다음과 같습니다.

0xFEFF:

$ od -t x1 input.txt
0000000 ef bb bf ea b0 89 0a
0000007

UTF-8의 BOM인 EF BB BF 가 결국 U+FEFF를 UTF-8로 변환한 것이므로 당연한 결과라고 보여지네요.

전혀 이상한 결과가 나온다면 fwscanf()의 리턴값을 체크해 봐야 되거 같네요.

int n;
n = fwscanf(fin, L"%lc", &c);

에서 성공했으면 n이 1이 되고 에러면 0이나 -1이 나올겁니다.

제가 테스트한 환경은 Ubuntu 7.10, gcc 4.1.2, 컴파일 명령은 "gcc -Wall -std=c99 -O0 -ggdb3 -o a a.c", 로켈은 ko_KR.UTF-8, en_US.UTF-8 두가지 다 해봤습니다.

noblepylon의 이미지

VirtualBox를 이용해 우분투 상에서 다시 컴파일해보니까 무리없이 잘 읽혔습니다.
이건 Windows의 문제일까요? 저는 Windows상에서 Code::Block + MinGW + gcc조합을 쓰고 있는지라.
---
"The truth will make you free."(John 8:32)
"I am the way, and the truth, and the life: no one comes to the Father but through Me."(John 14:6)

---
“내게 능력주시는 자 안에서 내가 모든 것을 할 수 있느니라.”(빌립보서 4:13)

dg의 이미지

음.. 대략 미스테리가 풀린듯 하네요..
'갉'을 utf-8로 하면 EA B0 89 가 되고..
EA B0 가 cp949로 '媛'이 되네요..

MinGW 에서는 표준 C 함수를 윈도우에서 제공하는 라이브러리에 있는걸로 씁니다. (아마도 msvcrt.dll)
그런데 이 라이브러리가 좀 오래된거라서 표준을 제대로 지원하지 않습니다. 대표적인 문제로 printf에서 64비트 정수를 출력하기 위해서 %lld 대신에 %I64d 를 써야 되죠..
msdn가서 보니까 fopen함수에서 ccs는 visual c++ 2005, 2008에서만 지원하고 2003 이전은 지원 안하네요.
(http://msdn.microsoft.com/en-us/library/yeby3zcb.aspx)
따라서 윈도우에 기본적으로 들어가있는 라이브러리도 ccs를 지원 안할겁니다.

이런경우라면 윈도우는 이른바 ANSI 인코딩을 사용합니다.
이게 뭐냐면 제어판에 보면 유니코드를 지원하지 않는 응용프로그램을 위한 언어설정이 있는데
이걸 한국어로 하면 ANSI 인코딩은 codepage 949가 되고
이게 English(US)면 ANSI 인코딩은 codepage 437이 됩니다.

결론은 윈도우에서는 fopen에서 ccs=UTF-8을 무시하고 입력파일을 cp949로 간주해서 처리를 한것으로 보입니다.
확인해보려면 제어판에서 앞에서 말한 설정을 바꿔놓고 해보면 되겠죠. (재부팅 필요)
English(US)로 바꾸면 EA가 cp437로 해석되서 Ω(U+03A9)가 나올겁니다.

noblepylon의 이미지

그러면 Windows에서 UTF-8을 온전히 다루려면 어떤 꼼수를 써야 하나요?
Notepad도 UTF-8을 지원하기 위해서 어떤 꼼수를 썼을지 모를 일입니다;
---
"The truth will make you free."(John 8:32)
"I am the way, and the truth, and the life: no one comes to the Father but through Me."(John 14:6)

---
“내게 능력주시는 자 안에서 내가 모든 것을 할 수 있느니라.”(빌립보서 4:13)

dg의 이미지

Windows API에서 제공하는 MultiByteToWideChar 함수를 쓰면 utf-8에서 utf-16로 변환할수 있습니다.
역으로는 WideCharToMultiByte 를 사용하면 됩니다.
아니면 iconv같은 라이브러리 사용해도 되고 직접 변환하는 코드를 짜도 되겠죠..

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.