【问题标题】:How to convert byte array to string and vice versa?如何将字节数组转换为字符串,反之亦然?
【发布时间】:2010-12-04 21:31:23
【问题描述】:

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

我陷入了这个问题。

【问题讨论】:

  • 你为什么要首先将任意二进制数据转换为字符串?除了答案已经提到的所有字符集问题之外,还有一个事实是,如果你这样做,你就是在滥用字符串。将byte[] 用于二进制数据和String 用于文本有什么问题?
  • @Joachim - 有时您有外部工具可以执行诸如存储字符串之类的操作。在这种情况下,您希望能够将字节数组转换为(以某种方式编码的)字符串。

标签: java


【解决方案1】:

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

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

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

【讨论】:

  • @MauricePerry 你能解释一下为什么它不适用于UTF-8 吗?
  • @UnKnown 因为 UTF-8 将某些字符编码为 2 或 3 字节字符串。并非每个字节数组都是有效的 UTF-8 编码字符串。 ISO-8859-1 会是更好的选择:这里每个字符都被编码为一个字节。
  • 这可能有效,但您应该不惜一切代价避免使用 String 构造函数。
  • 将一个字节映射到一个字符(使用 8859-1)并且没有异常处理(使用 nio.charset):String str = new String(bytes, java.nio.charset.StandardCharsets.ISO_8859_1);
  • 从 Java 1.7 开始,你可以使用 new String(bytes, StandardCharsets.UTF_8)
【解决方案2】:

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

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

【讨论】:

【解决方案3】:

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

 bytes != encode(decode(bytes))

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

解决办法是:

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

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

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

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

