簡體   English   中英

從 Windows 和 Linux 讀取文件會產生不同的結果(字符編碼?)

[英]Reading File from Windows and Linux yields different results (character encoding?)

目前我正在嘗試以 mime 格式讀取文件,該文件具有 png 的一些二進制字符串數據。

在 Windows 中,讀取文件給了我正確的二進制字符串,這意味着我只需復制字符串並將擴展名更改為 png 即可看到圖片。


在 Windows 中讀取文件后的示例如下:

    --fh-mms-multipart-next-part-1308191573195-0-53229
     Content-Type: image/png;name=app_icon.png
     Content-ID: "<app_icon>"
     content-location: app_icon.png

    ‰PNG

等等……等等……

在 Linux 中讀取文件后的示例如下:

    --fh-mms-multipart-next-part-1308191573195-0-53229
     Content-Type: image/png;name=app_icon.png
     Content-ID: "<app_icon>"
     content-location: app_icon.png

     �PNG

等等……等等……


我無法將 Linux 版本轉換為圖片,因為它都變成了一些帶有很多顛倒“?”的時髦符號。 和“1/2”符號。

任何人都可以啟發我了解正在發生的事情並提供解決方案嗎? 已經玩了一個星期以上的代碼了。

�是三個字符的序列- 0xEF 0xBF 0xBD ,且是Unicode碼點的UTF-8表示0xFFFD 代碼點本身是非法 UTF-8 序列的替換字符

顯然,出於某種原因,您的源代碼(在 Linux 上)中涉及的一組例程不准確地處理 PNG 標頭。 PNG 標頭以字節0x89開頭(后跟0x500x4E0x47 ),在 Windows 中正確處理(可能將文件視為 CP1252 字節序列)。 CP1252中0x89字符顯示為

但是,在 Linux 上,該字節由 UTF-8 例程(或認為可以將文件作為 UTF-8 序列處理的庫)進行解碼。 由於 0x89 本身不是 ASCII-7 范圍內的有效代碼點(參考: UTF-8 編碼方案),因此它無法映射到 0x00-0x7F 范圍內的有效 UTF-8 代碼點。 此外,它無法映射到表示為多字節 UTF-8 序列的有效代碼點,因為所有多字節序列都以至少 2 位設置為 1 ( 11.... ) 開始,並且由於這是文件的開頭,它也不能是連續字節。 由此產生的行為是 UTF-8 解碼器,現在用 UTF-8 替換字符0xEF 0xBF 0xBD替換0x89 (多么愚蠢,考慮到文件不是 UTF-8 開始),它將顯示在ISO-8859 中-1�

如果你需要解決這個問題,你需要在 Linux 中確保以下幾點:

  • 使用適合文件的編碼(即不是 UTF-8)讀取 PNG 文件中的字節; 如果您將文件作為字符序列讀取* ,這顯然是必要的,如果您單獨讀取字節則不需要。 您可能會正確執行此操作,因此也值得驗證后續步驟。
  • 當您查看文件的內容時,請使用合適的編輯器/視圖,該編輯器/視圖不會將文件執行任何內部解碼為 UTF-8 字節序列。 使用合適的字體也會有所幫助,因為您可能希望防止出現無法表示字形(對於0xFFFD實際上是菱形字符編輯器/查看器已編寫)。
  • 用合適的編碼寫出文件(如果你這樣做的話)也是一個好主意——也許是 ISO-8859-1,而不是 UTF-8。 如果您將文件內容作為字節而不是字符處理和存儲在內存中,那么將這些內容寫入輸出流(不涉及任何字符串或字符引用)就足夠了。

*顯然,如果將字節序列轉換為字符或字符串對象,Java 運行時會將字節序列解碼為 UTF-16 代碼點。

在 Java 中, Stringbyte[]

  • byte[]表示原始二進制數據。
  • String表示文本,它具有相關的字符集/編碼,以便能夠分辨出它代表哪些字符。

二進制數據≠文本

String文本數據具有 Unicode/UTF-16 作為字符集/編碼(或序列化時的 Unicode/mUTF-8)。 每當您從不是String轉換為String或反之亦然時,您需要為非String文本數據指定一個字符集/編碼(即使您使用平台的默認字符集隱式執行此操作)。

PNG 文件包含表示圖像(和相關元數據)的原始二進制數據,而不是文本。 因此,您不應將其視為文本。

\\x89PNG不是文本,它只是用於識別 PNG 文件的“神奇”標題。 0x89甚至不是一個字符,它只是一個任意的字節值,它唯一合理的顯示表示是\\x89 , 0x89 ,...同樣, PNG在現實中存在二進制數據,它也可能是0xdeadbeef它不會改變什么。 PNG恰好是人類可讀的這一事實只是一種方便。

您的問題來自這樣一個事實,即您的協議混合了文本和二進制數據,而 Java(與其他一些語言,如 C)對二進制數據的處理方式與文本不同。

Java 提供*InputStream用於讀取二進制數據,以及*Reader用於讀取文本。 我看到兩種處理輸入的方法:

  • 將一切視為二進制數據。 當您閱讀整個文本行時,使用適當的字符集/編碼將其轉換為String
  • InputStreamReader在頂部InputStream的,獲得InputStream直接當你想二進制數據,訪問InputStreamReader當你想要的文字。

您可能需要緩沖,將它放在第二種情況下的正確位置在*Reader下方。 如果您使用BufferedReader ,則BufferedReader可能會比它應該消耗更多的InputStream輸入。 所以,你會有類似的東西:

 ┌───────────────────┐
 │ InputStreamReader │
 └───────────────────┘
          ↓
┌─────────────────────┐
│ BufferedInputStream │
└─────────────────────┘
          ↓
   ┌─────────────┐
   │ InputStream │
   └─────────────┘

您將使用InputStreamReader讀取文本,然后使用BufferedInputStream從同一流中讀取適當數量的二進制數據。

一個有問題的情況是將"\\r" (舊 MacOS)和"\\r\\n" (DOS/Windows)都識別為行終止符。 在這種情況下,您最終可能會過多地閱讀一個字符。 您可以采用已棄用的DataInputStream.readline()方法采用的方法:將內部InputStream透明地包裝到PushbackInputStream並取消DataInputStream.readline()該字符。

但是,由於您似乎沒有Content-Length ,我會推薦第一種方法,將所有內容都視為二進制,並僅在閱讀整行后轉換為String 在這種情況下,我會將 MIME 分隔符視為二進制數據。

輸出:

由於您正在處理二進制數據,因此您不能只是println()它。 PrintStream具有可以處理二進制數據的write()方法(例如:用於輸出到二進制文件)。

或者,您的數據可能必須在將其視為文本的通道上傳輸。 Base64專為這種情況而設計(將二進制數據作為 ASCII 文本傳輸)。 Base64 編碼形式僅使用 US_ASCII 字符,因此您應該能夠將它與作為 US_ASCII 超集(ISO-8859-*、UTF-8、CP-1252...)的任何字符集/編碼一起使用。 由於您正在將二進制數據轉換為文本/從文本轉換,Base64 的唯一合理的 API 是這樣的:

String Base64Encode(byte[] data);
byte[] Base64Decode(String encodedData);

這基本上是內部java.util.prefs.Base64使用的內容。

結論:

在 Java 中, Stringbyte[]

二進制數據≠文本

暫無
暫無

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

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