【问题标题】:Why are the lengths different when converting a byte array to a String and then back to a byte array?为什么将字节数组转换为字符串然后再转换回字节数组时长度不同?
【发布时间】:2011-06-27 23:37:26
【问题描述】:

我有以下 Java 代码:

byte[] signatureBytes = getSignature();

String signatureString = new String(signatureBytes, "UTF8");
byte[] signatureStringBytes = signatureString.getBytes("UTF8");

System.out.println(signatureBytes.length == signatureStringBytes.length); // prints false

问:我可能理解错了,但我认为new String(byte[] bytes, String charset)String.getBytes(charset) 是逆运算?

问:作为后续,将 byte[] 数组作为字符串传输的安全方法是什么?

【问题讨论】:

    标签: java encoding utf-8 decoding


    【解决方案1】:

    并非每个byte[] 都是有效的 UTF-8。默认情况下,无效序列会被固定字符替换,我认为这就是长度变化的原因。

    试试 Latin-1,它不应该发生,因为它是一个简单的编码,每个 byte[] 都是有意义的。

    Windows-1252 也不应该发生这种情况。那里有未定义的序列(实际上是未定义的字节),但所有字符都被编码在一个字节中。新的byte[] 可能与原来的不同,但它们的长度必须相同。

    【讨论】:

    • 那行得通。那么将 byte[] 数组作为 String 传输的安全方法是什么?
    • org.apache.commons.codec.binary.Base64 永远是你最好的朋友,因为它可以将任意数据作为字符串传递:)
    • 对,Base64 是标准且最佳的解决方案。只要您继续使用 Java,Latin-1 也可以很好地工作。
    【解决方案2】:

    我可能误解了这一点,但我认为 new String(byte[] bytes, String charset) 和 String.getBytes(charset) 是逆运算?

    不一定。

    如果输入字节数组包含无效的 UTF-8 序列,则初始转换可能会将它们变成(例如)问号。然后第二个操作将这些转换为 UTF-8 编码的 '?' 字符 .... 与原始表示不同。


    确实,Unicode 中的某些字符有多种表示形式;例如重音字符可以是单个代码点,也可以是基本字符代码点和重音代码点。但是,在字节数组(包含有效的 UTF-8)和字符串之间来回转换应该保留代码点序列。它不执行任何“标准化”。


    那么,将 byte[] 数组作为 String 传输的安全方法是什么?

    最安全的替代方法是对字节数组进行 base64 编码。这样做还有一个额外的好处,即字符串中的字符在转换为任何可以表示拉丁字母和数字的字符集/编码后仍然有效。

    另一种选择是使用 Latin-1 而不是 UTF-8。然而:

    • 如果数据被(例如)错误地解释为 UTF-8,则存在损坏风险。
    • 如果“字符串”随后嵌入到 XML 中,则此方法是不合法的。许多控制字符在 XML 字符集之外,不能在 XML 文档中使用,甚至编码为字符实体。

    【讨论】:

    • 谢谢!那么,将 byte[] 数组作为 String 传输的安全方法是什么?
    【解决方案3】:

    我想到了两种可能性。

    首先是您的签名不是完全有效的 UTF8。您不能只获取任意二进制数据并将其“字符串化”。并非每一组位都定义了一个合法字符。 String 构造函数将为二进制数据插入一些默认替换内容,这些内容在 UTF8 中实际上并不“意味着”任何内容。这不是一个可逆的过程。如果要“字符串”一些任意二进制数据,则需要使用已建立的方法,我建议 org.apache.commons.codec.binary.Base64

    还有一些字符具有不止一种表示形式。例如,带有重音符号的事物可以被编码为重音字符,也可以编码为要组合的字符加上重音符号。在编码之间来回移动时,不能保证这是一个可逆的过程。

    【讨论】:

      【解决方案4】:

      我想将数据作为字符串存储到我的 JSP 页面中,然后将字符串作为参数发送到服务器端并转换为byte[]。这对我有用:

      将字节[] 转换为字符串

      String byteToString = Base64.getEncoder().encodeToString(byteContent);
      

      从字符串转换为字节[]

      byte[] stringToByte = Base64.getDecoder().decode(stringContent);
      

      这将返回具有相同长度的确切 byte[]

      【讨论】:

        猜你喜欢
        • 2011-12-18
        • 1970-01-01
        • 2017-04-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-04
        相关资源
        最近更新 更多