जावा में डिफ़ॉल्ट चारसेट / एन्कोडिंग कैसे खोजें?


92

स्पष्ट उत्तर का उपयोग करना है Charset.defaultCharset()लेकिन हमें हाल ही में पता चला है कि यह सही उत्तर नहीं हो सकता है। मुझे बताया गया था कि परिणाम कई अवसरों में java.io कक्षाओं द्वारा उपयोग किए जाने वाले वास्तविक डिफ़ॉल्ट चारसेट से अलग है। ऐसा लगता है कि जावा डिफ़ॉल्ट चारसेट के 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 / लैटिन -1 / UTF-8) से निपटने के लिए लैटिन -1 में डिफ़ॉल्ट चारसेट की आवश्यकता होती है। तो हमारे सभी सर्वर इस JVM पैरामीटर के साथ चलते हैं,

-Dfile.encoding=ISO-8859-1

यहाँ जावा 5 पर परिणाम है,

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

कोई कोड में file.encoding सेट करके एन्कोडिंग रनटाइम को बदलने का प्रयास करता है। हम सभी जानते हैं कि काम नहीं करता है। हालाँकि, यह स्पष्ट रूप से डिफॉल्टचार्सेट () को बंद कर देता है, लेकिन यह आउटपुटस्ट्रीमवेयर द्वारा उपयोग किए जाने वाले वास्तविक डिफॉल्ट चारसेट को प्रभावित नहीं करता है।

क्या यह बग या फीचर है?

संपादित करें: स्वीकृत उत्तर मुद्दे की जड़ को दर्शाता है। मूल रूप से, आप जावा 5 में डिफ़ॉल्ट चरसेट () पर भरोसा नहीं कर सकते हैं, जो कि I / O वर्गों द्वारा उपयोग की जाने वाली डिफ़ॉल्ट एन्कोडिंग नहीं है। ऐसा लगता है कि जावा 6 इस समस्या को ठीक करता है।


यह अजीब है, क्योंकि defaultCharset एक स्थिर वैरिएबल का उपयोग करता है जो केवल एक बार सेट होता है (डॉक्स के लिए - वीएम स्टार्टअप पर)। वीएम वेंडर क्या आप उपयोग कर रहे हैं?
Bozho

मैं इसे जावा 5 पर पुन: पेश करने में सक्षम था, दोनों पर Sun / Linux और Apple / OS X।
ZZ कोडर

यही कारण है कि defaultCharset () परिणाम कैशिंग नहीं। मुझे अभी भी यह पता लगाने की आवश्यकता है कि IO कक्षाओं द्वारा उपयोग किए जाने वाले वास्तविक डिफ़ॉल्ट चारसेट क्या हैं। कहीं और कैश्ड एक और डिफ़ॉल्ट चारसेट होना चाहिए।
ZZ कोडर

@ZZ कोडर, मैं अभी भी उस पर शोध कर रहा हूं। केवल मुझे लगता है कि मुझे पता है कि चारसेट.डेफुलीचर्सेट () को जेवीएम 1.5 में sun.nio.cs.StreamEncoder से नहीं बुलाया गया है। JVM 1.6 में चारसेट.डेफुलीचर्सेट () विधि को अपेक्षित परिणाम देने वाला कहा जाता है। StreamEncoder के JVM 1.5 कार्यान्वयन किसी तरह पिछले एन्कोडिंग को रोक रहा है।
ब्रूनो कोंडे

जवाबों:


62

यह वास्तव में अजीब है ... एक बार सेट होने पर, डिफ़ॉल्ट चारसेट को कैश कर दिया जाता है और क्लास के मेमोरी में रहने के दौरान इसे बदला नहीं जाता है। कुछ भी नहीं के "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

क्यों आईओ वर्गों जैसे के रूप में OutputStreamWriterएक अप्रत्याशित परिणाम लौटने के लिए,
के कार्यान्वयन sun.nio.cs.StreamEncoder(डायन इन आईओ वर्गों द्वारा प्रयोग किया जाता है) अच्छी तरह से JVM 1.5 और JVM 1.6 के लिए के रूप में अलग है। जेवीएम 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);
}

लेकिन मैं टिप्पणियों से सहमत हूं। आपको इस संपत्ति पर भरोसा नहीं करना चाहिए । यह एक कार्यान्वयन विवरण है।


इस त्रुटि को पुन: उत्पन्न करने के लिए, आपको जावा 5 पर होना चाहिए और आपका JRE डिफ़ॉल्ट एन्कोडिंग UTF-8 होना चाहिए।
ZZ कोडर

2
यह अमल को लिख रहा है, अमूर्त को नहीं। यदि आप अनिर्धारित सामान पर भरोसा करते हैं, तो जब आप प्लेटफ़ॉर्म के नए संस्करण में अपग्रेड करते हैं तो आपका कोड टूट जाता है तो आश्चर्यचकित न हों।
मैकडॉवेल

24

क्या यह बग या फीचर है?

अपरिभाषित व्यवहार की तरह दिखता है। मुझे पता है कि व्यवहार में, आप कमांड-लाइन प्रॉपर्टी का उपयोग करके डिफ़ॉल्ट एन्कोडिंग को बदल सकते हैं, लेकिन मुझे नहीं लगता कि जब आप ऐसा करते हैं तो यह परिभाषित होता है।

