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

问题描述 投票:1回答:2

我在Java8,Java 11中使用String进行了一些编码,但这个问题基于Java 8.我有这个小片段。

final char e = (char)200;//È

我只是认为0.255 [Ascii +扩展的Ascii]之间的字符总是适合一个字节只是因为2 ^ 8 = 256但这似乎不是真的我已经尝试在网站https://mothereff.in/byte-counter并声明该字符占用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 -56
US-ASCII [0, 0, 0, 0] //DONT GET IT SEE THIS ITEM FOR LATER
UTF-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 ONE
getBytes
[-61, -120]//OK MY UTF-8 REPRESENTATION
ISO-8859-1 [-56]//OK
US-ASCII [63]//OK BUT WHY WHEN I ENCODE IN ASCCI DOESNT GET ANY BYTE ENCODED?
UTF-16 [-2, -1, 0, -56]//AGAIN WHAT ARE -2,-1 IN THE LEADING BYTES?
UTF-8 [-61, -120]//OK

我试过了

System.out.println(new String(new byte[]{-1,-2},"UTF-16"));//SIMPLE "" I AM WASTING THIS 2 BYTES??

在简历中。

  1. 为什么UTF-16总是有两个前导字节被浪费了?新字节[] { - 1,-2}
  2. 为什么我编码“È”我在ASCII字符集中没有任何字节,但是当我做È.getBytes(StandardCharsets.US_ASCII)时,我得到{63}?
  3. Java使用UTF-16,但在我的情况下,UTF-8是平台依赖?

对不起,如果这篇文章令人困惑

环境

Windows 7 64 Bits Netbeans 8.2 with Java 1.8.0_121
java string utf-8 utf-16
2个回答
1
投票

第一个问题

对于您的第一个问题:这些字节是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 8 Strings在内部使用UTF-16,但在与其他软件通信时,可能需要不同的编码,例如UTF-8。 Charset.defaultCharset()方法返回虚拟机的默认字符集,该字符集取决于操作系统的区域设置和字符集,而不是Java字符串内部使用的编码。


1
投票

让我们稍微回顾一下......

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。因此,如果理解的是字节是使用任何Unicode编码进行文本编码并且存在BOM,那么确定它是哪种Unicode编码以及字节顺序是非常简单的,如果相关的话。

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.