将字节数组转换为字符串(Java)


85

我正在用Google App Engine编写一个Web应用程序。它允许人们从根本上编辑html代码,这些代码作为.html文件存储在blobstore中。

我正在使用fetchData返回byte[]文件中所有字符中的一个。我正在尝试打印到html,以便用户编辑html代码。一切正常!

现在这是我唯一的问题:

转换回字符串时,字节数组存在一些问题。智能引号和几个字符看上去很时髦。(?或日语符号等。)具体来说,我看到的是几个字节,它们的负值导致了问题。

智能引号赶回来,-108-109字节数组英寸 为什么会这样,如何解码负字节以显示正确的字符编码?



嗨,我知道这是一个非常古老的职位,但我面临着类似的问题。我正在为ssl做中间人代理。我面临的问题与您的相同。我监听套接字,然后将数据InputStream放入byte[]。现在,当我尝试将转换byte[]为String时(我需要使用响应主体进行攻击),我得到的是非常有趣的字符,充满了智能引号和问号,而没有。我相信你的问题是和我一样,因为我们都正在处理htmlbyte[]。你能请教吗?
Parul S 2014年

顺便说一句,我竭尽全力使用Sytem.properties查找系统的编码,并发现它是“ Cp1252”。现在,我使用String str=new String(buffer, "Cp1252");但没有帮助。
Parul S 2014年

Answers:


141

字节数组包含采用特殊编码的字符(您应该知道)。将其转换为字符串的方法是:

String decoded = new String(bytes, "UTF-8");  // example for one encoding type

顺便说一句-原始字节的出现可能显示为负十进制,这仅仅是因为java数据类型byte已签名,它涵盖了从-128到127的范围。


-109 = 0x93: Control Code "Set Transmit State"

值(-109)是UNICODE中不可打印的控制字符。因此,UTF-8不是该字符流的正确编码。

0x93“ Windows-1252”中的“智能引号”是您要查找的,因此该编码的Java名称为“ Cp1252”。下一行提供了一个测试代码:

System.out.println(new String(new byte[]{-109}, "Cp1252")); 

5
我尝试使用UTF-8,但仍然以?的形式出现。为什么找不到这些负值的映射?
乔什(Josh)

但是,0x93在UTF-8中是有效的连续字节-如果该字节不出现在设置了前两位的字节之后,则该字节的存在仅排除它是UTF-8。
尼克·约翰逊,

1
@Josh Andreas解释了原因-因为Java的byte数据类型已签名。“负”值只是设置了最高有效字节的字节。他还解释了您最应该使用的字符集是-Windows-1252。但是,您应该从上下文或约定中知道要使用的字符集,而不必猜测。
尼克·约翰逊

25

Java 7及以上

您也可以将所需的编码String作为Charset常量传递给StandardCharsets。这可能比将编码作为String如其他答案中所建议的,。

例如,对于UTF-8编码

String bytesAsString = new String(bytes, StandardCharsets.UTF_8);

1
这是2011
james.garriss15

2
@ james.garriss我不这么认为,就我刚才提到的是Java 7中引入的新构造函数而言,它允许将编码作为常量传递,在我看来,它比以前的api更好,更安全。前面的答案中提到过,如果有的话,编码是作为字符串传递的。
davnicwil


5
public class Main {

    /**
     * Example method for converting a byte to a String.
     */
    public void convertByteToString() {

        byte b = 65;

        //Using the static toString method of the Byte class
        System.out.println(Byte.toString(b));

        //Using simple concatenation with an empty String
        System.out.println(b + "");

        //Creating a byte array and passing it to the String constructor
        System.out.println(new String(new byte[] {b}));

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        new Main().convertByteToString();
    }
}

输出量

65
65
A

5
public static String readFile(String fn)   throws IOException 
{
    File f = new File(fn);

    byte[] buffer = new byte[(int)f.length()];
    FileInputStream is = new FileInputStream(fn);
    is.read(buffer);
    is.close();

    return  new String(buffer, "UTF-8"); // use desired encoding
}

3
如果read抛出异常,此代码将泄漏资源。
Raedwald

4

我建议 Arrays.toString(byte_array);

这取决于您的目的。例如,我想保存一个字节数组,就像在调试时看到的格式一样:[1, 2, 3]如果您要保存完全相同的值而不将字节转换为字符格式,请Arrays.toString (byte_array)执行此操作。但是,如果要保存字符而不是字节,则应使用String s = new String(byte_array)。在这种情况下,s等于[1, 2, 3]字符格式中的。


您能否提供更多有关为什么建议这样做的信息?(它将解决问题吗?您能说说为什么要解决它吗?)谢谢!
Dean J

这取决于您的目的。例如,我想保存一个字节数组,其格式完全类似于调试时可以看到的格式:[1、2、3]如果要保存完全相同的值而不将字节转换为字符格式, Arrays.toString(byte_array)执行此操作。但是,如果要保存字符而不是字节,则应使用String s = new String(byte_array)。在这种情况下,s等于字符格式的[1、2、3]。
发问者

@sas,您应该将此信息添加到答案本身(通过编辑),而不是作为注释。通常,在SO上,您应始终牢记,可以随时删除评论-真正重要的信息应该在答案本身中。
Jeen Broekstra 2015年

3

来自Andreas_D的先前答案很好。我要补充一点,无论您在哪里显示输出,都会有一个字体和一个字符编码,并且可能不支持某些字符。

要确定是Java还是显示问题,请执行以下操作:

    for(int i=0;i<str.length();i++) {
        char ch = str.charAt(i);
        System.out.println(i+" : "+ch+" "+Integer.toHexString(ch)+((ch=='\ufffd') ? " Unknown character" : ""));
    }

Java会将无法理解的任何字符映射到0xfffd未知字符的正式字符。如果看到“?” 在输出中,但未映射到0xfffd,这是问题在于显示字体或编码,而不是Java。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.