将byte 编码为String

Heyho,

我想将字节数据(可以是任何东西)转换为String。 我的问题是,用UTF-8编码字节数据是否“安全”,例如:

String s1 = new String(data, "UTF-8"); 

或者使用base64:

 String s2 = Base64.encodeToString(data, false); //migbase64 

我只是担心使用第一种方法会产生负面影响。 我的意思是两个变体都可以工作,但是s1可以包含UTF-8字符集的任何字符, s2只使用“可读”字符。 我只是不确定它是否真的需要使用base64。 基本上我只需要创建一个String,通过网络发送它并再次接收它。 (在我的情况下没有别的办法:/)

问题只是关于消极的副作用 ,而不是如果可能的话!

绝对应该使用base64或者hex。 (要么工作; base64更紧凑,但人类更难阅读。)

你声称“两种变体都能完美地工作”,但事实并非如此。 如果使用第一种方法且data实际上不是有效的UTF-8序列,则会丢失数据。 您不是要尝试将UTF-8编码的文本转换为String ,因此不要编写尝试这样做的代码。

使用ISO-8859-1作为编码将保留所有数据 – 但在很多情况下,返回的字符串将不容易通过其他协议传输。 例如,它可能包含不可打印的控制字符。

当你拥有固有的文本数据时,只使用String(byte[], String)构造函数,你恰好有编码forms(编码被指定为第二个参数)。 对于其他任何东西 – 例如音乐,video,图像,加密或压缩数据 – 你应该使用一种方法,将输入数据视为“任意二进制数据”,并找到它的文本编码……这正是base64和hex。

你可以在String中存储一个字节,虽然这不是一个好主意。 您不能使用UTF-8,因为这将管理字节,但更快更有效的方法是使用ISO-8859-1编码或普通的8位。 最简单的方法是使用

 String s1 = new String(data, 0); 

要么

 String s1 = new String(data, "ISO-8859-1"); 

来自维基百科上的UTF-8 ,如Jon Skeet所述,这些编码在标准下无效。 他们在Java中的行为各不相同 DataInputStream将前三个版本视为相同,后两个抛出exception。 Charset解码器默默地将它们视为单独的字符。

 00000000 is \0 11000000 10000000 is \0 11100000 10000000 10000000 is \0 11110000 10000000 10000000 10000000 is \0 11111000 10000000 10000000 10000000 10000000 is \0 11111100 10000000 10000000 10000000 10000000 10000000 is \0 

这意味着如果在String中看到\ 0,则无法确定原始byte []值是什么。 DataOutputStream使用第二个选项与C兼容,将C视为终止符。

BTW DataOutputStream不知道代码点,因此以UTF-16编写高代码点字符,然后写入UTF-8编码。

0xFE和0xFF无效出现在字符中。 值0x11000000 +只能出现在字符的开头,而不能出现在多字节字符内。

用Java证实了已接受的答案。 重复一遍,UTF-8,UTF-16不保留所有字节值。 ISO-8859-1确实保留了所有字节值。 但是如果要将编码的字节传输到JVM之外,请使用Base64。

 @Test public void testBase64() { final byte[] original = enumerate(); final String encoded = Base64.encodeBase64String( original ); final byte[] decoded = Base64.decodeBase64( encoded ); assertTrue( "Base64 preserves bytes", Arrays.equals( original, decoded ) ); } @Test public void testIso8859() { final byte[] original = enumerate(); String s = new String( original, StandardCharsets.ISO_8859_1 ); final byte[] decoded = s.getBytes( StandardCharsets.ISO_8859_1 ); assertTrue( "ISO-8859-1 preserves bytes", Arrays.equals( original, decoded ) ); } @Test public void testUtf16() { final byte[] original = enumerate(); String s = new String( original, StandardCharsets.UTF_16 ); final byte[] decoded = s.getBytes( StandardCharsets.UTF_16 ); assertFalse( "UTF-16 does not preserve bytes", Arrays.equals( original, decoded ) ); } @Test public void testUtf8() { final byte[] original = enumerate(); String s = new String( original, StandardCharsets.UTF_8 ); final byte[] decoded = s.getBytes( StandardCharsets.UTF_8 ); assertFalse( "UTF-8 does not preserve bytes", Arrays.equals( original, decoded ) ); } @Test public void testEnumerate() { final Set byteSet = new HashSet<>(); final byte[] bytes = enumerate(); for ( byte b : bytes ) { byteSet.add( b ); } assertEquals( "Expecting 256 distinct values of byte.", 256, byteSet.size() ); } /** * Enumerates all the byte values. */ private byte[] enumerate() { final int length = Byte.MAX_VALUE - Byte.MIN_VALUE + 1; final byte[] bytes = new byte[length]; for ( int i = 0; i < length; i++ ) { bytes[i] = (byte)(i + Byte.MIN_VALUE); } return bytes; }