【问题标题】:Byte and char conversion in JavaJava中的字节和字符转换
【发布时间】:2013-07-28 13:41:12
【问题描述】:

如果我将一个字符转换为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);

直到第 2 行一切正常:

  • 在第 1 行中,我可以在控制台中打印“a”,它会显示“È”。

  • 在第 2 行中,我可以在控制台中打印“b”,它会显示 -56,即 200,因为字节已签名。 200 是“È”。所以还是没问题的。

但是第 3 行出了什么问题? “c”变成别的东西,程序打印? 65480。那是完全不同的东西。

为了得到正确的结果,我应该在第 3 行写什么?

【问题讨论】:

  • A byte8 bitchar16 bit。明白了吗?
  • @RohitJain 一个字符——我的意思是一个 Unicode 代码点——可以占用两个字符或四个字节。此外,谁知道事物的规范化形式是什么?字符串"È" 本身可以包含一个或两个代码点,具体取决于它是分别处于规范化形式 C 还是 D。
  • char 的两个字节与 byte 的一个字节在一般情况下是一个问题,但在这里,就其本身而言,这并不重要,因为 'È' 是低于 256 的代码点,所以可以存储在一个字节中。这里的问题是char 是未签名的,而byte 不是。将 char 转换为 byte 仅适用于 ASCII,因此不适用于 127 以上的代码点,就像这里一样。
  • 这能回答你的问题吗? Char into byte? (Java)

标签: java encoding unicode utf-16


【解决方案1】:

new String(byteArray, Charset.defaultCharset())

这会将字节数组转换为 java 中的默认字符集。它可能会抛出异常,具体取决于您为 byteArray 提供的内容。

【讨论】:

  • 错了。来自文档:“此方法始终用此字符集的默认替换字符串替换格式错误的输入和不可映射的字符序列。当需要对解码过程进行更多控制时,应使用 CharsetDecoder 类。”所以它不会像你建议的那样抛出异常。
  • 不代表错了。这意味着如果您需要更多控制,请使用 CharsetDecoder
  • 不,这是错误的,因为您指出它可能会抛出异常而不会。是的,您可以使用CharsetDecoder 进行更多控制,但这并不能使答案正确。很高兴为更正的答案投票。
【解决方案2】:

Java 中的字符是一个 Unicode 代码单元,它被视为无符号数字。所以如果你执行c = (char)b,你得到的值是 2^16 - 56 或 65536 - 56。

或者更准确地说,首先在扩展转换中使用符号扩展将字节转换为值为0xFFFFFFC8 的有符号整数。然后在转换为 char 时将其缩小到 0xFFC8,这将转换为正数 65480

来自语言规范:

5.1.4. Widening and Narrowing Primitive Conversion

首先,通过加宽原始转换(第 5.1.2 节)将字节转换为 int, 然后通过缩小原始转换将生成的 int 转换为 char (§5.1.3)。


要获得正确的点,请使用char c = (char) (b & 0xFF),它首先使用掩码将b的字节值转换为正整数200,转换后将前24位归零:0xFFFFFFC8变为0x000000C8或以小数表示的正数200


以上是对byteintchar 原始类型之间转换过程中发生的情况的直接解释。

如果您想对字节中的字符进行编码/解码,请使用CharsetCharsetEncoderCharsetDecodernew String(byte[] bytes, Charset charset)String#toBytes(Charset charset) 等便利方法之一。您可以从StandardCharsets 获取字符集(例如 UTF-8 或 Windows-1252)。

【讨论】:

  • 实际上,Java char 不是 Unicode code point。它是一个 UTF-16 代码单元。要实际表示任意 Unicode“字符”(我指的是实际代码点),Java char 还不够好:您必须使用 int(有效地为您提供 UTF-32),它可以占用到传统 UTF-16 表示法中的两个字符。这就是为什么所有东西都有一个 codePointAt API,而不仅仅是糟糕的旧式 charAt API。
  • 为什么 char c = (char) (b & 0xFF) 只使用一个字节,而 Java 字符应该是两个字节?
  • @Maarten .. 谢谢你的好收获。你知道为什么字节先加宽为整数,然后缩小为字符的原因吗?为什么不直接将一个字节加宽为一个字符呢?
  • @RockyInde 我再次查看了这个答案,现在它得到了 50 个赞。答案似乎是正确的,但对此评论的答案却没有。主要是因为 everything 在 Java 中一般都会转换成整数。 int 确实是 Java 中的主要类型;在这样的计算过程中,字节、短裤和字符的计算都被扩展为整数类型。这种转换只是一个基本但奇怪的例子。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多