什么是Java中的"代理对"?

Ray*_*ond 139 java unicode utf-16 surrogate-pairs

我正在阅读文档StringBuffer,特别是reverse()方法.该文件提到了代理人对的一些内容.在这种情况下,什么是代理对?什么是代理人?

Jef*_*dge 119

术语"代理对"是指在UTF-16编码方案中用高代码点编码Unicode字符的方法.

在Unicode字符编码中,字符映射到0x0和0x10FFFF之间的值.

在内部,Java使用UTF-16编码方案来存储Unicode文本的字符串.在UTF-16中,使用16位(双字节)代码单元.由于16位只能包含从0x0到0xFFFF的字符范围,因此使用一些额外的复杂度来存储高于此范围(0x10000到0x10FFFF)的值.这是使用称为代理的代码单元对完成的.

代理代码单元在两个范围内称为"高代理"和"低代理",这取决于它们是否允许在双代码单元序列的开头或结尾.

  • 这是得票最多的,但它没有提供单个代码示例。这些答案也没有实际如何使用它。这就是为什么这被否决的原因。 (11认同)
  • @GeorgeXavier 这个问题没有要求示例或代码。他们要求解释什么是代理对,答案提供了这个解释。 (2认同)

ibr*_*ban 49

早期Java版本使用16位char数据类型表示Unicode字符.这种设计在当时是有意义的,因为所有Unicode字符的值都小于65,535(0xFFFF),并且可以用16位表示.但是,稍后,Unicode将最大值增加到1,114,111(0x10FFFF).由于16位值太小而无法表示Unicode版本3.1中的所有Unicode字符,因此UTF-32编码方案采用32位值(称为代码点).但是为了有效使用内存,16位值优于32位值,因此Unicode引入了一种新设计,允许继续使用16位值.采用UTF-16编码方案的这种设计将1624个值分配给16位高代理(在U + D800到U + DBFF范围内),另外1,024个值分配给16位低代理(在U + DC00范围内)到U + DFFF).它使用高代理,后跟低代理 - 代理对 - 代表(1,024和1,024的乘积)1,048,576(0x100000)值介于65,536(0x10000)和1,114,111(0x10FFFF)之间.

  • 我比接受的答案更喜欢这个,因为它解释了Unicode 3.1如何保留原始65535中的1024 + 1024(高+低)值以获得1024*1024个新值,而没有添加要求解析器从一开始就开始串. (7认同)
  • 我不喜欢这个暗示 UTF-16 是最节省内存的 Unicode 编码的答案。UTF-8 存在,并且_不会_将大多数文本呈现为两个字节。如今,UTF-16 被广泛使用,因为微软在 UTF-32 出现之前就选择了它,而不是为了内存效率。您实际上_想要_ UTF-16 的唯一时间是当您在 Windows 上进行大量文件处理时,因此会大量读取并写入它。否则,UTF-32 用于高速(b/c 常量偏移)或 UTF-8 用于低内存(b/c 最小 1 字节) (2认同)

Jer*_*ock 22

