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

时间:2019-03-10 13:27:22

标签: java string utf-8 utf-16

我一直在Java8,Java 11中使用String进行编码,但是这个问题是基于Java 8的。

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

我只是认为0.255 [Ascii + extended 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. 为什么当我对“È”进行编码时,我在ASCCI字符集中没有得到任何字节,但是当我进行È.getBytes(StandardCharsets.US_ASCII)时却得到了{63}?

  3. Java使用UTF-16,但就我而言,UTF-8取决于平台?

对不起,这则帖子令人困惑

环境

Windows 7 64 Bits Netbeans 8.2 with Java 1.8.0_121

2 个答案:

答案 0 :(得分: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作为默认字符编码,这对您想要的方式是否很重要,并已在该系统上对文本文件进行了编码系统吗?

答案 1 :(得分: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 String在内部使用UTF-16,但是当与其他软件通信时,可能会期望使用不同的编码,例如UTF-8。 Charset.defaultCharset()方法返回虚拟机的默认字符集,该默认字符集取决于操作系统的语言环境和字符集,而不取决于Java字符串内部使用的编码。