繁体   English   中英

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

[英]How to convert byte array to string and vice versa?

我必须在 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);

我陷入了这个问题。

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

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

您可以使用一堆编码,查看Oracle javadocs中支持的编码。

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

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

根本问题是(我认为)您在不知不觉中使用了一个字符集:

 bytes != encode(decode(bytes))

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

解决方案是:

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

对于 Java,最常见的字符集在java.nio.charset.StandardCharsets中。 如果您正在对可以包含任何 Unicode 字符值的字符串进行编码,则建议使用 UTF-8 编码( UTF_8 )

如果您想在 Java 中进行 1:1 映射,那么您可以使用ISO 拉丁字母 No. 1 - 通常称为“Latin 1”或简称为“Latin”( ISO_8859_1 )。 请注意,Java 中的 Latin-1 是 Latin-1 的 IANA 版本,它将字符分配给所有可能的 256 个值,包括控制块 C0 和 C1 这些是不可打印的:您不会在任何输出中看到它们。

从 Java 8 开始,Java 包含用于 Base64 编码/解码的java.util.Base64 对于 URL 安全编码,您可能希望使用Base64.getUrlEncoder而不是标准编码器 自 Android Oreo (8),API 级别 26 以来,此类也存在于 Android 中。

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

String s = new String(bytes);

结果字符串的字节因您使用的字符集而异。 当您调用 String# 时,new String(bytes) 和 new String(bytes, Charset.forName("utf-8")) 和 new String(bytes, Charset.forName("utf-16")) 都会有不同的字节数组# getBytes() (取决于默认字符集)

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

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

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

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

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

PDF File >> Base64.encodeBase64(byte[]) >> String >> Send in request(POST) >> receive String >> Base64.decodeBase64(byte[]) >> create binary

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

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();
        }

虽然

new String(bytes, "UTF-8")

是正确的,它会引发UnsupportedEncodingException ,它会强制您处理已检查的异常。 您可以使用自 Java 1.6 以来的另一个构造函数作为替代方法,将字节数组转换为String

new String(bytes, StandardCharsets.UTF_8)

这个不会抛出任何异常。

转换回来也应该使用StandardCharsets.UTF_8完成:

"test".getBytes(StandardCharsets.UTF_8)

您再次避免处理已检查的异常。

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;
}

这对我来说很好:

String cd = "Holding some value";

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

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

从 byte[] 转换为字符串:

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

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

new String(cbuf)
其中 cbuf 是字符数组。 要转换回来,循环遍历将每个字符转换为字节的字符串以放入一个字节数组,这个字节数组将与第一个相同。

 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); } }

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

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

下面是一些将字节数组转换为字符串的方法。 我已经对它们进行了测试,它们运行良好。

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();

}

虽然 base64 编码是安全的并且有人可能会争论“正确的答案”,但我来到这里是为了寻找一种将 Java 字节数组原样转换为 Java 字符串/从 Java 字符串转换为/从 Java 字符串转换的方法。 也就是说,字节数组的每个成员在其对应的字符串中保持不变,编码/传输不需要额外的空间。

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

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

  byte[] bytes = "Techie Delight".getBytes();
        // System.out.println(Arrays.toString(bytes));
 
        // Create a string from the byte array without specifying
        // character encoding
        String string = new String(bytes);
        System.out.println(string);

以下是示例代码安全地将字节数组转换为字符串并将字符串转换为字节数组。

 byte bytesArray[] = { 1, -2, 4, -5, 10};
 String encoded = java.util.Base64.getEncoder().encodeToString(bytesArray);
 byte[] decoded = java.util.Base64.getDecoder().decode(encoded);
 System.out.println("input: "+Arrays.toString(bytesArray));
 System.out.println("encoded: "+encoded);
 System.out.println("decoded: "+Arrays.toString(decoded));

输出:

input: [1, -2, 4, -5, 10]
encoded: Af4E+wo=
decoded: [1, -2, 4, -5, 10]
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);
}

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

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();
}

这个对我有用,直到 android Q:

您可以使用以下方法将 o 十六进制字符串转换为字符串

    public static String hexToString(String hex) {
    StringBuilder sb = new StringBuilder();
    char[] hexData = hex.toCharArray();
    for (int count = 0; count < hexData.length - 1; count += 2) {
        int firstDigit = Character.digit(hexData[count], 16);
        int lastDigit = Character.digit(hexData[count + 1], 16);
        int decimal = firstDigit * 16 + lastDigit;
        sb.append((char)decimal);
    }
    return sb.toString();
}

使用以下内容将字节数组转换为十六进制字符串

    public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

在 Android 上使用 Kotlin 我发现为此目的创建一些简单的扩展函数很方便。 基于Base64编码/解码能够通过JSON、XML等的解决方案:

import android.util.Base64

fun ByteArray.encodeToString() = String(Base64.encode(this, Base64.NO_WRAP), Charsets.UTF_8)

fun String.decodeToBytes(): ByteArray = Base64.decode(toByteArray(Charsets.UTF_8), Base64.NO_WRAP)

所以你可以使用它

val byteArray = byteArrayOf(0, 1, 2, -1, -2, -3)
val string = byteArray.encodeToString()
val restoredArray = string.decodeToBytes()

这里是工作代码。

            // 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 循环进行转换:

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);
}
byte[] image = {...};
String imageString = Base64.encodeToString(image, Base64.NO_WRAP);

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

使用ByteArrayInputStreamString中读取字节,并使用 BufferedReader 进行包装, BufferedReader是 Char Stream 而不是将字节数据转换为 String 的 Byte Stream。

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();
        }

    }
}

输出是:

萨哈尔是个好孩子

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

您可以执行以下操作将字节数组转换为字符串,然后将该字符串转换为字节数组:

// 1. convert byte array to string and then string to byte array

    // convert byte array to string
    byte[] by_original = {0, 1, -2, 3, -4, -5, 6};
    String str1 = Arrays.toString(by_original);
    System.out.println(str1); // output: [0, 1, -2, 3, -4, -5, 6]

    // convert string to byte array
    String newString = str1.substring(1, str1.length()-1);
    String[] stringArray = newString.split(", ");
    byte[] by_new = new byte[stringArray.length];
    for(int i=0; i<stringArray.length; i++) {
        by_new[i] = (byte) Integer.parseInt(stringArray[i]);
    }
    System.out.println(Arrays.toString(by_new)); // output: [0, 1, -2, 3, -4, -5, 6]

但是要将字符串转换为字节数组,然后将该字节数组转换为字符串,可以使用以下方法:

// 2. convert string to byte array and then byte array to string

    // convert string to byte array
    String str2 = "[0, 1, -2, 3, -4, -5, 6]";
    byte[] byteStr2 = str2.getBytes(StandardCharsets.UTF_8);
    // Now byteStr2 is [91, 48, 44, 32, 49, 44, 32, 45, 50, 44, 32, 51, 44, 32, 45, 52, 44, 32, 45, 53, 44, 32, 54, 93]

    // convert byte array to string
    System.out.println(new String(byteStr2, StandardCharsets.UTF_8)); // output: [0, 1, -2, 3, -4, -5, 6]

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

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/

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM