스트링 엔코딩 변환 - euc-kr <---> utf-8
세 줄 요약.
1. JAVA 의 String 클래스는 유니코드로 처리되는 char 의 배열 이외에 어떠한 인코딩 정보를 갖고 있지 않는다.
2. String.getByte('인코딩명') 을 사용하여 인코딩 할 수 있으며, new String(byte[], "인코딩명") 을 사용하여 디코딩 할 수 있다/.
3. EUC-KR 에서 UTF-8 으로 바꾸기 위해서는 EUC-KR ---> java.lang.String ----> UTF-8 과 같은 방식으로 변환 할 수 있다.
기본적으로 자바의 String 클래스는 2 byte 크기의 유니코드를 사용하여 문자열을 처리하는 Character 배열을 감싸고 있는 형태이다.
https://docs.oracle.com/javase/8/docs/api/java/lang/Character.html 참고.
JDK 1.8 의 String 클래스의 코드를 보면 아래와 같이...
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 /** use serialVersionUID from JDK 1.0.2 for interoperability */ private static final long serialVersionUID = -6849794470754667710L; /** * Class String is special cased within the Serialization Stream Protocol. * * A String instance is written into an ObjectOutputStream according to * <a href="{@docRoot}/../platform/serialization/spec/output.html"> * Object Serialization Specification, Section 6.2, "Stream Elements"</a> */ private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
달랑 네 개의 필드를 확인할 수 있는데, 여기에 문자열 관련된 값이 저장되는 것은 value[] 가 유일하다.
String 클래스의 getBytes() 메소드를 사용하여 String 을 다른 인코딩으로 변환할 수 있으며, 이 것은 즉 2byte 의 유니코드가 다른 인코딩으로 변환되어 반환되는 것이다.
getBytes() 사용시 아무런 인자값을 넣지 않고 호출한다면, 그리고 JVM 실행 옵션으로 인코딩을 정의하지 않았다면 ( 예 : -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8) 시스템 기본 인코딩으로 변환되어 나올 것이다. 만약 윈도우에서 아무런 옵션 없이 JVM 을 실행하여 String 에 대하여 getBytes() 를 호출한다면 MS949 로 인코딩된 byte 배열이 튀어나올 것이다. (윈도우에서 돌아가는 JVM은 기본적으로 MS949 를 사용하기 때문이다.) 이렇게 아무런 인자값 없이 getBytes() 를 호출한다면 내부적으로 JVM 기본 인코딩을 정보를 가져오는 getProperty("file.encoding") 을 호출하여 얻은 결과 값으로 시스템 인코딩 값으로 인코딩 한다. 그렇기 때문에 String 으로부터 자신이 원하는 인코딩 바이트 배열을 얻기 위해서는 아래 예제처럼 인자값으로 인코딩명을 넣어줘야 한다.
Unicode String --> euc-kr :
// String 을 euc-kr 인코딩으로 변환하여 반환. String helloString = "안녕하세요?"; byte[] euckrStrBuffer = helloString.getBytes("euc-kr");
위 구현은 내부적으로 String 을 인코드 혹은 디코드 해주는 StringCoding 클래스의 encode 라는 이름의 static 메소드를 이용한다.
하지만, StringCoding 클래스는 protected 클래스기 때문에 직접적으로 사용할 수 없다.
하지만 String.getBytes("euc-kr") 메소드를 쓰는 방법 이외에도 같이 nio 의 Charset 을 이용하여 String 을 euc-kr 로 변환할 수 있다.
Unicode String --> euc-kr (nio.charset 사용) :
Charset euckrCharset = Charset.forName("euc-kr"); ByteBuffer byteBuffer = euckrCharset.encode(helloString); byte[] euckrStringBuffer = new byte[byteBuffer.remaining()]; byteBuffer.get(euckrStringBuffer);
다음으로 인코딩된 문자열의 바이트 배열을 다시 String 으로 만드는 방법이다.
euc-kr --> Unicode String :
String helloString = "안녕하세요"; byte[] euckrStringBuffer = helloString.getBytes(Charset.forName("euc-kr")); // euc-kr 로 변환된 byte 문자열을 다시 유니코드 String 으로 변환. // String 생성자의 // 첫 번째 인자로 문자열 byte 배열을 넣어주고, // 두 번째 인자로 byte 배열의 인코딩 값을 넣어준다. String decodedHelloString = new String(euckrStringBuffer, "euc-kr");
이 것 또한 마찬가지로 nio 의 Charset 를 이용하여 디코드 할 수 있다.
euc-kr --> Unicode String (nio.charset 사용) :
Charset euckrCharset = Charset.forName("euc-kr"); CharBuffer charBuffer = euckrCharset.decode(ByteBuffer.wrap(euckrStringBuffer)); String decodedHelloString = charBuffer.toString();
System.out.Println 를 찍어보면 값이 정상적으로 변환된 것을 확인할 수 있다.
그렇다면, euc-kr 을 utf-8 로 변환시키거나 또는 반대로 변환시키고 싶을 때는 어떻게 해야 하는가?
답은 도중에 String 으로 변환하는 과정을 한 번 거치면 된다.
EUC-KR ---> UTF-8 :
String helloString = "안녕하세요. ㄱㄴㄷㄹㅁㅂㅆㅢ 놟쐛씗쀍"; System.out.println("Source : " + helloString); // String 을 euc-kr 로 인코딩. byte[] euckrStringBuffer = helloString.getBytes(Charset.forName("euc-kr")); System.out.println(); System.out.println("euc-kr - length : " + euckrStringBuffer.length); String decodedFromEucKr = new String(euckrStringBuffer, "euc-kr"); System.out.println("String from euc-kr : " + decodedFromEucKr);
byte[] utf8StringBuffer = decodedFromEucKr.getBytes("utf-8"); System.out.println(); System.out.println("utf-8 - length : " + utf8StringBuffer.length); String decodedFromUtf8 = new String(utf8StringBuffer, "utf-8"); System.out.println("String from utf-8 : " + decodedFromUtf8);// String 을 utf-8 로 인코딩.
즉 위 코드는
euc-kr ---> JAVA String 객체(유니코드) ---> utf-8
과 같은 과정으로 인코딩 변환이 되는 것이다.
하지만, 아래 결과를 살펴보면...
결과:
Source : 안녕하세요. ㄱㄴㄷㄹㅁㅂㅆㅢ 놟쐛씗쀍 euc-kr - length : 33 String from euc-kr : 안녕하세요. ㄱㄴㄷㄹㅁㅂㅆㅢ ???? utf-8 - length : 46 String from utf-8 : 안녕하세요. ㄱㄴㄷㄹㅁㅂㅆㅢ ????
깨지는 글자가 발생하게 된다.
이유는 완성형인 euc-kr 이 '놟쐛씗쀍' 과 같은 문자를 지원하지 못하기 때문에 문자열 '안녕하세요. ㄱㄴㄷㄹㅁㅂㅆㅢ 놟쐛씗쀍' 를 인코딩 할 경우 지원하지 못 하는 문자에 대한 손실이 일어나게 되는 것이다.