code

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

codestyles 2020. 9. 10. 07:53
반응형

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.

As for why the IO classes such as OutputStreamWriter return an unexpected result,
the implementation of sun.nio.cs.StreamEncoder (witch is used by these IO classes) is different as well for JVM 1.5 and JVM 1.6. The JVM 1.6 implementation is based in the Charset.defaultCharset() method to get the default encoding, if one is not provided to IO classes. The JVM 1.5 implementation uses a different method Converters.getDefaultEncodingName(); to get the default charset. This method uses its own cache of the default charset that is set upon JVM initialization:

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);
}

But I agree with the comments. You shouldn't rely on this property. It's an implementation detail.


Is this a bug or feature?

Looks like undefined behaviour. I know that, in practice, you can change the default encoding using a command-line property, but I don't think what happens when you do this is defined.

Bug ID: 4153515 on problems setting this property:

This is not a bug. The "file.encoding" property is not required by the J2SE platform specification; it's an internal detail of Sun's implementations and should not be examined or modified by user code. It's also intended to be read-only; it's technically impossible to support the setting of this property to arbitrary values on the command line or at any other time during program execution.

The preferred way to change the default encoding used by the VM and the runtime system is to change the locale of the underlying platform before starting your Java program.

I cringe when I see people setting the encoding on the command line - you don't know what code that is going to affect.

If you do not want to use the default encoding, set the encoding you do want explicitly via the appropriate method/constructor.


First, Latin-1 is the same as ISO-8859-1, so, the default was already OK for you. Right?

You successfully set the encoding to ISO-8859-1 with your command line parameter. You also set it programmatically to "Latin-1", but, that's not a recognized value of a file encoding for Java. See http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.doc.html

When you do that, looks like Charset resets to UTF-8, from looking at the source. That at least explains most of the behavior.

I don't know why OutputStreamWriter shows ISO8859_1. It delegates to closed-source sun.misc.* classes. I'm guessing it isn't quite dealing with encoding via the same mechanism, which is weird.

But of course you should always be specifying what encoding you mean in this code. I'd never rely on the platform default.


The behaviour is not really that strange. Looking into the implementation of the classes, it is caused by:

  • Charset.defaultCharset() is not caching the determined character set in Java 5.
  • Setting the system property "file.encoding" and invoking Charset.defaultCharset() again causes a second evaluation of the system property, no character set with the name "Latin-1" is found, so Charset.defaultCharset() defaults to "UTF-8".
  • The OutputStreamWriter is however caching the default character set and is probably used already during VM initialization, so that its default character set diverts from Charset.defaultCharset() if the system property "file.encoding" has been changed at runtime.

As already pointed out, it is not documented how the VM must behave in such a situation. The Charset.defaultCharset() API documentation is not very precise on how the default character set is determined, only mentioning that it is usually done on VM startup, based on factors like the OS default character set or default locale.


I have set the vm argument in WAS server as -Dfile.encoding=UTF-8 to change the servers' default character set.


check

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

it seems to be the same encoding as the one used in your system's command line.

참고URL : https://stackoverflow.com/questions/1749064/how-to-find-the-default-charset-encoding-in-java

반응형