杰瑞科技汇

string与byte数组如何互转?

核心概念:字符编码

在开始转换之前,必须理解字符编码

string与byte数组如何互转?-图1
(图片来源网络,侵删)
  • String: 在 Java 中,String 内部使用 UTF-16 编码来表示字符,这意味着一个 char 类型通常占用 2 个字节。
  • byte[]: 这是一个原始字节的数组,它本身不包含任何关于字符或编码的信息,它只是数据的二进制表示。

当你在 Stringbyte[] 之间转换时,你必须明确指定使用哪种编码规则(如 UTF-8, ISO-8859-1, GBK 等)。如果编码不一致,就会出现乱码(mojibake)

为什么 UTF-8 是最佳选择? UTF-8 是目前互联网上最广泛使用的编码,它具有以下优点:

  1. 兼容 ASCII:对于英文字符,UTF-8 的编码与 ASCII 完全相同。
  2. 变长编码:它可以使用 1 到 4 个字节来表示一个字符,非常节省空间。
  3. 无 BOM:通常不需要字节顺序标记。
  4. 通用性强:可以表示世界上几乎所有的字符。

Stringbyte[]

使用 String 类的 getBytes() 方法。

显式指定编码(推荐)

这是最安全、最推荐的方式,你明确告诉 JVM 使用哪种编码来将字符串转换为字节数组。

string与byte数组如何互转?-图2
(图片来源网络,侵删)
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
public class StringToByteArray {
    public static void main(String[] args) {
        String str = "Hello, 世界!";
        // --- 推荐方式:使用 StandardCharsets (Java 7+) ---
        // StandardCharsets 是一个枚举类,提供了几种常用编码的常量,避免了拼写错误。
        try {
            byte[] utf8Bytes = str.getBytes(StandardCharsets.UTF_8);
            System.out.println("使用 UTF-8 编码的字节数组: " + java.util.Arrays.toString(utf8Bytes));
            // 也可以使用字符串字面量
            byte[] gbkBytes = str.getBytes("GBK");
            System.out.println("使用 GBK 编码的字节数组: " + java.util.Arrays.toString(gbkBytes));
        } catch (UnsupportedEncodingException e) {
            // 在使用 StandardCharsets 时,这个异常通常不会发生
            e.printStackTrace();
        }
    }
}

输出分析:

  • Hello, 世界!UTF-8 编码字节数组:
    • H -> 72
    • e -> 101
    • l -> 108
    • l -> 108
    • o -> 111
    • -> 44
    • ` ` -> 32
    • -> -28, -72, -83 (UTF-8 中,中文字符通常占3个字节)
    • -> -27, -101, -67
    • -> 33
  • GBK 编码中,中文字符通常占2个字节, 的字节表示会与 UTF-8 不同。

使用平台默认编码(不推荐)

如果你不指定编码,getBytes() 会使用 JVM 运行所在平台的默认字符编码。

String str = "Hello, 世界!";
// --- 不推荐方式:使用平台默认编码 ---
// 这种代码在不同操作系统(如 Windows vs. Linux)或不同配置的机器上,
// 可能会产生不同的结果,导致乱码问题。
byte[] defaultBytes = str.getBytes(); 
System.out.println("使用默认编码的字节数组: " + java.util.Arrays.toString(defaultBytes));

为什么不推荐? 因为代码的可预测性差,你的开发环境可能是 UTF-8,但生产服务器可能是 GBK,这会导致难以排查的乱码问题。


byte[]String

使用 String 的构造函数 String(byte[] bytes, String charsetName)

string与byte数组如何互转?-图3
(图片来源网络,侵删)

显式指定编码(推荐)

这是唯一能正确还原出原始字符串的方式。

import java.nio.charset.StandardCharsets;
public class ByteArrayToString {
    public static void main(String[] args) {
        byte[] utf8Bytes = {(byte) 72, (byte) 101, (byte) 108, (byte) 108, (byte) 111, (byte) 44, (byte) 32, (byte) -28, (byte) -72, (byte) -83, (byte) -27, (byte) -101, (byte) -67, (byte) 33};
        // --- 推荐方式:使用 StandardCharsets ---
        // 必须使用与当初编码时完全相同的编码,才能正确还原字符串。
        String strFromUtf8 = new String(utf8Bytes, StandardCharsets.UTF_8);
        System.out.println("从 UTF-8 字节数组还原的字符串: " + strFromUtf8);
        // --- 错误示例:编码不一致 ---
        // 如果用错误的编码来解码,就会出现乱码
        String wrongStr = new String(utf8Bytes, StandardCharsets.ISO_8859_1);
        System.out.println("用 ISO-8859-1 错误解码后的字符串: " + wrongStr); // 输出类似 "Hello, 世畎!"
    }
}

