개발자면 프로젝트를 할 때마다 나오는 문제 중에 하나인 인코딩 문제가 있다.
항상 궁금하고 너무 어려운 문제인 문자열 깨지는 문제...
이 라이브러리도 이 문제에 대한 해결책을 제시하려고 노력하는 라이브러리 중에 하나 인것을 알 고 있다.
다음 문서1에서 소스를 끌어다가 사용 했다. 그리고 mingw 윈도우용으로 만들어 진 것이 아니다.
레드햇 리눅스에서 소스코드는 테스트하고 실행 해 보았다.
1. 사용 시나리오
그냥 위의 문서에서 나온 소스만 사용한다면 너무 티가 날 것이므로 약간의 시나리오를 가미해 보자
- 현재 디렉토리를 읽어서
- 한글이 섞여 있는 디렉토리나 혹은 파일의 경우에
- 인코딩을 하여 보자.
여러가지 생각들이 있겠지만 간단하게 생각해서 진행해 보도록 한다.
2. 현재 디렉토리 읽기
코드에 대해선 별 할 말이 없다 그냥 이렇게 쓰는 구나 정도?
void printdir(char *dir, int depth)
{
DIR *dp;
struct dirent *entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr,"cannot open directory: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name,&statbuf);
if(S_ISDIR(statbuf.st_mode)) {
/* Found a directory,but ignore . and .. */
if(strcmp(".",entry->d_name) == 0 || strcmp("..",entry->d_name) == 0)
continue;
printf("%*s%s\n",depth,"",entry->d_name);
/* Recurse at a new indent level */
printdir(entry->d_name,depth+4);
}
else
{
printf("%*s%s\n",depth,"",entry->d_name);
}
}
chdir("..");
closedir(dp);
}
디렉토리 및 파일 표시를 완성 하였 보았다.
3. 한글 파일 혹은 한글 디렉토리 찾기
우선 한글 인지 여부를 확인 해 보기 위해서 다음 함수가 필요하다.
bool is_hangul(const char *input_text)
{
int is_hangul = true;
while(*input_text)
{
char letter = *input_text;
if(!(letter & 0x80))
{
is_hangul = false;
break;
}
input_text++;
}
return is_hangul;
}
위의 경우는 한글이 하나라도 안 들어가 있으면 false를 리턴한다. 사실 인코딩의 문제는 하나라도 한글이 섞이면 인코딩 해주어야 하므로...위의 함수를 좀 고쳐서 has_hangul 함수를 만들었다
bool has_hangul(const char *input_text)
{
int is_hangul = false;
while(*input_text)
{
char letter = *input_text;
if((letter & 0x80))
{
is_hangul = true;
break;
}
input_text++;
}
return is_hangul;
}
위의 함수는 문자가 하나라도 섞여 있으면 참을 리턴하도록 하는 함수이다.
그럼 첫번 째 함수를 고쳐서 한글이 섞여 있는 문자열을 찾을 수 있을 것이다.
void printdir(char *dir, int depth)
{
......
while((entry = readdir(dp)) != NULL) {
.......
printf("%*s%s\n",depth,"",entry->d_name);
if(has_hangul(entry->d_name)){
printf("%*s%s\n",depth,"",entry->d_name);
......
}
else
{
if(has_hangul(entry->d_name))
printf("%*s%s\n",depth,"",entry->d_name);
}
}
.....
}
4. 인코딩 하기 전에
인코딩 하기 전에 위의 코드에서 인코딩을 하게 되면 코드가 늘어날 것 같기도 하고 로직도 한데 섞이는 것 같아서 따로 한글 명만 저장 했다가 인코딩 하는 방식으로 선택하기로 한다.
따로 저장하는 방법에는 무엇이 있을까? list 이겠지...
이 사이트 내에서도 여러 군데 둘러봐도 되겠지만.. 우선 다음 사이트의 2소스 코드를 이용 했다.
struct file_list *head = NULL;
struct file_list *curr = NULL;
struct file_list
{
char filename[256];
struct file_list *next;
};
struct file_list* create_list(char* val)
{
printf("\n creating list with headnode as [%d]\n",val);
struct file_list *ptr = (struct file_list*)malloc(sizeof(struct file_list));
if(NULL == ptr)
{
printf("\n Node creation failed \n");
return NULL;
}
strcpy(ptr->filename, val);
ptr->next = NULL;
head = curr = ptr;
return ptr;
}
struct file_list* add_to_list(char* val, bool add_to_end)
{
if(NULL == head)
{
return (create_list(val));
}
if(add_to_end)
printf("\n Adding node to end of list with value [%d]\n",val);
else
printf("\n Adding node to beginning of list with value [%d]\n",val);
struct file_list *ptr = (struct file_list*)malloc(sizeof(struct file_list));
if(NULL == ptr)
{
printf("\n Node creation failed \n");
return NULL;
}
strcpy(ptr->filename, val);
ptr->next = NULL;
if(add_to_end)
{
curr->next = ptr;
curr = ptr;
}
else
{
ptr->next = head;
head = ptr;
}
return ptr;
}
문자열을 저장해야 하기에 약간 변형을 가하였다. 그리고 최종적으로 이를 쓰는 함수
void print_list(void)
{
struct file_list *ptr = head;
printf("\n -------Printing list Start------- \n");
while(ptr != NULL)
{
printf("\n [%s] \n",ptr->filename);
conv_str(ptr->filename, NULL, NULL);
ptr = ptr->next;
}
printf("\n -------Printing list End------- \n");
return;
}
그럼 이제 어디에 집어넣어야 할 지는 이미 정해져 있는 것이 아닌가 아까 함수에서 printf 문을 대체한다.
void printdir(char *dir, int depth)
{
......
while((entry = readdir(dp)) != NULL) {
.......
printf("%*s%s\n",depth,"",entry->d_name);
if(has_hangul(entry->d_name))
{
add_to_list(entry->d_name, true);
......
}
else
{
if(has_hangul(entry->d_name))
add_to_list(entry->d_name, true);
}
}
.....
}
5. 인코딩하기
이제 모든 작업이 끝났으니 문자열을 인코딩 할 차례가 왔다.
이를 쓰는 함수는 다음과 같다.
void conv_str( char* msg_buf, const char *dest_cod, const char *src_cod )
{
char * out_string;
// Conversion descriptor.
iconv_t conv_desc;
conv_desc = initialize (src_cod, dest_cod);
out_string = euc2utf8 (conv_desc, msg_buf);
finalize (conv_desc);
if (out_string) {
printf ("Final iconv output: %s\n", out_string);
create_file(out_string)
}
}
말할 필요 없이 소스 에서 목적지로 인코딩을 하고 해당 내용이 인코딩 되었을 경우 다른 이름으로 저장 한다.
void create_file(char *out_string)
{
FILE *newFile;
char fileName[256];
memset(fileName, 0x00, sizeof(fileName));
if (out_string) {
printf ("Final iconv output: %s\n", out_string);
}
sprintf(fileName, "%s_Converted.txt", out_string);
if((newFile = (FILE*)fopen(fileName,"ab+")) == NULL)
{
printf("Can not create file [%s] \n", fileName);
return;
}
else
printf("File [%s] created successfully \n", fileName);
if(newFile) fclose(newFile);
newFile = NULL;
}
위에서 이미 conv_str이라는 함수가 나왔다. print_list에서 우선 함번 뿌려 준다
이제 iconv를 사용한 함수들을 둘러 볼 차례인 데 그렇게 중요한 내용이 없어 보이고 함수도 익숙하지 않다.
우선 헤더 파일을 다음과 같이 만들었다.
#ifndef __ICONV_EX_H__
#define __ICONV_EX_H__
#ifdef __cplusplus
extern "C" {
#endif
iconv_t initialize (const char *outset, const char *inpuset);
char * euc2utf8 (iconv_t conv_desc, const char * euc) ;
void finalize (iconv_t conv_desc);
#ifdef __cplusplus
};
#endif
#endif
그리고 소스코드는 다음과 같다.
/*http://www.lemoda.net/c/iconv-example/iconv-example.html*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <iconv.h>
#include <errno.h>
#include <string.h>
#include "iconv-ex.h"
/* Print the hexadecimal bytes. */
void showhex (const char * what, const char * a, int len)
{
int i;
printf ("%s: ", what);
for (i = 0; i < len; i++) {
printf ("%02X", (unsigned char) a[i]);
if (i < len - 1)
printf (" ");
}
printf ("\n");
}
/* Display values, for the purpose of showing what this is doing. */
void show_values (const char * before_after, const char * euc_start, int len_start,
const char * utf8_start, int utf8len_start)
{
printf ("%s:\n", before_after);
showhex ("EUC-KR string", euc_start, len_start);
showhex ("UTF-8 string", utf8_start, utf8len_start);
}
/* Initialize the library. */
iconv_t initialize (const char *outset, const char *inpuset)
{
iconv_t conv_desc;
conv_desc = iconv_open (outset, inpuset);
if ((int) conv_desc == -1) {
/* Initialization failure. */
if (errno == EINVAL) {
fprintf (stderr,
"Conversion from '%s' to '%s' is not supported.\n",
inpuset, outset);
} else {
fprintf (stderr, "Initialization failure: %s\n",
strerror (errno));
}
exit (1);
}
return conv_desc;
}
/* Convert EUC into UTF-8 using the iconv library. */
char * euc2utf8 (iconv_t conv_desc, const char * euc)
{
size_t iconv_value;
char * utf8;
unsigned int len;
unsigned int utf8len;
/* The variables with "start" in their name are solely for display
of what the function is doing. As iconv runs, it alters the
values of the variables, so these are for keeping track of the
start points and start lengths. */
char * utf8start;
const char * euc_start;
int len_start;
int utf8len_start;
len = strlen (euc);
if (!len) {
fprintf (stderr, "Input string is empty.\n");
return (0);
}
/* Assign enough space to put the UTF-8. */
utf8len = 2*len;
utf8 = calloc (utf8len, 1);
/* Keep track of the variables. */
len_start = len;
utf8len_start = utf8len;
utf8start = utf8;
euc_start = euc;
/* Display what is in the variables before calling iconv. */
show_values ("before",euc_start, len_start,utf8start, utf8len_start);
iconv_value = iconv (conv_desc, & euc, & len, & utf8, & utf8len);
/* Handle failures. */
if (iconv_value == (size_t) -1) {
fprintf (stderr, "iconv failed: in string '%s', length %d, "
"out string '%s', length %d\n",
euc, len, utf8start, utf8len);
switch (errno) {
/* See "man 3 iconv" for an explanation. */
case EILSEQ:
fprintf (stderr, "Invalid multibyte sequence.\n");
break;
case EINVAL:
fprintf (stderr, "Incomplete multibyte sequence.\n");
break;
case E2BIG:
fprintf (stderr, "No more room.\n");
break;
default:
fprintf (stderr, "Error: %s.\n", strerror (errno));
}
exit (1);
}
/* Display what is in the variables after calling iconv. */
show_values ("after", euc_start, len_start, utf8start, utf8len_start);
return utf8start;
}
/* Close the connection with the library. */
void finalize (iconv_t conv_desc)
{
int v;
v = iconv_close (conv_desc);
if (v != 0) {
fprintf (stderr, "iconv_close failed: %s\n", strerror (errno));
exit (1);
}
}
소스 설명은 생략하기로 한다.
마지막으로 컴파일 및 빌드는 다음과 같이 하였다.
6. 컴파일 빌드
gcc -I. -I/usr/local/include -o hangultest hangul-test.c iconv-example.c -L/usr/local/lib -liconv
오류가 발생 했는 데 다음과 같은 오류이다.
gcc: 치명적: libiconv.so.2: 열기 실패
해결책은
export LD_PRELOAD=/usr/local/lib//libiconv.so.2
컴파일 및 빌드를 다음과 같이도 해보았다.
gcc -I. -I/usr/local/include -o hangultest hangul-test.c iconv-example.c /usr/local/lib/libiconv.so.2
'프로그래밍' 카테고리의 다른 글
파이썬 사용해보기-2 (0) | 2023.01.24 |
---|---|
파이썬 사용해보기-1 (0) | 2023.01.23 |
[Java]apache commons 를 이용한 tar압축 풀기 (2) | 2023.01.19 |
[자바] apache-commons-net FtpClient 래퍼 및 파일 다운로드 (0) | 2023.01.18 |
[Java] JTable, JscrollPane을 사용한 스크롤 바 활성화 (0) | 2023.01.16 |