簡體   English   中英

Java 字符串:處理/轉換系統本機字符編碼

[英]Java String: Treating/converting system native character encoding

在訪問 Windows 系統資源(與音頻相關)時,我發現 Windows 使用自己的字符集提供所述資源的描述字符串,而 Java 將這些字符串視為默認處理所有字符串:unicode-encoded。 所以,我得到了一堆問號,而不是合理的文本:

????????? ???????? ???????

使用String .codePointAt ()方法我發現這些問題實際上隱藏了一些使用 Windows-1252 編碼的文本。 我當然想看。 於是我開始將這個字符串轉換成可讀的東西。

半天后,在我翻遍 Stackoverflow 和 Google 尋找相關主題后,我取得了一些進展,但這只會導致更多問題。 所以,這是我的代碼:

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import javax.sound.sampled.AudioSystem;


public class Study_Encoding {
    
    //private static final Charset utf8Charset = Charset .forName ("UTF-8");
    private static final Charset win1251Charset = Charset .forName ("Windows-1251");
    private static final Charset win1252Charset = Charset .forName ("Windows-1252");
    
    public static void main(String[] args) {
        
        String str = AudioSystem .getMixerInfo () [0] .getName ();
        
        System .out .println ("Original string:");
        System .out .println (str + "\n");
        
        System .out .println ("Its code-points:");
        displayCodePointSequence (str);
        
        System .out .println ("Windows-1251-decoded byte array (wrong):");
        byte [] win1251ByteArr = str .getBytes (win1251Charset);
        displayByteSequence (win1251ByteArr);
        
        System .out .println ("Windows-1252-decoded byte array (right):");
        byte [] win1252ByteArr = str .getBytes (win1252Charset);
        displayByteSequence (win1252ByteArr);
        
        System .out .println ("Windows-1252-encoded string (wrong):");
        try {
            System .out .println (win1252Charset .newDecoder ()
                    .decode (ByteBuffer .wrap (win1252ByteArr)) .toString () + "\n");
        } catch (Exception e) {
            System .out .println ("ERROR:" + e .toString ());
        }
        
        System .out .println ("Windows-1251-encoded string (right):");
        try {
            System .out .println (win1251Charset .newDecoder ()
                    .decode (ByteBuffer .wrap (win1252ByteArr)) .toString () + "\n");
        } catch (Exception e) {
            System .out .println ("ERROR:" + e .toString ());
        }
    }
    
    private static void displayCodePointSequence (String str) {
        
        if (null == str) {
            System .out .println ("No string");
            return;
        }
        if (str .isEmpty ()) {
            System .out .println ("Empty string");
            return;
        }
        for (int k = 0; str .length () > k; ++k) {
            System .out .print (str .codePointAt (k) + " ");
        }
        System .out .println ("[" + str .length () + "]\n");
    }
    
    private static void displayByteSequence (byte [] byteArr) {
        
        if (null == byteArr) {
            System .out .println ("No array");
            return;
        }
        if (0 == byteArr .length) {
            System .out .println ("Empty array");
            return;
        }
        for (int k = 0; byteArr .length > k; ++k) {
            System .out .print ((((int) byteArr [k]) & 0xFF) + " ");
        }
        System .out .println ("[" + byteArr .length + "]\n");
    }
}

該程序產生以下輸出(最后一行是我一直想要的):

Original string:
????????? ???????? ???????

Its code-points:
207 229 240 226 232 247 237 251 233 32 231 226 243 234 238 226 238 233 32 228 240 224 233 226 229 240 [26]

Windows-1251-decoded byte array (wrong):
63 63 63 63 63 63 63 63 63 32 63 63 63 63 63 63 63 63 32 63 63 63 63 63 63 63 [26]

Windows-1252-decoded byte array (right):
207 229 240 226 232 247 237 251 233 32 231 226 243 234 238 226 238 233 32 228 240 224 233 226 229 240 [26]

Windows-1252-encoded string (wrong):
????????? ???????? ???????

Windows-1251-encoded string (right):
Первичный звуковой драйвер

任何人都可以看到由於某種原因 win1251 和 win1252 編碼混合在一起。 另外,我想,有一種方法可以讓 Java 程序將所有字符串視為某些本機編碼(我不想要!!!)中的字符串,或者至少是系統提供的字符串。 所以,...

...我的問題是:

  1. 如何轉換字符串? (我猜我已經解決了)
  2. 這是怎么回事? (混合字符集和所有其他)
  3. 怎么做才對? (字符串獲取,如果沒有,字符串轉換)

編輯:

似乎我沒有說清楚,但我不是在談論文本文件的內容,而是在談論系統提供的字符串,例如設備(物理和虛擬)的名稱和描述,也許是文件和目錄名稱。 在上面的示例中,字符串“Первичный звуковой драйвер”應該類似於英語Windows中的“默認音頻設備”。

這是一個令人費解的問題,但基本知識是:

  1. 沒有沒有編碼的字符串這樣的東西。 最常見的形式(c 字符串)使用 ASCII 編碼。 Java 本身使用 UTF16。
  2. 某些字符集之間沒有完美的編碼轉換。 例如 ASCII -> EBCDIC -> ASCII 由於這些字符集之間缺乏 1:1 的關系而導致字符串損壞。
  3. 對我來說,該文件似乎包含 1 個字符集中的數據,並且您希望將其轉換為 Java 本機格式 (UTF16)。 這很簡單。 您可以使用 FileInputStream 來讀取字節數據。 您可以使用 Reader 讀取字符串數據。 因此,您希望您的閱讀器執行轉換: https : //docs.oracle.com/javase/8/docs/api/java/io/InputStreamReader.html#InputStreamReader(java.io.InputStream,%20java.nio.charset .字符集)

所以基本上,你所追求的代碼是這樣的:

try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(myFile), StandardCharsets.CHARSETOFCHOICE)))
{
   String line;
   while ((line = br.readLine()) != null)
   {
      // Do what you want with the string.
   }
}

我將重申,根據源/目標字符集,轉換可能不完美,並可能導致損壞。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM