[java] Java에서 기본 문자 세트 / 인코딩을 찾는 방법은 무엇입니까?

분명한 대답은 사용하는 Charset.defaultCharset()것이지만 최근에 이것이 정답이 아닐 수 있음을 발견했습니다. 결과가 java.io 클래스에서 사용하는 실제 기본 문자 집합과 여러 번 다르다고 들었습니다. Java가 2 세트의 기본 문자 세트를 유지하는 것처럼 보입니다. 누구든지이 문제에 대한 통찰력이 있습니까?

하나의 실패 사례를 재현 할 수있었습니다. 일종의 사용자 오류이지만 다른 모든 문제의 근본 원인을 여전히 노출 할 수 있습니다. 다음은 코드입니다.

public class CharSetTest {

    public static void main(String[] args) {
        System.out.println("Default Charset=" + Charset.defaultCharset());
        System.setProperty("file.encoding", "Latin-1");
        System.out.println("file.encoding=" + System.getProperty("file.encoding"));
        System.out.println("Default Charset=" + Charset.defaultCharset());
        System.out.println("Default Charset in Use=" + getDefaultCharSet());
    }

    private static String getDefaultCharSet() {
        OutputStreamWriter writer = new OutputStreamWriter(new ByteArrayOutputStream());
        String enc = writer.getEncoding();
        return enc;
    }
}

레거시 프로토콜에서 일부 혼합 인코딩 (ANSI / Latin-1 / UTF-8)을 처리하려면 서버에 Latin-1의 기본 문자 집합이 필요합니다. 따라서 모든 서버는이 JVM 매개 변수로 실행됩니다.

-Dfile.encoding=ISO-8859-1

다음은 Java 5의 결과입니다.

Default Charset=ISO-8859-1
file.encoding=Latin-1
Default Charset=UTF-8
Default Charset in Use=ISO8859_1

누군가 코드에서 file.encoding을 설정하여 인코딩 런타임을 변경하려고합니다. 우리 모두는 그것이 작동하지 않는다는 것을 압니다. 그러나 이것은 분명히 defaultCharset ()을 던지지 만 OutputStreamWriter가 사용하는 실제 기본 문자 집합에는 영향을 미치지 않습니다.

버그 또는 기능입니까?

편집 : 수락 된 답변은 문제의 근본 원인을 보여줍니다. 기본적으로 I / O 클래스에서 사용하는 기본 인코딩이 아닌 Java 5의 defaultCharset ()을 신뢰할 수 없습니다. Java 6이이 문제를 해결 한 것 같습니다.



답변

이것은 정말 이상합니다 … 일단 설정되면 기본 Charset이 캐시되고 클래스가 메모리에있는 동안 변경되지 않습니다. 로 "file.encoding"속성을 설정하면 System.setProperty("file.encoding", "Latin-1");아무 작업도 수행되지 않습니다. 매번Charset.defaultCharset()호출 될 캐시 된 문자 집합을 반환합니다.

내 결과는 다음과 같습니다.

Default Charset=ISO-8859-1
file.encoding=Latin-1
Default Charset=ISO-8859-1
Default Charset in Use=ISO8859_1

그래도 JVM 1.6을 사용하고 있습니다.

(최신 정보)

확인. JVM 1.5로 버그를 재현했습니다.

1.5의 소스 코드를 살펴보면 캐시 된 기본 문자 집합이 설정되지 않습니다. 이것이 버그인지 아닌지는 모르겠지만 1.6은이 구현을 변경하고 캐시 된 문자 집합을 사용합니다.

JVM 1.5 :

public static Charset defaultCharset() {
    synchronized (Charset.class) {
        if (defaultCharset == null) {
            java.security.PrivilegedAction pa =
                    new GetPropertyAction("file.encoding");
            String csn = (String) AccessController.doPrivileged(pa);
            Charset cs = lookup(csn);
            if (cs != null)
                return cs;
            return forName("UTF-8");
        }
        return defaultCharset;
    }
}

JVM 1.6 :

public static Charset defaultCharset() {
    if (defaultCharset == null) {
        synchronized (Charset.class) {
            java.security.PrivilegedAction pa =
                    new GetPropertyAction("file.encoding");
            String csn = (String) AccessController.doPrivileged(pa);
            Charset cs = lookup(csn);
            if (cs != null)
                defaultCharset = cs;
            else
                defaultCharset = forName("UTF-8");
        }
    }
    return defaultCharset;
}

file.encoding=Latin-1다음에를 호출 할 때 파일 인코딩을 설정하면 Charset.defaultCharset()캐시 된 기본 문자 집합이 설정되지 않았기 때문에 이름에 대한 적절한 문자 집합을 찾으려고합니다 Latin-1. 이 이름은 올바르지 않기 때문에 찾을 수 없으며 기본값을 반환합니다 UTF-8.

이러한 IO 클래스 OutputStreamWriter가 예상치 못한 결과 를 반환하는 이유는 JVM 1.5 및 JVM 1.6에서도 (witch는 이러한 IO 클래스에서 사용됨)
구현 sun.nio.cs.StreamEncoder이 다릅니다. JVM 1.6 구현은 Charset.defaultCharset()IO 클래스에 제공되지 않는 경우 기본 인코딩을 가져 오는 방법을 기반으로합니다 . JVM 1.5 구현은 다른 방법을 사용합니다.Converters.getDefaultEncodingName(); 을 하여 기본 문자 집합을 가져옵니다. 이 메서드는 JVM 초기화시 설정된 기본 문자 집합의 자체 캐시를 사용합니다.

