为了账号安全,请及时绑定邮箱和手机立即绑定

Java 8 UTF-16 不是默认字符集,而是 UTF-8

Java 8 UTF-16 不是默认字符集,而是 UTF-8

holdtom 2022-07-27 20:58:09
我一直在用 Java8、Java 11 中的 String 进行一些编码,但这个问题是基于 Java 8 的。我有这个小片段。final char e = (char)200;//È我只是认为 0.255[Ascii+extended Ascii] 之间的字符总是适合一个字节,只是因为 2^8=256 但这似乎不是真的我在网站上尝试过https://mothereff.in/byte-计数器并声明该字符占用 2 个字节,有人可以向我解释一下。许多帖子中的另一个问题指出 Java 是 UTF-16,但在我运行 Windows 7 的机器上,在此代码段中返回 UTF-8。String csn = Charset.defaultCharset().name();这个平台依赖吗?其他问题我试过这个片段。final List<Charset>charsets = Arrays.asList(StandardCharsets.ISO_8859_1,StandardCharsets.US_ASCII,StandardCharsets.UTF_16,StandardCharsets.UTF_8);    charsets.forEach(a->print(a,"È"));    System.out.println("getBytes");    System.out.println(Arrays.toString("È".getBytes()));    charsets.forEach(a->System.out.println(a+" "+Arrays.toString(sb.toString().getBytes(a))));private void print(final Charset set,final CharSequence sb){    byte[] array = new byte[4];                  set.newEncoder()            .encode(CharBuffer.wrap(sb), ByteBuffer.wrap(array), true);    final String buildedString = new String(array,set);    System.out.println(set+" "+Arrays.toString(array)+" "+buildedString+"<<>>"+buildedString.length());    }和版画run:ISO-8859-1 [-56, 0, 0, 0] È//PERFECT USING 1 BYTE WHICH IS -56US-ASCII [0, 0, 0, 0] //DONT GET IT SEE THIS ITEM FOR LATERUTF-16 [-2, -1, 0, -56] È<<>>1 //WHAT IS -2,-1 BYTE USED FOR? I HAVE TRY WITH OTHER EXAMPLES AND THEY ALWAYS APPEAR AM I LOSING TWO BYTES HERE??UTF-8 [-61, -120, 0, 0] 2 È //SEEMS TO MY CHARACTER NEEDS TWO BYTES?? I THOUGHT THAT CODE=200 WOULD REQUIRE ONLY ONEgetBytes我试过System.out.println(new String(new byte[]{-1,-2},"UTF-16"));//SIMPLE "" I AM WASTING THIS 2 BYTES??在简历中。为什么 UTF-16 总是有两个前导字节被浪费了?新字节[]{-1,-2}为什么当我对“È”进行编码时,我在 ASCCI 字符集中没有得到任何字节,但是当我执行 È.getBytes(StandardCharsets.US_ASCII) 时却得到 {63}?Java 使用 UTF-16 但在我的情况下 UTF-8 取决于平台?对不起,如果这篇文章令人困惑环境Windows 7 64 Bits Netbeans 8.2 with Java 1.8.0_121
查看完整描述

2 回答

?
当年话下

TA贡献1890条经验 获得超9个赞

第一个问题

对于您的第一个问题:这些字节是 BOM 代码,它们指定多字节编码(例如 UTF-16)的字节顺序(无论是最重要的还是最重要的)。

第二个问题

每个 ASCII 字符都可以在 UTF-8 中编码为单个字节。但是 ASCII 不是 8 位编码,它为每个字符使用 7 位。事实上,所有代码点 >= 128 的 Unicode 字符至少需要两个字节。(原因是您需要一种方法来区分 200 和第一个字节恰好是 200 的多字节代码点。UTF-8 通过使用字节 >= 128 来表示多字节代码点来解决这个问题。)

'È' 不是 ASCII 字符,因此不能用 ASCII 表示。这解释了第二个输出:63 是字符“?”的 ASCII。实际上,该getBytes(Charset)方法的 Javadoc 指定不可映射的输入映射到“默认替换字节数组”,在本例中为“?”。另一方面,要获取第一个 ASCII 字节数组,您可以CharsetEncoder直接使用,这是一个更底层的 API,不会执行此类自动替换。(当您检查该encode方法的结果时,您会发现它返回了一个CoderResult表示错误的实例。)

第三个问题

Java 8String内部使用 UTF-16,但在与其他软件通信时,可能会出现不同的编码,例如 UTF-8。该Charset.defaultCharset()方法返回虚拟机的默认字符集,它取决于操作系统的语言环境和字符集,而不是 Java 字符串内部使用的编码。


查看完整回答
反对 回复 2022-07-27
?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

让我们备份一下……

Java 的文本数据类型使用 Unicode 字符集的 UTF-16 字符编码。(和 VB4/5/6/A/Script、JavaScript、.NET、...一样。)您可以在使用字符串 API 执行的各种操作中看到这一点:索引、长度、...。

库支持使用各种编码在文本数据类型和字节数组之间进行转换。其中一些被归类为“扩展 ASCII”,但说明这是一个非常差的替代命名实际使用的字符编码的替代品。

一些操作系统允许用户指定默认字符编码。(不过,大多数用户不知道也不关心。)Java 试图接受这一点。只有当程序理解用户的输入是字符编码或输出应该是时,它才有用。本世纪,处理文本文件的用户更喜欢使用特定的编码,在系统之间保持不变地进行通信,不喜欢有损转换,因此这个概念没有任何用处。从程序的角度来看,它永远不是你想要的,除非它正是你想要的。

如果转换有损,您可以选择替换字符(例如“?”)、忽略它或抛出异常。

根据编码的定义,字符编码是字符集的代码点(整数)与一个或多个代码单元之间的映射。代码单元是固定大小的,代码点所需的代码单元数量可能因代码点而异。

在库中,拥有一个代码单元数组通常没有用,因此它们需要进一步转换为字节数组/从字节数组转换。byte值的范围确实从 -128 到 127,但是,这是 Java 解释为二进制补码 8 位整数。由于字节被理解为编码文本,因此将根据字符编码规则解释这些值。

由于某些 Unicode 编码的代码单元长度超过一个字节,因此字节顺序变得很重要。因此,在字节数组级别,有 UTF-16 Big Endian 和 UTF-16 Little Endian。在传输文本文件或流时,您将发送字节并共享编码知识。这个“元数据”是理解所必需的。因此,例如 UTF-16BE 或 UTF-16LE。为了使这更容易一点,Unicode 允许文件或流的一些元数据开头来指示字节顺序。它被称为字节顺序标记(BOM)。因此,外部元数据可以共享编码(例如,UTF-16),而内部元数据则共享字节顺序。即使字节顺序不相关,Unicode 也允许存在 BOM,例如 UTF-8。所以,

1) 您在一些 Unicode 编码输出中看到了 BOM。

2) È 不在 ASCII 字符集中。在这种情况下会发生什么?我经常喜欢一个例外。

3)您在测试时为您的帐户使用的系统可能已将 UTF-8 作为默认字符编码,这对您想要的方式以及在该系统上对文本文件进行编码是否重要?


查看完整回答
反对 回复 2022-07-27
  • 2 回答
  • 0 关注
  • 125 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信