什么是Java中的“代理对”?

vs91vp4v  于 11个月前  发布在  Java
关注(0)|答案(7)|浏览(185)

我在阅读StringBuffer的文档,特别是reverse()方法。文档中提到了关于 surrogate pairs 的内容。在这种情况下,什么是surrogate pair?什么是 lowhigh surrogate?

jum4pzuy

jum4pzuy1#

术语“代理项对”是指在UTF-16编码方案中使用高码位编码Unicode字符的方法。
在Unicode字符编码中,字符Map到0x 0和0x 10 FFFF之间的值。
在内部,Java使用UTF-16编码方案来存储Unicode文本的字符串。在UTF-16中,使用16位(两字节)代码单元。由于16位只能包含从0x 0到0xFFFF的字符范围,因此使用一些额外的复杂性来存储此范围(0x 10000到0x 10 FFFF)以上的值。这使用称为代理的代码单元对来完成。
代理代码单元在称为“高代理”和“低代理”的两个范围内,这取决于它们是否被允许在双代码单元序列的开始或结束处。

tnkciper

tnkciper2#

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

qfe3c7zg

qfe3c7zg3#

添加一些更多的信息,以上述答案从this后。
在Java-12中测试,应该可以在所有Java版本5以上工作。
如前所述: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 the number of Unicode characters in a Java String

字符串
1.平等:
使用Unicode \ud83c\udf09将“”表示为String,如下所示,并检查相等性。

"🌉".equals("\ud83c\udf09") // true


Java不支持UTF-32

"🌉".equals("\u1F309") // false


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

"🌉".equals(new String(Character.toChars(0x0001F309))) //true

  1. substring()不考虑补充字符
"🌉🌐".substring(0,1) //"?"
"🌉🌐".substring(0,2) //"🌉"
"🌉🌐".substring(0,4) //"🌉🌐"


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