JVM 1.6 :

public static StreamEncoder forOutputStreamWriter(OutputStream out,
        Object lock,
        String charsetName)
        throws UnsupportedEncodingException
{
    String csn = charsetName;
    if (csn == null)
        csn = Charset.defaultCharset().name();
    try {
        if (Charset.isSupported(csn))
            return new StreamEncoder(out, lock, Charset.forName(csn));
    } catch (IllegalCharsetNameException x) { }
    throw new UnsupportedEncodingException (csn);
}

JVM 1.5 :

public static StreamEncoder forOutputStreamWriter(OutputStream out,
        Object lock,
        String charsetName)
        throws UnsupportedEncodingException
{
    String csn = charsetName;
    if (csn == null)
        csn = Converters.getDefaultEncodingName();
    if (!Converters.isCached(Converters.CHAR_TO_BYTE, csn)) {
        try {
            if (Charset.isSupported(csn))
                return new CharsetSE(out, lock, Charset.forName(csn));
        } catch (IllegalCharsetNameException x) { }
    }
    return new ConverterSE(out, lock, csn);
}

그러나 나는 의견에 동의합니다. 당신은 이 속성에 의존해서는 안됩니다 . 구현 세부 사항입니다.


답변

버그 또는 기능입니까?

정의되지 않은 동작처럼 보입니다. 실제로 명령 줄 속성을 사용하여 기본 인코딩을 변경할 수 있다는 것을 알고 있지만이 작업을 수행 할 때 어떤 일이 발생하는지 정의되어 있지 않습니다.

버그 ID : 4153515이 속성 설정 문제에 대한 :

이것은 버그가 아닙니다. “file.encoding”등록 정보는 J2SE 플랫폼 사양에 필요하지 않습니다. 이는 Sun 구현의 내부 세부 사항이며 사용자 코드로 조사하거나 수정해서는 안됩니다. 또한 읽기 전용입니다. 명령 줄에서 또는 프로그램 실행 중 다른 시간에이 속성을 임의의 값으로 설정하는 것은 기술적으로 불가능합니다.

VM 및 런타임 시스템에서 사용하는 기본 인코딩을 변경하는 데 선호되는 방법은 Java 프로그램을 시작하기 전에 기본 플랫폼의 로케일을 변경하는 것입니다.

사람들이 명령 줄에서 인코딩을 설정하는 것을 보면 움찔합니다. 어떤 코드가 영향을 미칠지 알 수 없습니다.

기본 인코딩을 사용하지 않으려면 적절한 method / constructor 를 통해 원하는 인코딩을 명시 적으로 설정하십시오 .


답변

첫째, Latin-1은 ISO-8859-1과 동일하므로 기본값은 이미 괜찮습니다. 권리?

명령 줄 매개 변수를 사용하여 인코딩을 ISO-8859-1로 성공적으로 설정했습니다. 또한 프로그래밍 방식으로 “Latin-1″로 설정했지만 이는 Java 용 파일 인코딩의 인식 된 값이 아닙니다. 보다http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.doc.html을

그렇게하면 소스를 보면 Charset이 UTF-8로 재설정되는 것처럼 보입니다. 그것은 적어도 대부분의 행동을 설명합니다.

OutputStreamWriter가 ISO8859_1을 표시하는 이유를 모르겠습니다. 비공개 소스 sun.misc. * 클래스에 위임합니다. 나는 그것이 동일한 메커니즘을 통한 인코딩을 다루지 않는다고 생각합니다.

그러나 물론이 코드에서 의미하는 인코딩을 항상 지정해야합니다. 플랫폼 기본값에 의존하지 않습니다.


답변

그 행동은 실제로 그렇게 이상하지 않습니다. 클래스 구현을 살펴보면 다음과 같은 원인이 발생합니다.

  • Charset.defaultCharset() Java 5에서 결정된 문자 세트를 캐싱하지 않습니다.
  • 시스템 속성 “file.encoding”을 설정하고 Charset.defaultCharset()다시 호출 하면 시스템 속성이 두 번째로 평가되고 이름이 “Latin-1″인 문자 집합이 없습니다.Charset.defaultCharset() 기본값은 “UTF-8″입니다.
  • OutputStreamWriter기본 문자 집합 산란부터 그렇게, 그러나 기본 문자 집합을 캐싱 아마 VM 초기화 중에 이미 사용 Charset.defaultCharset()“file.encoding으로”시스템 속성이 런타임에 변경 한 경우.

이미 지적했듯이 이러한 상황에서 VM이 어떻게 작동해야하는지 문서화되어 있지 않습니다. Charset.defaultCharset()API 문서는 기본 문자 집합이 어떻게 결정되는지, 단지 그것을 일반적으로 OS의 기본 문자 세트 또는 기본 로케일과 같은 요소를 기준으로 VM 시작시 수행되는 것을 언급에 매우 정확하지 않습니다.


답변

WAS 서버의 vm 인수를 -Dfile.encoding = UTF-8로 설정하여 서버의 기본 문자 집합을 변경했습니다.


답변

검사

System.getProperty("sun.jnu.encoding")

시스템의 명령 줄에 사용 된 것과 동일한 인코딩 인 것 같습니다.


답변