该文档所说的是,无效的UTF-16字符串在调用reverse方法后可能变为有效,因为它们可能是有效字符串的反转.代理对(此处讨论)是UTF-16中的一对16位值,它们编码单个Unicode代码点; 低和高代理是该编码的两半.

  • 澄清.必须在"true"字符(又名"字形"或"文本元素")上反转字符串.单个"字符"代码点可以是一个或两个"字符"块(代理对),字形可以是这些代码点中的一个或多个(即基本字符代码加上一个或多个组合字符代码,每个代码字符代码可能是一个或两个16位块或"字符长".因此,单个字素可以是三个组合字符,每两个"字符"长,总共6个"字符".当反转整个字符串时,所有6个"字符"必须按顺序(即不反转)保持在一起. (6认同)
  • 因此,"char"数据类型相当误导."性格"是一个松散的术语."char"类型实际上只是UTF16块大小,我们将其称为字符,因为代理对发生的相对罕见(即它通常代表整个字符代码点),因此"字符"实际上指的是单个unicode代码点,但随后使用组合字符,您可以拥有一系列显示为单个"字符/字形/文本元素"的字符.这不是航天科技; 概念很简单,但语言令人困惑. (4认同)

dkb*_*dkb 11

帖子的以上答案中添加更多信息

在Java-12中进行了测试,应该可以在高于5的所有Java版本中使用。

如此处所述:https : //stackoverflow.com/a/47505451/2987755
无论哪个字符(Unicode高于U + FFFF)都表示为代理对,Java将其存储为一对char值,即单个Unicode字符表示为两个相邻的Java字符。
如下面的示例所示。
1.长度:

"".length()  //2, Expectations was it should return 1

"".codePointCount(0,"".length())  //1, To get number of Unicode character in Java String  
Run Code Online (Sandbox Code Playgroud)

2.相等性:如下
使用Unicode表示“”为String \ud83c\udf09并检查相等性。

"".equals("\ud83c\udf09") // true
Run Code Online (Sandbox Code Playgroud)

Java不支持UTF-32

"".equals("\u1F309") // false  
Run Code Online (Sandbox Code Playgroud)

3.您可以将Unicode字符转换为Java字符串

"".equals(new String(Character.toChars(0x0001F309))) //true
Run Code Online (Sandbox Code Playgroud)

4. String.substring()不考虑补充字符

"".substring(0,1) //"?"
"".substring(0,2) //""
"".substring(0,4) //""
Run Code Online (Sandbox Code Playgroud)

为了解决这个问题,我们可以使用 String.offsetByCodePoints(int index, int codePointOffset)

"".substring(0,"".offsetByCodePoints(0,1) // ""
"".substring(2,"".offsetByCodePoints(1,2)) // ""
Run Code Online (Sandbox Code Playgroud)

5.迭代Unicode字符串与的BreakIterator
6.排序字符串使用Unicode java.text.Collat​​or中
7.字符toUppercase,小写不应使用,相反,使用字符串大写和特定地点的小写
8. Character.isLetter(char ch)不支持,更好使用Character.isLetter(int codePoint),对于每个methodName(char ch)在Character类中的方法将存在methodName(int codePoint)可以处理补充字符的类型。
9.在中指定字符集String.getBytes(),将Bytes转换为String InputStreamReaderOutputStreamWriter

参考:https:
//coolsymbol.com/emojis/emoji-for-copy-and-paste.html#objects
https://www.online-toolz.com/tools/text-unicode-entities-convertor.php
https: //www.ibm.com/developerworks/library/j-unicode/index.html
https://www.oracle.com/technetwork/articles/javaee/supplementary-142654.html

有关示例image1 image2的更多信息
其他值得探讨的术语:NormalizationBiDi

  • 特地登录以投票支持此答案(我的意思是将窗口从隐身更改为正常窗口:P)。菜鸟的最佳解释 (4认同)

Art*_*tru 7

小序

  • Unicode 表示代码点。每个代码点都可以根据 Unicode 标准以 8 位、16 位或 32 位块进行编码。

  • 在 3.1 版之前,主要使用的是 8 位编码,称为 UTF-8,和 16 位编码,称为 UCS-2 或“以 2 个八位字节编码的通用字符集”。UTF-8 将 Unicode 点编码为 1 个字节的块序列,而 UCS-2 总是占用 2 个字节:

    A = 41 - 一个带有 UTF-8 的 8 位块
    A = 0041 - 一个带有 UCS-2 的 16 位块
    ?= CE A9 - 两块 8 位的 UTF-8
    ?= 03A9 - 一块 16 位的 UCS-2

问题

该联盟认为 16 位足以涵盖任何人类可读的语言,这给出了2^16 = 65536 个可能的代码值。对于平面 0(也称为 BPM 或基本多语言平面)而言,情况确实如此,它包括今天的 65536 个代码点中的 55,445 个。BPM 涵盖了世界上几乎所有人类语言,包括中日韩符号 (CJK)。

随着时间的流逝和新的亚洲字符集的添加,仅中文符号就花费了 70,000 多个点。现在,甚至有表情符号点作为标准的一部分。添加了新的 16 个“附加”平面。UCS-2 房间不足以覆盖比 Plane-0 更大的任何东西。

统一码决定

  1. 将 Unicode 限制为 17 个平面 × 每个平面 65 536 个字符 = 1 114 112 个最大点。
  2. 目前的 UTF-32,以前称为 UCS-4,为每个代码点保存 32 位并覆盖所有平面。
  3. 继续使用 UTF-8 作为动态编码,将 UTF-8 限制为每个代码点最多 4 个字节,即每个点从 1 到 4 个字节。
  4. 弃用 UCS-2
  5. 基于 UCS-2 创建 UTF-16。使 UTF-16 动态化,因此每个点需要 2 个字节或 4 个字节。将 1024 个点 U+D800–U+DBFF,称为 High Surrogates,分配给 UTF-16;将 1024 个符号 U+DC00–U+DFFF(称为低代理)分配给 UTF-16。

通过这些更改,BPM 被 UTF-16 中的 16 位块覆盖,而所有“补充字符”都被代理对覆盖,每个块显示 2 个块 x 16 位,总共 1024x1024 = 1 048 576 个点。

高代理先于低代理。任何与此规则的偏差都被视为错误编码。例如,没有一对的代理是不正确的,站在高代理之前的低代理是不正确的。

, 'MUSICAL SYMBOL G CLEF', 在 UTF-16 中编码为一对代理 0xD834 0xDD1E(2 x 2 个字节),
在 UTF-8 中编码为 0xF0 0x9D 0x84 0x9E(4 x 1 个字节),
在 UTF-30010x1E 中编码为 0xD834 0xDD1E (1 x 4 字节)。

现在的情况

  • 尽管根据标准,代理仅指定给 UTF-16,但历史上一些 Windows 和 Java 应用程序使用现在保留到代理范围的 UTF-8 和 UCS-2 点。
    为了支持使用不正确的 UTF-8/UTF-16 编码的遗留应用程序,创建了一个新的标准 WTF-8,即摇摆变换格式。它支持任意代理点,例如非配对代理或不正确的序列。今天,有些产品不符合标准,将 UTF-8 视为 WTF-8。
  • 代理解决方案带来了一些安全问题,以及使用“非法代理对”的尝试。

许多历史细节被压制以跟随话题?。
最新的 Unicode 标准可以在http://www.unicode.org/versions/latest找到


dfb*_*dfb 6

代理对是指UTF-16对某些字符进行编码的方式,请参阅http://en.wikipedia.org/wiki/UTF-16/UCS-2#Code_points_U.2B10000..U.2B10FFFF

  • "性格"是一个如此负载的术语. (11认同)

Ger*_*ill 5

代理对是 UTF-16 中组成一个“代码点”的两个“代码单元”。Java 文档指出,在相反之后,这些“代码点”仍然有效,其“代码单元”顺序正确。它还指出两个不成对的代理代码单元可以颠倒并形成有效的代理对。这意味着如果存在未配对的代码单元,那么逆向的逆向可能会不一样!

但请注意,文档中没有提及任何有关字形的内容——字形是多个代码点的组合。这意味着 e 和随之而来的重音仍然可以交换,从而将重音放在 e 之前。这意味着如果 e 之前有另一个元音,它可能会得到 e 上的重音。

哎呀!