如何将字节数组转换为字符串,反之亦然?


247

我必须在Android中将字节数组转换为字符串,但是我的字节数组包含负值。

如果我再次将该字符串转换为字节数组,则得到的值与原始字节数组值不同。

我该怎么做才能正确转换?我用于执行转换的代码如下:

// Code to convert byte arr to str:
byte[] by_original = {0,1,-2,3,-4,-5,6};
String str1 = new String(by_original);
System.out.println("str1 >> "+str1);

// Code to convert str to byte arr:
byte[] by_new = str1.getBytes();
for(int i=0;i<by_new.length;i++) 
System.out.println("by1["+i+"] >> "+str1);

我陷入了这个问题。


3
为什么首先要尝试将任意二进制数据转换为String?除了已经提到的所有字符集问题之外,如果这样做,您还会滥用String。将a byte[]用作二进制数据和String文本有什么问题?
约阿希姆·绍尔

8
@Joachim-有时您拥有可以执行诸如存储字符串之类的外部工具。在这种情况下,您希望能够将字节数组转换为(以某种方式编码)字符串。
James Moore

Answers:


377

您的字节数组必须具有某种编码。如果值为负,则编码不能为ASCII。一旦弄清楚了,就可以使用以下方法将一组字节转换为字符串:

byte[] bytes = {...}
String str = new String(bytes, "UTF-8"); // for UTF-8 encoding

您可以使用许多编码,请查看Sun javadocs中的Charset类。


4
@MauricePerry您能解释为什么它不能使用UTF-8吗?
阿西夫·穆斯塔克

12
@UnKnown,因为UTF-8将某些字符编码为2或3字节字符串。并非每个字节数组都是有效的UTF-8编码的字符串。ISO-8859-1将是一个更好的选择:这里每个字符都被编码为一个字节。
莫里斯·佩里

1
这可能有效,但是您应该避免不惜一切代价使用String构造函数。
hfontanez

