2017-03-26 52 views
0

我使用以下代碼以UTF-8格式將字符串寫入流。我在我的字符串的字節前加上一個有符號的短,然後我寫出來。有一個例外:我不能以0x0010作爲前綴,因爲它是最終格式的關鍵字。但是我必須確保讀者最終得到與我的str參數完全相同的字符串,即使它的長度是0x0010。是否有任何UTF-8無法識別的字節?

public static void writeString(DataOutputStream out,String str) throws IOException{ 
    byte[] bytes = str.getBytes(CHARSET_UTF_8); 
    if(bytes.length > Short.MAX_VALUE){ 
     throw new IOException(); 
    } 
    short len = (short)bytes.length; 
    if(bytes.length == 0x0010){ 
     len++; 
    } 
    out.writeShort(len); 
    out.write(bytes); 
    if(bytes.length == 0x0010){ 
     out.write(DEAD_BYTE); 
    } 
} 
public static final Charset CHARSET_UTF_8 = Charset.forName("UTF-8"); 

UTF-8在字符串末尾無法識別的字節是否存在(256)?

另外,以下問題沒有幫助我。最後我以?的角色結束了。 30025693

+0

是的,0xFF是UTF-8中不能在任何地方的字節之一。而任何≥0xC0的數據都不能成爲最後一個。 – user2233709

+0

'0xff'給出'?'它不起作用。 – Gergely

+1

不知道你的意思。你的問題是你應該爲DEAD_BYTE使用什麼值? (我不知道java,不知道這是一個語言定義的值還是你自己定義的東西。)如果是這樣,我認爲你會好的0x00。無論如何,你處理特殊值0x10的方式對我來說看起來很錯... – user2233709

回答

1

默認情況下,您放入UTF-8字符串的任何內容都將被解碼爲某個字符。如果它不是有效的UTF-8序列,將使用替換字符() - 並且仍將出現在輸出中。

您可以從輸出字符串中去除,但它也可能來自輸入字符串。相反,你應該從編碼的UTF-8字節剝去額外的字節:

static String readString(final DataInputStream in) throws IOException { 
    int len = in.readUnsignedShort(); 
    final byte[] bytes = new byte[len]; 
    in.read(bytes); 
    if (bytes[len - 1] == -1) { 
     len--; 
    } 
    return new String(bytes, 0, len, UTF_8); 
} 

另一種選擇是編碼長度時跳過0x0010和由1移動上述所有值:

static void writeString(final DataOutputStream out, final String str) throws IOException { 
    final byte[] bytes = str.getBytes(UTF_8); 
    short len = (short) bytes.length; 
    if (bytes.length >= 0x0010) { 
     len++; 
    } 
    out.writeShort(len); 
    out.write(bytes); 
} 

static String readString(final DataInputStream in) throws IOException { 
    int len = in.readUnsignedShort(); 
    if (len == 0x0010) { 
     throw new IllegalStateException(); 
    } else if (len > 0x0010) { 
     len--; 
    } 
    final byte[] bytes = new byte[len]; 
    in.read(bytes); 
    return new String(bytes, UTF_8); 
} 

兩種這些解決方案是黑客並可能在未來造成麻煩。正確的解決方案是去除這種人爲限制:

  1. 如果您控制最終格式,請重新設計它,以便允許任何字節序列。
  2. 否則,如果0x0010僅在第一個位置被禁止,請始終在其中放置一個常數值,然後再放入實際長度。 (例如:00 11 00 10 ...
  3. 否則,如果0x0010不能在任何位置出現,逃避它:\x00\x10被編碼爲\\n\被編碼爲\\

最後0x0010看起來像UTF-16編碼的新行。如果確實如此,您不應該將二進制數據放入文本中 - 這會導致更多的問題。在這種情況下,您應該將字符串直接放在UTF-16編碼的文本中,或者使用像base64這樣的ASCII安全編碼。