컴퓨터가 7bit면 자신들의 모든 문자를 표현 가능한 문화권에서 개발되다 보니 아무래도 우리나라처럼 2-byte 문자열을 쓰는 나라들에서는 아직까지도 문제가 생기는 경우가 많습니다. 우리나라 같은 경우는 한글문제라고 흔히들 통칭하는데..
그나마 현대의 언어들은 상당수 다국어 지원(유니코드를 비롯)을 기본적으로 가져가기 때문에 일반적인 환경에서는 문제가 잘 생기지 않는 경우들이 많지만 그렇기 때문에 가끔 문제가 생기면 더더욱 힘든 케이스들도 많이 보입니다.
자바의 경우는 처음부터 내부적으로 유니코드만을 사용하는 등 언어 자체적으로 국제화를 고려해서 만들었지만 초기 API 설계의 miss로 지금은 deprecated된 함수들도 많이 있죠...(국제화쪽 관련해서는 주로 I/O쪽.. LineNumberInputStream 등등..)
여튼 서론은 이정도고..
이번에 하는 프로젝트에서 property 파일(key=value 형태로 텍스트로 저장하는)에서 값을 읽어와야 할 일이 있는데 Eclipse 기반의 개발환경에서 property 파일 편집을 위해 일본에서 만든 플러그인인 PropertiesEditor라는걸 사용하더군요..
그런데 문제(?)는 이 플러그인이 파일을 저장하면 한글이 다음과 같이 저장됩니다.
# \uc2dc\uc2a4\ud15c \uacf5\ud1b5
저 문자열은 원래 다음과 같은 글자입니다.
# 시스템 공통
유니코드를 저장할 때 ASCII 영역 외의 2-byte 문자열의 경우 \uXXXX 형태로 저장하는 걸로 보입니다.
그런데 문제는 저 내용을 다음과 같이 소스코드상에 넣으면 잘 나오는데
public static void main(String[] args) {
System.out.println("# \uc2dc\uc2a4\ud15c \uacf5\ud1b5");
}
저걸 파일에서 읽어오면 저 문자열 그대로 읽어오더군요..
처음에는 다음과 같은 방법을 사용해 봤습니다.
public static void main(String[] args) throws IOException {
LineNumberReader in = new LineNumberReader(new InputStreamReader(new FileInputStream("c:\\a.properties"), "UTF-8"));
System.out.println(in.readLine());
in.close();
음.. UTF-8이긴 하지만 헥사값의 내용이 아닌 헥사값의 표현인 영문값이 저장된 형태이기 때문에 효용이 없었습니다.
그래서 이런때를 위한 구글신에게 검색이 들어갔습니다.
찾아보다 보니 다음과 같은 내용이 보이더군요..
Unicode Escape Formats 라는 내용인데 유니코드를 Escape Sequence를 이용해서 표기하는 여러가지 방법을 나열했는데 그 중에 위의 내용이 보입니다.
Java나 Ruby에서 쓰이는 포맷이라고 합니다. 언어별로 표기하는 법들이 다들 천차만별이라..
여튼 검색하다 보니 반대의 경우, 즉 한글을 넣으면 저렇게 unicode sequence를 기반으로 만들어 주는 프로그램은 JDK에 기본적으로 같이 제공이 되고 있습니다.
native2ascii 라는 프로그램인데 실행 후 console에서 입력하면 다음과 같이 만들어 줍니다.
실행하면 그냥 커서만 깜빡이는데 여기에 문자열을 아무거나 입력하면 "\u" 를 이용한 Escape Sequence 문자열로 만들어줍니다.(종료시에는 그냥 Ctrl-C 사용)
그런데 일단 다른 별도의 프로그램을 쓰는 방식은 프로그래머들에게는 별 도움 안되고 일단 지금 필요한 건 반대의 케이스라..
무언가가 있을거 같긴 한데 결국 찾지는 못했고 그냥 만들었습니다.(몇 개 찾는 솔루션들도 다들 직접 구현했더군요..)
원리는 간단합니다.\uXXXXX 형태로 저장된 문자열을 찾으면 \u를 제거하고 XXXX 로 표현된 32bit 문자열을 읽어서 해당 char 형태로 바꿔주면 됩니다.(자바는 내부적으로 유니코드를 사용하므로 char는 2 byte 입니다.)
일단 최적화는 크게 고민은 안했고 기능상으로 다음과 같이 만들었습니다.
private String unicodeConvert(String str) {
StringBuilder sb = new StringBuilder();
char ch;
int len = str.length();
for (int i = 0; i < len; i++) {
ch = str.charAt(i);
if (ch == '\\' && str.charAt(i+1) == 'u') {
sb.append((char) Integer.parseInt(str.substring(i+2, i+6), 16));
i+=5;
continue;
}
sb.append(ch);
}
return sb.toString();
}
일단 개념 정도고 제대로 사용하려면 예외상황(\u 뒤에 4byte의 16진수 데이터가 올바로 나오지 않을때는? 등등)에 대한 처리가 좀 더 필요합니다.