使用平台默认编码(不推荐)

getBytes() 类似,如果不指定编码,构造函数会使用平台默认编码。

byte[] someBytes = {(byte) 72, (byte) 101, (byte) 108, (byte) 108, (byte) 111};
// --- 不推荐方式:使用平台默认编码 ---
String strFromDefault = new String(someBytes);
System.out.println("使用默认编码还原的字符串: " + strFromDefault);

同样,这种方式依赖于环境,是不可靠的。


特殊情况:ISO-8859-1 编码

ISO-8859-1(也称为 Latin-1)是一种非常特殊的编码,它在 Stringbyte[] 转换中有独特的用途。

  • 特点ISO-8859-1 的编码范围是 0-255,它对字节的值不做任何修改,也就是说,一个字节 0x41ISO-8859-1 中就是字符 'A',反过来,字符 'A' 转换成 ISO-8859-1 的字节就是 0x41
  • 用途:这种“不修改”的特性使得 ISO-8859-1 成为一个完美的“中间媒介”“无损通道”

场景: 当你不确定一个 byte[] 的原始编码,但又想安全地将其转换成 String(之后再转换回 byte[])而不丢失任何字节时,可以使用 ISO-8859-1

public class Iso8859_1Example {
    public static void main(String[] args) {
        // 假设我们有一个来自未知来源的 byte[]
        byte[] unknownSourceBytes = {(byte) 0xE4, (byte) 0xBD, (byte) 0xA0, (byte) 0xE5, (byte) 0xA5, (byte) 0xBD}; // "你好" 的 GBK 编码
        // 1. 使用 ISO-8859-1 将 byte[] 转为 String
        //    这个 String 里面的字符对应着原始的字节,但内容是乱码的。
        String intermediateStr = new String(unknownSourceBytes, StandardCharsets.ISO_8859_1);
        System.out.println("中间 String (内容乱码): " + intermediateStr); // 输出类似 "ä½ å¥½"
        // 2. 将这个 String 再用 ISO-8859-1 转回 byte[]
        //    因为 ISO-8859-1 是无损的,所以得到的 byte[] 和原始的完全一样!
        byte[] recoveredBytes = intermediateStr.getBytes(StandardCharsets.ISO_8859_1);
        System.out.println("原始字节数组: " + java.util.Arrays.toString(unknownSourceBytes));
        System.out.println("恢复后的字节数组: " + java.util.Arrays.toString(recoveredBytes));
        System.out.println("是否相等: " + java.util.Arrays.equals(unknownSourceBytes, recoveredBytes)); // 输出 true
    }
}

注意:这种技术只保证了字节数据的完整性,不保证字符的可读性,当你需要处理一个 byte[],但又不想因为编码问题丢失数据时,这是一个非常实用的技巧。


总结与最佳实践

转换方向 推荐方法 代码示例 关键点
String -> byte[] 显式指定编码 byte[] bytes = myString.getBytes(StandardCharsets.UTF_8); 必须指定编码,推荐 UTF-8
byte[] -> String 显式指定编码 String str = new String(myBytes, StandardCharsets.UTF_8); 必须使用与编码时相同的编码,否则乱码。
特殊情况 使用 ISO-8859-1 作为无损通道 new String(bytes, "ISO-8859-1")str.getBytes("ISO-8859-1") 用于在不丢失字节的情况下安全地“包装”byte[]

核心原则:

  1. 永远不要依赖平台默认编码。
  2. Stringbyte[] 之间转换时,始终显式地、明确地指定字符编码。
  3. 优先使用 StandardCharsets.UTF_8,除非你有特殊需求(如处理旧系统或特定地区的文本)。
  4. 当需要无损地保存和恢复字节数组时,ISO-8859-1 是一个可靠的工具。
分享:
扫描分享到社交APP
上一篇
下一篇