将一个字节映射到一个字符(使用8859-1)并且没有异常处理(使用nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
iman

1
从Java 1.7开始,您可以使用新的String(bytes,StandardCharsets.UTF_8)
ihebiheb

101

byte[]和之间的“正确转换” String用于明确说明您要使用的编码。如果以a开头byte[]且实际上不包含文本数据,则没有 “正确转换”。Strings用于文本,byte[]用于二进制数据,唯一真正明智的做法是避免在它们之间进行转换,除非绝对必要。

如果确实必须使用A String来保存二进制数据,那么最安全的方法是使用Base64编码。


1
是的,字符编码是您必须了解的有关在字符串和字节之间进行转换的知识。
Raedwald 2015年

4
Base64,您救了我的命
mstzn

2
Base64编码解决了我的问题。UTF-8并没有对所有投入工作
铝阿拉明

37

根本问题是(我认为)您无意中使用了以下字符集:

 bytes != encode(decode(bytes))

在某些情况下。UTF-8是此类字符集的示例。具体来说,某些字节序列在UTF-8中不是有效的编码。如果UTF-8解码器遇到这些序列之一,则有可能丢弃有问题的字节或将它们解码为“无此类字符”的Unicode代码点。自然,当您随后尝试将字符编码为字节时,结果将有所不同。

解决方案是:

  1. 明确说明您正在使用的字符编码;即使用字符串构造函数和String.toByteArray具有显式字符集方法。
  2. 为您的字节数据使用正确的字符集...或替代一种(例如“ Latin-1”,其中所有字节序列都映射到有效的Unicode字符)。
  3. 如果您的字节是(真的)二进制数据,并且您希望能够通过“基于文本”的通道发送/接收它们,请使用为此目的而设计的诸如Base64编码之类的东西。

1
感谢您使用“ Latin-1”编码的提示!
奇闻趣事

31

我们只需要String使用数组构建一个新数组:http : //www.mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

String s = new String(bytes);

根据您使用的字符集,结果字符串的字节会有所不同。当您调用String#时,新的String(bytes)和新的String(bytes,Charset.forName(“ utf-8”))和新的String(bytes,Charset.forName(“ utf-16”))都将具有不同的字节数组。 getBytes()(取决于默认字符集)


9
否。结果字符串的字节根据您使用的字符集而有所不同。new String(bytes)new String(bytes, Charset.forName("utf-8"))并且new String(bytes, Charset.forName("utf-16"))在调用时都将具有不同的字节数组String#getBytes()(取决于默认字符集)
NS du Toit 2015年

1
误导。当进行不同的解码时char,结果的s(从而显示的文本)也String有所bytes不同。使用默认编码(String#getBytes("charset")否则用于指定)转换回字节的方法必然会有所不同,因为它会转换不同的输入。字符串不存储byte[]它们的来源,chars没有编码,a String不存储。
zapl

14

使用new String(byOriginal)并转换回byte[]using getBytes()不能保证两个byte[]值相等。这是由于调用StringCoding.encode(..)将编码StringCharset.defaultCharset()。在此编码过程中,编码器可能会选择替换未知字符并进行其他更改。因此,using String.getBytes()可能不会返回您最初传递给构造函数的相等数组。


9

问题出在哪里正如已经有人指出的那样: 如果您以byte []开头并且实际上不包含文本数据,则没有“正确转换”。字符串用于文本,byte []用于二进制数据,唯一真正明智的做法是避免在它们之间进行转换,除非绝对必要。

当我尝试从pdf文件创建byte [],然后将其转换为String,然后将String作为输入并转换回文件时,我正在观察此问题。

因此,请确保您的编码和解码逻辑与我相同。我将byte []显式编码为Base64并将其解码以再次创建文件。

用例: 由于某些限制,我试图发送byte[]request(POST)过程如下:

PDF文件>> Base64.encodeBase64(byte [])>>字符串>>发送请求(POST)>>接收字符串>> Base64.decodeBase64(byte [])>>创建二进制文件

试试这个,这对我有用。

File file = new File("filePath");

        byte[] byteArray = new byte[(int) file.length()];

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            fileInputStream.read(byteArray);

            String byteArrayStr= new String(Base64.encodeBase64(byteArray));

            FileOutputStream fos = new FileOutputStream("newFilePath");
            fos.write(Base64.decodeBase64(byteArrayStr.getBytes()));
            fos.close();
        } 
        catch (FileNotFoundException e) {
            System.out.println("File Not Found.");
            e.printStackTrace();
        }
        catch (IOException e1) {
            System.out.println("Error Reading The File.");
            e1.printStackTrace();
        }

6

这对我来说很好:

String cd="Holding some value";

从字符串转换为字节[]:

byte[] cookie = new sun.misc.BASE64Decoder().decodeBuffer(cd);

从byte []转换为字符串:

cd = new sun.misc.BASE64Encoder().encode(cookie);

5
private static String toHexadecimal(byte[] digest){
        String hash = "";
    for(byte aux : digest) {
        int b = aux & 0xff;
        if (Integer.toHexString(b).length() == 1) hash += "0";
        hash += Integer.toHexString(b);
    }
    return hash;
}

这不能回答问题。
james.garriss 2015年

无法回答问题,但很有用+1
Lazy Ninja

5

我确实注意到了所有答案中都没有的内容。您可以将字节数组中的每个字节转换为字符,然后将其放入char数组中。然后字符串是

new String(cbuf)
其中cbuf是char数组。要进行转换,请遍历将每个字符转换为字节的字符串,然后将其放入字节数组,该字节数组将与第一个相同。


public class StringByteArrTest {

    public static void main(String[] args) {
        // put whatever byte array here
        byte[] arr = new byte[] {-12, -100, -49, 100, -63, 0, -90};
        for (byte b: arr) System.out.println(b);
        // put data into this char array
        char[] cbuf = new char[arr.length];
        for (int i = 0; i < arr.length; i++) {
            cbuf[i] = (char) arr[i];
        }
        // this is the string
        String s = new String(cbuf);
        System.out.println(s);

        // converting back
        byte[] out = new byte[s.length()];
        for (int i = 0; i < s.length(); i++) {
            out[i] = (byte) s.charAt(i);
        }
        for (byte b: out) System.out.println(b);
    }

}

2

javax.xml.bind.DatatypeConverter 应该这样做:

byte [] b = javax.xml.bind.DatatypeConverter.parseHexBinary("E62DB");
String s = javax.xml.bind.DatatypeConverter.printHexBinary(b);

2

这里有一些将字节数组转换为字符串的方法。我已经测试过它们运作良好。

public String getStringFromByteArray(byte[] settingsData) {

    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(settingsData);
    Reader reader = new BufferedReader(new InputStreamReader(byteArrayInputStream));
    StringBuilder sb = new StringBuilder();
    int byteChar;

    try {
        while((byteChar = reader.read()) != -1) {
            sb.append((char) byteChar);
        }
    }
    catch(IOException e) {
        e.printStackTrace();
    }

    return sb.toString();

}

public String getStringFromByteArray(byte[] settingsData) {

    StringBuilder sb = new StringBuilder();
    for(byte willBeChar: settingsData) {
        sb.append((char) willBeChar);
    }

    return sb.toString();

}

2

即使

new String(bytes, "UTF-8")

是正确的,它抛出一个UnsupportedEncodingException,迫使您处理一个已检查的异常。从Java 1.6开始,您可以使用另一个构造函数来将字节数组转换为String

new String(bytes, StandardCharsets.UTF_8)

这一点不会引发任何异常。

向后转换也应使用StandardCharsets.UTF_8

"test".getBytes(StandardCharsets.UTF_8)

同样,您避免必须处理受检查的异常。


1

我使用此方法成功将字节数组转换为字符串:

public static String byteArrayToString(byte[] data){
    String response = Arrays.toString(data);

    String[] byteValues = response.substring(1, response.length() - 1).split(",");
    byte[] bytes = new byte[byteValues.length];

    for (int i=0, len=bytes.length; i<len; i++) {
        bytes[i] = Byte.parseByte(byteValues[i].trim());
    }

    String str = new String(bytes);
    return str.toLowerCase();
}

1

虽然base64编码是安全的,并且可以说出“正确的答案”,但我来到这里是在寻找一种将Java字节数组原样转换为Java String的方法。也就是说,字节数组的每个成员在其String副本中均保持不变,而无需进行编码/传输的额外空间。

描述8位透明编码的答案对我很有帮助。我使用ISO-8859-1了数TB的二进制数据来成功地进行来回转换(二进制<->字符串),而没有base64编码所需的膨胀空间要求,因此对于我的用例-YMMV是安全的。

这也有助于解释何时/是否应该进行实验。


0
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;    

private static String base64Encode(byte[] bytes)
{
    return new BASE64Encoder().encode(bytes);
}

private static byte[] base64Decode(String s) throws IOException
{
    return new BASE64Decoder().decodeBuffer(s);
}

为什么?为什么要通过Base64才能将字节转换为字符串?开销。
james.garriss 2015年

0

这里是工作代码。

            // Encode byte array into string . TemplateBuffer1 is my bytearry variable.

        String finger_buffer = Base64.encodeToString(templateBuffer1, Base64.DEFAULT);
        Log.d(TAG, "Captured biometric device->" + finger_buffer);


        // Decode String into Byte Array. decodedString is my bytearray[] 
        decodedString = Base64.decode(finger_buffer, Base64.DEFAULT);


-1

从读出的字节String使用ByteArrayInputStream和包装它与BufferedReader它是焦炭流,而不是字节流中与字节数据转换成字符串。

package com.cs.sajal;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

public class TestCls {

    public static void main(String[] args) {

        String s=new String("Sajal is  a good boy");

        try
        {
        ByteArrayInputStream bis;
        bis=new ByteArrayInputStream(s.getBytes("UTF-8"));

        BufferedReader br=new BufferedReader(new InputStreamReader(bis));
        System.out.println(br.readLine());

        }
        catch(Exception e)
        {
            e.printStackTrace();
        }

    }
}

输出为:

沙加尔是个好男孩


-1

您可以使用简单的for循环进行转换:

public void byteArrToString(){
   byte[] b = {'a','b','$'};
   String str = ""; 
   for(int i=0; i<b.length; i++){
       char c = (char) b[i];
       str+=c;
   }
   System.out.println(str);
}


-3

字符串是char的集合(16位无符号)。因此,如果您要将负数转换为字符串,则它们会在翻译中丢失。


1
-1:这是不正确的。虽然“字节”是Java中的带符号类型,但是执行字符集编码和解码的库代码将它们视为无符号类型。
斯蒂芬·C

一个很好的例子说明为什么使用无符号8位数据类型确实是一个好主意。避免不必要的混乱; ^)
Toad

是因为Java的UTF-16的谨慎假设一个Java字符将是16位,他们可以扩展至32位
乔·普兰特

1
@Toad实际上是的,当存储为UTF-16时,某些Unicode字符占用两个代码点,即32位。在UTF-8中也会发生相同的情况:某些字符使用两个/三个/四个代码点,即16/24/32位。实际上,这正是UTF的意义所在(即UTF!= Unicode)。
CAFxX

1
@Toad您将获得第一个代理-即仅字符的第一个“半”。查看文档中的String.charAt方法和Character类。
CAFxX 2012年

-3
public class byteString {

    /**
     * @param args
     */
    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        String msg = "Hello";
        byte[] buff = new byte[1024];
        buff = msg.getBytes("UTF-8");
        System.out.println(buff);
        String m = new String(buff);
        System.out.println(m);


    }

}

通过Charset编码作为对getBytes的争论
Shyam Sreenivasan

1
您可能需要考虑在代码之外补充说明以充实此答案。
Charlie Schliesser 2015年

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.