【讨论】:

    【解决方案4】:

    我们只需要用数组构造一个新的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()(取决于默认字符集)

    【讨论】:

    • 没有。结果字符串的字节因您使用的字符集而异。当您调用 String#getBytes() 时,new String(bytes)new String(bytes, Charset.forName("utf-8"))new String(bytes, Charset.forName("utf-16")) 将具有不同的字节数组(取决于默认字符集)
    • 误导。当以不同方式解码bytes 时,生成的Stringchars(以及由此显示的文本)会有所不同。使用默认编码转换回字节(使用String#getBytes("charset") 另行指定)必然会有所不同,因为它转换不同的输入。字符串不存储它们的来源byte[]chars 没有编码,String 不存储它。
    【解决方案5】:

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

    【讨论】:

      【解决方案6】:

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

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

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

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

      PDF 文件 >> Base64.encodeBase64(byte[]) >> String >> 发送请求(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();
              }
      

      【讨论】:

      • 这是否使用外部库,例如 Apache 编解码器?如果是这样请在答案中注明
      【解决方案7】:
      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;
      }
      

      【讨论】:

      • 这没有回答问题。
      • 不回答问题但很有用+1
      【解决方案8】:

      虽然

      new String(bytes, "UTF-8")
      

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

      new String(bytes, StandardCharsets.UTF_8)
      

      这个没有抛出任何异常。

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

      "test".getBytes(StandardCharsets.UTF_8)
      

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

      【讨论】:

      • 这个说法不错,但是使用new String本身就不好,所以并不能解决根本问题。
      【解决方案9】:

      这对我来说很好用:

      String cd = "Holding some value";
      

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

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

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

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

      【讨论】:

      • 永远不要使用sun. 内部类。自 1.0 以来的每个 Java 教程都会警告它,新的模块化系统甚至默认直接禁止它。
      【解决方案10】:

      我确实注意到了一些答案中没有的东西。您可以将字节数组中的每个字节转换为字符,并将它们放入 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);
          }
      
      }
      
      

      【讨论】:

      • 除了已经很可怕的String之外,为什么还要添加另一个块内存?
      【解决方案11】:

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

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

      【讨论】:

      • 在较新版本的 Java 中,java.util 中包含一个 Base64 类,在最新版本中,它甚至可以直接处理十六进制(喘气!)。
      【解决方案12】:

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

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

      【讨论】:

        【解决方案13】:

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

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

        This was also helpful 解释何时/是否应该进行实验。

        【讨论】:

        • 为什么要将 TB 的数据存储在字符串中,首先二进制有什么问题?什么搞砸的协议或 API 需要将数据作为字符串?
        • @MaartenBodewes,不是单个字符串缓冲区中的 TB,更像是随时间推移的数据流。这篇文章已经有几年了,但我认为这是为了满足使用 Apache Ignite 的要求。不是我一般推荐的东西,但如果你需要它很有用。
        【解决方案14】:
        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 因为不需要从 unspecified 字节值到字符串进行存储,所以最后你只需要它通信 或 显示。通常,很难沟通,例如任何类型的基于文本的协议中的退格或其他控制字符(如果不是未映射的字符)。只有知道文本是否可以某种编码格式(UTF-8、Latin 1 等)打印时,您才会进行转换。
        【解决方案15】:

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

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

        【讨论】:

        • 这是一个无法解释的答案,说明你做了什么以及为什么它适合。如果有什么特别之处,请提供解码器。
        【解决方案16】:

        这个对我来说适用于 android Q:

        可以使用以下方法将o hex字符串转换为字符串

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

        【讨论】:

        • 好的,十六进制有效,但是您忘记提供hexArray,并且由于某种原因您的方法不对称(十六进制字符串 -> 字符串,后跟字节 [] -> 十六进制字符串)。
        【解决方案17】:

        这里是工作代码。

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

        【讨论】:

          【解决方案18】:

          您可以使用简单的 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);
          }
          

          【讨论】:

            【解决方案19】:
            byte[] image = {...};
            String imageString = Base64.encodeToString(image, Base64.NO_WRAP);
            

            【讨论】:

              【解决方案20】:

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

              // 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]
              

              【讨论】:

              • 我投了反对票。该问题未指定字节数组中的内容。当然,您可以将字节数组编码为字符串并使用您的代码对其进行解码,但是对 base64 编码的一次调用将创建更密集和(更重要的是)标准化的编码。所以 1. 它并没有真正解决这个问题,并且 2. 如果它会解决这个问题,那么编码是次优的。它基本上也是一个“仅代码”的答案,因为它没有描述编码格式或为什么这会有益。显式方法也不错。
              • @MaartenBodewes 在问题中提到了字节数组中的内容。我已经回答了同样的问题。请检查问题兄弟。
              【解决方案21】:

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

              【讨论】:

                【解决方案22】:

                使用ByteArrayInputStreamString 读取字节,并用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();
                        }
                
                    }
                }
                

                输出是:

                萨哈尔是个好孩子

                【讨论】:

                  【解决方案23】:

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

                  【讨论】:

                  • -1:这是不正确的。虽然 'byte' 在 Java 中是有符号类型,但它们被执行字符集编码和解码的库代码视为无符号类型。
                  • 一个很好的例子,为什么在语言中拥有一个无符号的 8 位数据类型确实是一个好主意。避免不必要的混淆;^)
                  • 小心假设 Java char 为 16 位,因为 Java 的 UTF-16,它们可以扩展到 32 位
                  • @Toad 实际上是的,一些 Unicode 字符在存储为 UTF-16 时占用两个代码点,即 32 位。在 UTF-8 中也会发生同样的情况:某些字符使用两个/三个/四个代码点,即 16/24/32 位。事实上,这正是 UTF 的意义所在(即 UTF != Unicode)。
                  • @Toad 你会得到第一个代理 - 即只有角色的前“一半”。查看String.charAt 方法和Character 类的文档。
                  【解决方案24】:
                  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);
                  
                  
                      }
                  
                  }
                  

                  【讨论】:

                  • 将字符集编码作为参数传递给 getBytes
                  • 除了代码之外,您可能需要考虑用解释来充实这个答案。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多