2009-01-31 30 views
6

我有一個文件,它被編碼爲iso-8859-1,並且包含諸如ô的字符。Java應用程序:無法正確讀取iso-8859-1編碼文件

我讀書用java代碼,像這樣的文件:

File in = new File("myfile.csv"); 
InputStream fr = new FileInputStream(in); 
byte[] buffer = new byte[4096]; 
while (true) { 
    int byteCount = fr.read(buffer, 0, buffer.length); 
    if (byteCount <= 0) { 
     break; 
    } 

    String s = new String(buffer, 0, byteCount,"ISO-8859-1"); 
    System.out.println(s); 
} 

然而ô角色總是出現亂碼,通常打印作爲? 。

我已經讀過這個主題(並且在學習中學到了一些東西),例如

但仍不能得到這個工作

有趣的是這部作品在我的本地PC(XP),但不是我的Linux中。

我已經檢查了我的JDK支持所需字符集(它們是標準的,所以這是沒有驚喜)使用:

System.out.println(java.nio.charset.Charset.availableCharsets()); 
+0

我應該補充一點,我能夠正確地使用我的linux終端看到字符或原始文件,如果我只是簡單地捕捉文件的內容 – Joel 2009-01-31 11:45:08

+0

終端正在使用什麼字符編碼? – McDowell 2009-01-31 11:59:08

+0

有趣的是,如果我添加運行時Java屬性「-Dfile.encoding = UTF16」,它可以按預期工作,但我不明白爲什麼這應該重要 - 我不認爲它是一個解決方案,但更多的是黑客。它不適用於設置爲UTF8的屬性。 – Joel 2009-01-31 12:55:30

回答

12

我懷疑你的文件不是實際上是編碼爲ISO-8859-1,或者System.out不知道如何打印字符。

我建議檢查第一個,檢查文件中的相關字節。要檢查第二,檢查字符串中的字符有關,它打印出來與

System.out.println((int) s.getCharAt(index)); 

在這兩種情況下,結果應該是244小數; 0xf4十六進制。

查看my article on Unicode debugging的一般建議(所提供的代碼是用C#編寫的,但很容易轉換爲Java,原理相同)。

一般來說,順便說一下,我會用正確的編碼將InputStreamReader打包到流中 - 這比「手動」創建新字符串更容易。我意識到這可能只是演示代碼。

編輯:這裏是一個非常簡單的方法來證明控制檯是否會工作:

System.out.println("Here's the character: \u00f4"); 
3

如果可以,儘量在調試器中運行你的程序,看看有什麼是你的內's'字符串創建後。它有可能是正確的內容,但輸出在System.out.println(s)調用後出現亂碼。在這種情況下,Java認爲輸出的編碼和Linux上終端/控制檯的字符編碼之間可能存在不匹配。

9

解析文件作爲一個字節的固定大小的塊不好---如果有些什麼人物都有跨越兩個塊的字節表示?使用一個InputStreamReader用合適的字符編碼來代替:

BufferedReader br = new BufferedReader(
     new InputStreamReader(
     new FileInputStream("myfile.csv"), "ISO-8859-1"); 

char[] buffer = new char[4096]; // character (not byte) buffer 

while (true) 
{ 
     int charCount = br.read(buffer, 0, buffer.length); 

     if (charCount == -1) break; // reached end-of-stream 

     String s = String.valueOf(buffer, 0, charCount); 
     // alternatively, we can append to a StringBuilder 

     System.out.println(s); 
} 

順便說一句,記得檢查Unicode字符確實可以正確顯示。您也可以將程序輸出重定向到一個文件,然後將其與原始文件進行比較。

由於Jon Skeet暗示,問題也可能與控制檯有關。嘗試System.console().printf(s)以查看是否有差異。

1

基本上,如果它在你的本地XP PC上工作,但不在Linux上,並且你正在解析完全相同的文件(即你在盒子之間以二進制方式傳輸它),那麼它可能與System.out.println調用。我不知道你是如何驗證輸出的,但是如果你通過從XP機箱連接遠程shell來完成,那麼就要考慮shell(和客戶機)的字符集。

此外,Zach Scrivena建議的內容也是如此 - 你不能假設你可以用這種方式從數據塊中創建字符串 - 要麼使用InputStreamReader,要麼先讀取完整的數據到數組中(顯然不適用於一個大文件)。但是,因爲它似乎在XP上工作,那麼我敢打賭,這可能不是你在這個特定情況下的問題。

6

@Joel - your own answer確認問題是操作系統上的默認編碼(UTF-8,一個Java選取的)和您的終端正在使用的編碼(ISO-8859-1)之間的差異。

考慮以下代碼:

public static void main(String[] args) throws IOException { 
    byte[] data = { (byte) 0xF4 }; 
    String decoded = new String(data, "ISO-8859-1"); 
    if (!"\u00f4".equals(decoded)) { 
     throw new IllegalStateException(); 
    } 

    // write default charset 
    System.out.println(Charset.defaultCharset()); 

    // dump bytes to stdout 
    System.out.write(data); 

    // will encode to default charset when converting to bytes 
    System.out.println(decoded); 
} 

默認情況下,我的Ubuntu(8.04)終端使用UTF-8編碼。通過此編碼,可以打印:

UTF-8
?&#x00F4;

如果我切換終端的編碼爲ISO 8859-1,這是印刷:

UTF-8
&#x00F4;&#x00C3;&#x00B4;

在兩種情況下,相同的字節被髮射由Java程序:

5554 462d 380a f4c3 b40a 

唯一的區別是在該終端是如何解釋它接收到的字節。在ISO 8859-1中,&#x00F4;被編碼爲0xF4。在UTF-8中,&#x00F4;被編碼爲0xC3B4。其他字符對於這兩種編碼都是通用的。

相關問題