Java中的字节和字符转换

63

如果我将一个字符转换为byte,然后再转回char,那么这个字符会神秘地消失并变成其他的东西。这是怎么可能的呢?

以下是代码:

char a = 'È';       // line 1       
byte b = (byte)a;   // line 2       
char c = (char)b;   // line 3
System.out.println((char)c + " " + (int)c);

直到第二行都很好:

  • 在第一行中,我可以在控制台上打印"a",它会显示"È"。

  • 在第二行中,我可以在控制台中打印"b",它将显示-56,这是因为字节是有符号的,而200是"È"。所以还是很好的。

但第三行出了什么问题?"c"变成了其他内容,程序输出? 65480。那完全不同。

我应该在第三行写什么才能得到正确的结果?


14
一个 byte8位比特(bit)。一个 char16位比特(bit)。明白了吗? - Rohit Jain
@RohitJain 一个字符——我指的是Unicode代码点——可以占用两个字符或四个字节。此外,谁知道这些东西处于什么规范化形式?字符串“È”本身可以由一个或两个代码点组成,具体取决于它是否在规范化形式C或D中。 - tchrist
3
在一般情况下,char类型需要两个字节,而byte类型只需要一个字节,这是一个问题,但在这里,单独来看,这并不重要,因为“È”是一个编码点在256以下的字符,所以可以存储在一个字节中。问题在于,char是无符号的,而byte不是。将char强制转换为byte仅适用于ASCII字符,因此对于像这样编码点在127以上的字符则不适用。 - Lumi
这个回答解决了你的问题吗?Char转byte?(Java) - user12208242
3个回答

85
Java中的字符是Unicode码单元,被视为无符号数字。因此,如果执行c =(char)b,则获得的值为2^16-56或65536-56。
更准确地说,通过扩展转换使用符号扩展将字节转换为带符号整数,其值为0xFFFFFFC8。然后,将其缩小到char时,变成了0xFFC8,这对应于正数65480
来自语言规范: 5.1.4。扩宽和缩小原始转换 首先,通过扩展原始转换(§5.1.2),将字节转换为int,然后通过缩小原始转换(§5.1.3)将结果转换为char。

要达到正确的目的,使用 char c = (char) (b & 0xFF) ,它首先通过使用掩码将 b 的字节值转换为正整数200:清零转换后的前24位: 0xFFFFFFC8 变成十进制中的正数 200


以下是对于 byte、int 和 char 原始类型之间转换发生的直接解释。
如果您想要从字节编码/解码字符,请使用 Charset、CharsetEncoder、CharsetDecoder 或其中一个方便的方法,例如 new String(byte[] bytes, Charset charset) 或 String#toBytes(Charset charset)。您可以从 StandardCharsets 中获取字符集(例如 UTF-8 或 Windows-1252)。

9
实际上,Java中的char并不是Unicode的代码点,而是UTF-16的代码单元。要实际表示任意Unicode“字符”(我指的是实际的代码点),Java中的char是不够用的:你必须使用一个int(有效地给出UTF-32),它可以在遗留的UTF-16表示法中占用两个字符。这就是为什么所有东西都有codePointAt API,而不仅仅是糟糕的旧的charAt API。 - tchrist
2
为什么 char c = (char) (b & 0xFF) 只使用一个字节,当 Java 的 char 应该是两个字节? - Cory
1
@Maarten .. 感谢你的发现。你知道为什么字节首先被扩展为整数,然后再缩小为字符吗?为什么不直接将字节扩展为字符呢? - Rocky Inde
2
@RockyInde 我现在再次查看了这个答案,因为它已经获得了50个赞。答案似乎是正确的,但对于这个评论的回答却不是。这主要是因为在Java中通常将所有东西转换为整数。int确实是Java中的主要类型;在此类计算期间,字节、短整型和字符的计算都会扩展为整数类型。这种转换只是一个基本但奇怪的例子。 - Maarten Bodewes

0
这对我有用: //添加导入语句
import java.nio.charset.Charset;

// 更改

sun.io.ByteToCharConverter.getDefault().getCharacterEncoding() -> Charset.defaultCharset()

瞧!有人在2022年提到了sun.这个包!!! - undefined

-2

new String(byteArray, Charset.defaultCharset())

这将在Java中将字节数组转换为默认字符集。根据您提供的byteArray,它可能会抛出异常。


1
错误。根据文档:“此方法始终使用此字符集的默认替换字符串替换格式不正确的输入和无法映射的字符序列。当需要更多地控制解码过程时,应使用CharsetDecoder类。”因此它并不像你所说的那样抛出异常。 - Maarten Bodewes
并不意味着它是错误的。这意味着如果您需要更多的控制,请使用CharsetDecoder。 - Joe
不,这是错误的,因为你表明它可能会抛出异常,但实际上并没有。 是的,你可以使用CharsetDecoder来获得更多的控制,但这并不能使答案正确。很高兴支持已纠正的答案。 - Maarten Bodewes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接