"🌉🌐".substring(0,"🌉🌐".offsetByCodePoints(0,1) // "🌉"
"🌉🌐".substring(2,"🌉🌐".offsetByCodePoints(1,2)) // "🌐"


1.使用BreakIterator迭代Unicode字符串
1.使用Unicode java.text.Collator排序字符串
1.不应使用Character的toUpperCase()toLowerCase()方法,而应使用特定区域设置的String String和String。

  1. Character.isLetter(char ch)不支持,最好使用Character.isLetter(int codePoint),对于Character类中的每个methodName(char ch)方法,都会有methodName(int codePoint)类型可以处理补充字符。
    1.指定String.getBytes()中的字符集,从字符串转换为字符串,InputStreamReaderOutputStreamWriter
    1.Java-21java.lang.Character.isEmoji和新的Regex相关模式中添加了新方法,来自here的emoji数据,如果您正在使用here提到的任何库,此新功能可能会有所帮助
public static void main(String[] args) {
    System.out.println('☺' + " isEmoji : " + isEmoji('☺')); // true
    System.out.println('❌' + " isEmoji : " + isEmoji('❌')); // true
    System.out.println('ž' + " isEmoji : " + isEmoji('ž')); // false

    emojiChecks("A");
    emojiChecks("©");
    emojiChecks("☺");
    emojiChecks("\uD83D\uDE0A");
}

private static void emojiChecks(String emoji) {
    // If any string is not emoji then it can not be moji_Component, Emoji_Presentation, Emoji_Modifier, and Emoji_Modifier_Base. 
    // Ref: https://unicode.org/reports/tr51/#Emoji_Properties_and_Data_Files
    final Pattern emojiPattern = Pattern.compile("\\p{IsEmoji}");
    final Pattern emojiModifierBasePattern = Pattern.compile("\\p{IsEmoji_Modifier_Base}");
    final Pattern emojiComponentPattern = Pattern.compile("\\p{IsEmoji_Component}");
    final Pattern emojiPresentationPattern = Pattern.compile("\\p{IsEmoji_Presentation}");
    final Pattern isExtendedPictographicPattern = Pattern.compile("\\p{IsExtended_Pictographic}");
    System.out.println(emoji + " IsEmoji: " + emojiPattern.matcher(emoji).matches());
    System.out.println(emoji + " IsEmojiModifierBase: " + emojiModifierBasePattern.matcher(emoji).matches());
    System.out.println(emoji + " IsEmojiComponent: " + emojiComponentPattern.matcher(emoji).matches());
    System.out.println(emoji + " IsEmojiPresentation: " + emojiPresentationPattern.matcher(emoji).matches());
    System.out.println(emoji + " IsExtended_Pictographic: " + isExtendedPictographicPattern.matcher(emoji).matches());
    System.out.println("----------------------------------------");
}

// output
☺ isEmoji : true
❌ isEmoji : true
ž isEmoji : false
A IsEmoji: false
A IsEmojiModifierBase: false
A IsEmojiComponent: false
A IsEmojiPresentation: false
A IsExtended_Pictographic: false
----------------------------------------
© IsEmoji: true
© IsEmojiModifierBase: false
© IsEmojiComponent: false
© IsEmojiPresentation: false
© IsExtended_Pictographic: true
----------------------------------------
☺ IsEmoji: true
☺ IsEmojiModifierBase: false
☺ IsEmojiComponent: false
☺ IsEmojiPresentation: false
☺ IsExtended_Pictographic: true
----------------------------------------
😊 IsEmoji: true
😊 IsEmojiModifierBase: false
😊 IsEmojiComponent: false
😊 IsEmojiPresentation: true
😊 IsExtended_Pictographic: true
----------------------------------------


参考号:
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
更多关于image1image2的信息
其他值得探讨的术语:标准化,BiDi

ioekq8ef

ioekq8ef4#

该文档的意思是,无效的UTF-16字符串在调用reverse方法后可能会变为有效,因为它们可能是有效字符串的反转。代理项对(这里讨论)是一对UTF-16中的16位值,它们编码单个Unicode代码点;低代理项和高代理项是该编码的两部分。

kkbh8khc

kkbh8khc5#

小序

  • Unicode表示码位。根据Unicode标准,每个码位可以编码为8位、16位或32位块。
  • 在3.1版本之前,主要使用的是8位编码,称为UTF-8,和16位编码,称为UCS-2或“以2个八位字节编码的通用字符集”。UTF-8将Unicode点编码为1字节块的序列,而UCS-2总是占用2个字节:
  • A = 41* -一个8位UTF-8块
  • A = 0041* -UCS-2的一个16位块
  • Ω = CE A9* -两个8位UTF-8块
  • Ω = 03 A9 * -UCS-2的一个16位块
    问题

该联盟认为16位将足以覆盖任何人类可读的语言,它给出了 2^16 = 65536 可能的代码值。这对于平面0来说是正确的,也被称为BMP或基本多语言平面,它包括今天65536个代码点中的55,445个。BMP几乎覆盖了世界上所有的人类语言,包括中国-日本-韩国符号(CJK)。
随着时间的推移,新的亚洲字符集被添加进来,仅中文符号就占据了70,000多个点。现在,甚至有Emoji points作为标准的Emoji points的一部分。新的16个“额外”Planes被添加进来。UCS-2房间不足以覆盖比Plane-0更大的东西。

Unicode决定

1.将Unicode限制为17个平面×每个平面65 536个字符= 1 114 112个最大点。
1.目前的UTF-32,以前称为UCS-4,为每个代码点保存32位,并覆盖所有平面。
1.继续使用UTF-8作为动态编码,将每个码点的UTF-8限制为最大4字节,即每个码点从1字节到4字节。
1.弃用UCS-2
1.基于UCS-2创建UTF-16。将UTF-16设置为动态的,因此每个点占用2个字节或4个字节。将1024个点U+D800-U+DBFF(称为高位替代)分配给UTF-16;将1024个符号U+ DC 00-U+DFFF(称为低位替代)分配给UTF-16。
通过这些变化,BMP被UTF-16中的1个16位块覆盖,而所有“补充字符”被代理对覆盖,每个代理对呈现2个16位块,总共1024 x1024 = 1 048 576点。

  • 高代理项位于低代理项之前 *。任何与此规则的偏差都被视为错误编码。例如,没有配对的代理项是不正确的,位于高代理项之前的低代理项是不正确的。

𝄞“MUSICAL SYMBOL G CLEF”,以UTF-16编码为一对代理0xD 834 0xDD 1 E(2 × 2字节),
在UTF-8中为0xF 0 0x 9D 0x 84 0x 9 E(4 × 1字节),
在UTF-32中为0x 0001 D11 E(1 × 4字节)。

目前情况

  • 虽然根据标准,代理项只指定给UTF-16,但历史上一些Windows和Java应用程序使用UTF-8和UCS-2,现在保留给代理项范围。

为了支持具有不正确的UTF-8/UTF-16编码的遗留应用程序,创建了一个新的标准WTF-8,即Wobbly Transformation Format。它支持任意代理点,例如非配对的代理或不正确的序列。今天,一些产品不符合该标准,并将UTF-8视为WTF-8。

  • 代理解决方案打开了一些security problems,并尝试使用“非法代理对”。

许多历史细节被压制,以遵循主题。
最新的Unicode标准可以在http://www.unicode.org/versions/latest上找到

7gcisfzg

7gcisfzg6#

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

c6ubokkw

c6ubokkw7#

代理对是UTF-16中的两个“代码单元”,它们组成一个“代码点”。Java文档指出,这些“代码点”在颠倒后仍然有效,它们的“代码单元”顺序正确。它进一步指出,两个不成对的代理代码单元可以颠倒并形成一个有效的代理对。这意味着如果存在不成对的代码单元,那么就有可能反向的反向可能不一样!
但是请注意,文档中没有提到字形--它是多个码点的组合。这意味着e和与它一起沿着的重音仍然可以交换,因此重音放在e之前。这意味着如果e之前有另一个元音,它可能会得到e上的重音。
哎呀!

相关问题