बग आईडी: 4153515 इस संपत्ति को स्थापित करने में समस्याओं पर :

यह एक बग नहीं है। J2SE प्लेटफ़ॉर्म विनिर्देश द्वारा "file.encoding" संपत्ति की आवश्यकता नहीं है; यह सूर्य के कार्यान्वयन का आंतरिक विवरण है और उपयोगकर्ता कोड द्वारा जांच या संशोधित नहीं किया जाना चाहिए। यह भी केवल पढ़ने के लिए इरादा है; प्रोग्राम के निष्पादन के दौरान कमांड लाइन पर या किसी अन्य समय पर इस संपत्ति की मनमानी मूल्यों का समर्थन करना तकनीकी रूप से असंभव है।

VM और रनटाइम सिस्टम द्वारा उपयोग किए जाने वाले डिफ़ॉल्ट एन्कोडिंग को बदलने का पसंदीदा तरीका यह है कि आप अपने जावा प्रोग्राम को शुरू करने से पहले अंतर्निहित प्लेटफ़ॉर्म के स्थान को बदल दें।

जब मैं कमांड लाइन पर एन्कोडिंग सेट करने वाले लोगों को देखता हूं, तो मुझे बहुत परेशानी होती है - आप नहीं जानते कि कौन सा कोड प्रभावित करने वाला है।

यदि आप डिफ़ॉल्ट एन्कोडिंग का उपयोग नहीं करना चाहते हैं, तो एन्कोडिंग सेट करें जिसे आप उचित विधि / निर्माता के माध्यम से स्पष्ट रूप से चाहते हैं ।


4

सबसे पहले, लैटिन -1 ISO-8859-1 के समान है, इसलिए, डिफ़ॉल्ट आपके लिए पहले से ही ठीक था। सही?

आपने अपने कमांड लाइन पैरामीटर के साथ ISO-8859-1 को सफलतापूर्वक एन्कोडिंग सेट किया है। आपने इसे "लैटिन -1" के लिए प्रोग्रामेटिक रूप से सेट किया है, लेकिन, यह जावा के लिए फ़ाइल एन्कोडिंग का एक मान्यता प्राप्त मूल्य नहीं है। Http://java.sun.com/javase/6/docs/technotes/guides/intl/encoding.d.html देखें

जब आप ऐसा करते हैं, तो स्रोत को देखने से, चार्टसेट जैसे UTF-8 में दिखता है। वह कम से कम व्यवहार के बारे में बताता है।

मुझे नहीं पता कि क्यों OutputStreamWriter ISO8859_1 दिखाता है। यह बंद-स्रोत sun.misc को दर्शाता है। * कक्षाएं। मुझे लगता है कि यह काफी समान तंत्र के माध्यम से एन्कोडिंग के साथ काम नहीं कर रहा है, जो अजीब है।

लेकिन निश्चित रूप से आपको हमेशा यह निर्दिष्ट करना चाहिए कि इस कोड में आपके लिए कौन सी एन्कोडिंग है। मैं कभी भी प्लेटफ़ॉर्म डिफ़ॉल्ट पर निर्भर नहीं होता।


4

व्यवहार वास्तव में उतना अजीब नहीं है। वर्गों के कार्यान्वयन को देखते हुए, यह निम्न के कारण होता है:

  • Charset.defaultCharset() जावा 5 में निर्धारित निर्धारित वर्ण को कैशिंग नहीं कर रहा है।
  • सिस्टम प्रॉपर्टी को "file.encoding" सेट करना और Charset.defaultCharset()फिर से इनवॉइस करने से सिस्टम प्रॉपर्टी के दूसरे मूल्यांकन का कारण बनता है, "लैटिन -1" नाम के साथ कोई भी कैरेक्टर सेट नहीं मिलता है, इसलिएCharset.defaultCharset() "UTF-8" के लिए डिफॉल्ट हो जाता है।
  • OutputStreamWriterहालांकि डिफ़ॉल्ट वर्ण सेट कैश कर रहा है और शायद वीएम आरंभीकरण के दौरान पहले से ही प्रयोग किया जाता है, ताकि उसके डिफ़ॉल्ट वर्ण सेट का मार्ग बदल से Charset.defaultCharset()यदि सिस्टम संपत्ति "file.encoding" कार्यावधि में बदल दिया गया है।

जैसा कि पहले ही बताया गया है, यह दस्तावेज नहीं है कि ऐसी स्थिति में वीएम को कैसे व्यवहार करना चाहिए। Charset.defaultCharset()डिफ़ॉल्ट दस्तावेज़ सेट कैसे निर्धारित किया जाता है, इस पर एपीआई प्रलेखन बहुत सटीक नहीं है, केवल यह उल्लेख करते हुए कि यह आमतौर पर वीएम स्टार्टअप पर किया जाता है, ओएस डिफ़ॉल्ट चरित्र सेट या डिफ़ॉल्ट लोकेल जैसे कारकों के आधार पर।


3

मैंने WAS सर्वर में vm तर्क को -file.encoding = UTF-8 के रूप में सेट किया है ताकि सर्वर के डिफ़ॉल्ट चरित्र सेट को बदला जा सके।


1

जाँच

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

यह आपके सिस्टम के कमांड लाइन में उपयोग किए गए समान एन्कोडिंग के समान प्रतीत होता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.