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

How to convert byte array to string and vice versa?

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

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

我该怎么做才能得到正确的转换呢?我用来进行转换的代码如下:

1
2
3
4
5
6
7
8
9
// 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);

我陷入了这个问题。


字节数组必须有一些编码。如果有负值,则编码不能是ASCII。一旦您了解了这一点,就可以使用以下方法将一组字节转换为字符串:

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

您可以使用许多编码,看看Sun JavaDocs中的charset类。


byte[]String之间的"正确转换"是显式地说明要使用的编码。如果您从一个byte[]开始,它实际上不包含文本数据,那么就没有"正确的转换"。Strings用于文本,byte[]用于二进制数据,唯一真正明智的做法是避免它们之间的转换,除非您必须这样做。

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


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

1
 bytes != encode(decode(bytes))

在某些情况下。UTF-8就是这样一个字符集的例子。具体来说,某些字节序列不是有效的UTF-8编码。如果UTF-8解码器遇到这些序列中的一个,它可能会丢弃有问题的字节,或者将它们作为"无此类字符"的Unicode码位进行解码。当然,当您尝试将字符编码为字节时,结果会有所不同。

解决方案是:

  • 明确使用的字符编码;即使用字符串构造函数和带有明确字符集的String.toByteArray方法。
  • 为字节数据使用正确的字符集…或者一个(如"拉丁-1",其中所有字节序列映射到有效的Unicode字符。
  • 如果您的字节是(真的)二进制数据,并且您希望能够通过"基于文本"的通道发送/接收它们,请使用类似base64编码的方法…就是为了这个目的而设计的。

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

    1
    String s = new String(bytes);

    结果字符串的字节因使用的字符集而异。调用string getbytes()时,new string(bytes)、new string(bytes、charset.forname("utf-8")和new string(bytes、charset.forname("utf-16")都将具有不同的字节数组(取决于默认字符集)


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


    问题的原因:正如某人已经指定的那样:如果以字节[]开头,但它实际上不包含文本数据,则不存在"正确的转换"。字符串用于文本,字节[]用于二进制数据,唯一真正明智的做法是避免它们之间的转换,除非必须这样做。

    当我试图从PDF文件创建byte[]并将其转换为字符串,然后将字符串作为输入并转换回文件时,我观察到了这个问题。

    所以确保你的编码和解码逻辑和我一样。我将字节[]显式编码为base64,然后对其进行解码以再次创建文件。

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

    PDF文件>>base64.encodebase64(byte[])>>string>>发送请求(post)>>接收字符串>>base64.decodebase64(byte[])>>创建二进制文件

    试试这个,这个对我有用……

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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();
            }

    这对我来说很好:

    1
    String cd="Holding some value";

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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;
    }


    我确实注意到了一些答案中没有的东西。您可以将字节数组中的每个字节强制转换为字符,并将它们放入char数组中。然后字符串是

    1
    new String(cbuf)

    ,其中cbuf是char数组。要进行转换,请通过字符串进行循环,将每个字符转换为要放入字节数组的字节,该字节数组将与第一个相同。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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);
        }

    }


    下面是一些将字节数组转换为字符串的方法。我已经测试过了,它们工作得很好。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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();

    }

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

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

    虽然Base64编码是安全的,可以说"正确答案",但我来到这里寻找一种将Java字节数组转换成Java字符串的方法。也就是说,字节数组的每个成员在对应的字符串中保持完整,不需要额外的空间来进行编码/传输。

    这个描述8位透明编码的答案对我很有帮助。我在数兆字节的二进制数据上使用ISO-8859-1来成功地来回转换(binary<->string),而不需要base64编码所需的膨胀空间,因此对于我的用例YMMV是安全的。

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


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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
    new String(bytes,"UTF-8")

    是正确的,它抛出一个UnsupportedEncodingException,强制您处理一个检查过的异常。您可以使用另一个构造函数作为Java 1.6中的另一个构造函数,将字节数组转换为EDCOX1×1:

    1
    new String(bytes, StandardCharsets.UTF_8)

    这个不会引发任何异常。

    还应使用StandardCharsets.UTF_8进行转换:

    1
    "test".getBytes(StandardCharsets.UTF_8)

    同样,您可以避免处理选中的异常。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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);
    }


    使用ByteArrayInputStreamString中读取字节,并用BufferedReader包装,后者是字符流,而不是字节流,后者将字节数据转换为字符串。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    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();
            }

        }
    }

    输出为:

    Sajal is a good boy


    这里是工作代码。

    1
    2
    3
    4
    5
    6
    7
    8
                // 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);

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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);
    }

    尝试在两个转换中指定一个8位字符集。例如,ISO-8859-1。


    1
    2
    InputStream is = new FileInputStream("/home/kalt/Desktop/SUDIS/READY/ds.bin");
    byte[] bytes = IOUtils.toByteArray(is);

    字符串是字符(16位无符号)的集合。所以如果你要把负数转换成一个字符串,它们会在翻译中丢失。


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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);


        }

    }


    使用base64来解决你的问题。它太容易使用了。http://iharder.sourceforge.net/current/java/base64/