문자 변환

수호천사임다·2024년 10월 13일

자바

목록 보기
6/15

문자 변환(인코딩/디코딩)

데이터를 컴퓨터에서 처라히가 위한 중요한 과정으로, 문자를 숫자로 변환하거나 숫자를 문자로 변환하는 방식.

인코딩

문자를 컴퓨터가 이해할 수 있는 숫자로 변경하는 것

  • 문자 -> 숫자 - 인코딩
String writeString = "ABC";
// 문자 -> byte 변환 UTF-8 인코딩
byte[] writeBytes = writeString.getBytes(UTF_8);

디코딩

숫자로 변환된 데이터를 다시 원래의 문자로 변환하는 과정

  • 숫자 -> 문자 - 디코딩
byte[] bytes = fis.readAllBytes();
String readString = new String(bytes, UTF_8);

인코딩/디코딩 예시
문자 -> 숫자 (인코딩): "가"라는 문자를 EUC-KR로 인코딩하면 0xB0A1(16진수)로 표현.
숫자 -> 문자 (디코딩): 0xB0A1을 EUC-KR로 디코딩하면 "가"라는 문자로 변환.

주요 인코딩 방식

인코딩 방식에 따라 문자가 표현되는 방식이 다르다. 각 인코딩 방식은 사용하는 바이트 수와 지원하는 문자 집합에 따라 다르게 작동한다.

2바이트 인코딩 방식

1) EUC-KR

특징: 한국어 완성형 문자를 인코딩하는 표준 방식 중 하나이다.
바이트 수: 한글과 일부 특수 문자를 2바이트로 인코딩한다.
예시: "가" → 0xB0A1

2) MS949

특징: 마이크로소프트에서 EUC-KR을 확장한 인코딩 방식으로, 더 많은 문자 집합을 지원한다.
바이트 수: 한글을 2바이트로 인코딩한다.
예시: "가" → 0xB0A1 (EUC-KR과 동일한 값)

3) UTF-16

특징: Unicode 문자 집합을 사용하는 인코딩 방식이다.
바이트 수: 대부분의 문자를 2바이트로 인코딩하지만, 일부 문자는 4바이트를 사용하기도 한다.
예시: "가" → 0xAC00
3바이트 인코딩 방식

UTF-8

특징: 유니코드를 가변 길이 인코딩 방식으로 표현하며, 웹에서 가장 널리 사용되는 방식이다.
바이트 수: ASCII 문자는 1바이트, 한글과 같은 비ASCII 문자는 3바이트로 인코딩된다.
예시: "가" → 0xEAB080

실습

package charset;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import static java.nio.charset.StandardCharsets.*;

public class EncodingMain1 {

  private static final Charset EUC_KR = Charset.forName("EUC-KR");
  private static final Charset MS_949 = Charset.forName("MS949");

  public static void main(String[] args) {
    System.out.println("ASCII 영문 처리 ");
    encoding("A", US_ASCII);
    encoding("A", ISO_8859_1);
    encoding("A", EUC_KR);
    encoding("A", UTF_8);
    encoding("A", UTF_16BE); // 2byte 사용 -> 호환 불가

    System.out.println("== 한글 지원 ==");
    encoding("가", EUC_KR);
    encoding("가", MS_949);
    encoding("가", UTF_8); // 3byte
    encoding("가", UTF_16BE);

    String str = "A";
    byte[] bytes = str.getBytes(); // 기본값 UTF-8 사용
    System.out.println("bytes = " + Arrays.toString(bytes));

  }

  private static void encoding(String text, Charset charset){
    byte[] bytes = text.getBytes(charset);
    System.out.printf("%s -> [%s] 인코딩 -> %s %sbyte\n", text, charset, Arrays.toString(bytes), bytes.length);
  }
}
package charset;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;

import static java.nio.charset.StandardCharsets.*;

public class EncodingMain2 {

  private static final Charset EUC_KR = Charset.forName("EUC-KR");
  private static final Charset MS_949 = Charset.forName("MS949");


  public static void main(String[] args) {
    System.out.println("== 영문 ASCII 인코딩 ==");
    test("A", US_ASCII, US_ASCII);
    test("A", US_ASCII, ISO_8859_1); // ASCII 확장(LATIN-1)
    test("A", US_ASCII, EUC_KR); // ASCII 포함
    test("A", US_ASCII, MS_949); // ASCII 포함
    test("A", US_ASCII, UTF_8); // ASCII 포함
    test("A", US_ASCII, UTF_16BE); // UTF_16 디코딩 실패

    System.out.println("==한글 인코딩 - 기본 == ");
    test("가", US_ASCII, US_ASCII); // X
    test("가", ISO_8859_1, ISO_8859_1); // X
    test("가", EUC_KR, EUC_KR); // O
    test("가", MS_949, MS_949); // O
    test("가", UTF_8, UTF_8); // O
    test("가", UTF_16BE, UTF_16BE); // O

    System.out.println("==한글 인코딩 - 복잡한 문자 == ");
    test("쀍", EUC_KR, EUC_KR);
    test("쀍", MS_949, MS_949);
    test("쀍", UTF_8, UTF_8);
    test("쀍", UTF_16BE, UTF_16BE);

    System.out.println("== 한글 인코딩 - 디코딩이 다른 경우 == ");
    test("가", EUC_KR, MS_949);
    test("쀍", MS_949, EUC_KR); // 인코딩 가능, 디코딩 X
    test("가", EUC_KR, UTF_8); // X
    test("가", MS_949, UTF_8); // X
    test("가", UTF_8, MS_949); // X

    System.out.println("== 영문 인코딩 - 디코딩이 다른 경우 ==");
    test("A", EUC_KR, UTF_8);
    test("A", MS_949, UTF_8);
    test("A", UTF_8, MS_949);
    test("A", UTF_16BE, MS_949);
  }

  private static void test(String text, Charset encodingCharset, Charset decodingCharset) {
    byte[] encoded = text.getBytes(encodingCharset);
    String decoded = new String(encoded, decodingCharset);
    System.out.printf("%s -> [%s] 인코딩 -> %s %sbyte -> [%s] 디코딩 -> %s\n",
          text , encodingCharset, Arrays.toString(encoded), encoded.length,
        decodingCharset, decoded);


  }
}
  • 문자를 바이트로 변환하거나 바이트를문자로 변환할 때 반드시 인코딩 셋(encoding set), 즉 문자표(문자 집합)이 필요하다.
    • 문자표를 지정하지 않을 경우 컴퓨터의 default 문자표를 사용한다. (대부분 UTF-8)

2byte: EUC-KR, MS949, UTF-16
3byte: UTF-8

한글을 인코딩할 때 UTF-8과 EUC-KR(MS949)는 서로 호환되지 않는다.

ASCII 영문 인코딩: UTF-16을 제외하고 모두 호환
사실상 표준인 UTF-8 사용

한글이 깨지는 가장 큰 2가지 이유

  • EUC-KR(MS949), UTF-8이 서로 호환 X
    • 한글이 깨지는 대부분의 문제는 UTF-8로 인코딩한 한글을 EUC-KR(MS949)로 디코딩하거나 또는 EUC-KR(MS949)로 인코딩한 한글을 UTF-8로 디코딩할 때 발생
  • EUC-KR(MS949) 또는 UTF-8로 인코딩한 한글을 ISO-8859-1로 디코딩 할 때

0개의 댓글