我知道Java中有String#length
和Character
中的各种方法,它们更多或少地处理码元/码点。
在Java中,按照Unicode标准(UAX#29)返回结果,考虑语言/区域设置、规范化和字形群集等因素,建议采用什么方式?
String.length()
被规定为返回 String 中 char
值("code units")的数量。这是 Java 字符串长度最通用的定义,详见下文。
基于后端数组/数组片段大小来计算 length
语义的描述1是不正确的。length()
返回的值也是后端数组或数组片段的大小仅仅是典型 Java 类库的实现细节。String 没有必要这样实现。事实上,我认为我看到过一些不是这样实现的 Java String 实现。
要获取字符串中 Unicode 代码点(codepoints) 的数量,请使用 str.codePointCount(0, str.length())
-- 参见javadoc。
要获取特定编码(charset)下的字符串大小(以字节为单位),请使用 str.getBytes(charset).length
2。
要处理与语言环境相关的问题,您可以使用Normalizer
将字符串规范化为最适合您用例的形式,然后像上面那样使用codePointCount
。但在某些情况下,即使这样也不起作用;例如,匈牙利语字母计数规则,Unicode 标准显然没有考虑到。
大多数应用程序使用 String.length()
的原因是它们不关心以人为中心的方式计算单词、文本等中的字符数。例如,如果我执行此操作:
String s = "hi mum how are you";
int pos = s.indexOf("mum");
String textAfterMum = s.substring(pos + "mum".length());
重要的是,"mum".length()
不返回代码点,也不是语言上正确的字符计数。它使用适合当前任务的模型来测量字符串的长度,并能正常工作。
当涉及到多语言文本分析时,情况会变得更加复杂;例如搜索单词。但即使这样,在开始之前对文本和参数进行规范化后,大部分时间你仍然可以安全地使用“代码单元”而不是“代码点”进行编码,也就是说,length()
仍然有效。
1- 此描述在某些版本的问题中出现。如果您有足够的声望点,请查看编辑历史记录。
2- 使用 str.getBytes(charset).length
就意味着进行编码并将其丢弃。可能有一种通用方法可以在不进行此副本的情况下完成此操作。这将涉及将 String
包装为 CharBuffer
,创建一个没有备份以充当字节计数器的自定义 ByteBuffer
,然后使用 Encoder.encode(...)
来计算字节数。注意:我尚未尝试过这个方法,除非您有明确的证据表明 getBytes(charset)
是显著的性能瓶颈,否则不建议尝试。
java.text.BreakIterator
能够迭代文本并且能够报告“字符”,单词,句子和行边界。
考虑以下代码:
def length(text: String, locale: java.util.Locale = java.util.Locale.ENGLISH) = {
val charIterator = java.text.BreakIterator.getCharacterInstance(locale)
charIterator.setText(text)
var result = 0
while(charIterator.next() != BreakIterator.DONE) result += 1
result
}
运行它:
scala> val text = "Thîs lóo̰ks we̐ird!"
text: java.lang.String = Thîs lóo̰ks we̐ird!
scala> val length = length(text)
length: Int = 17
scala> val codepoints = text.codePointCount(0, text.length)
codepoints: Int = 21
使用代理对:
scala> val parens = "\uDBFF\uDFFCsurpi\u0301se!\uDBFF\uDFFD"
parens: java.lang.String = surpíse!
scala> val length = length(parens)
length: Int = 10
scala> val codepoints = parens.codePointCount(0, parens.length)
codepoints: Int = 11
scala> val codeunits = parens.length
codeunits: Int = 13
在大多数情况下,这应该可以完成任务。
这取决于你所说的“字符串长度”的确切含义:
String.length()
返回 String
中 chars
的数量。这通常只对编程相关的任务有用,例如分配缓冲区,因为多字节编码可能会导致问题,这意味着一个char
并不意味着一个Unicode code point。String.codePointCount(int, int)
和 Character.codePointCount(CharSequence,int,int)
都返回String
中Unicode代码点的数量。这通常只对需要将String
视为一系列Unicode代码点而无需担心多字节编码干扰的编程相关任务有用。BreakIterator.getCharacterInstance(Locale)
可用于获取给定Locale
下String
中下一个grapheme。多次使用此方法可以让您计算String
中图形符号的数量。由于在大多数情况下图形符号基本上是字母,因此此方法对于获取String
包含的可写字符数非常有用。本质上,此方法返回的数字与手动计算String
中字母数的数字大致相同,因此它非常适用于调整用户界面的大小和拆分Strings
而不会破坏数据。Input UTF-8 String
>> String.length() = 3431
>> String.codePointCount(int,int) = 3431
>> BreakIterator.getCharacterInstance(Locale) = 3386
NFC Normalized UTF-8 String
>> String.length() = 3431
>> String.codePointCount(int,int) = 3431
>> BreakIterator.getCharacterInstance(Locale) = 3386
NFD Normalized UTF-8 String
>> String.length() = 3554
>> String.codePointCount(int,int) = 3554
>> BreakIterator.getCharacterInstance(Locale) = 3386
正如您所看到的,即使是“看起来相同”的String
如果使用String.length()
或String.codePointCount(int,int)
可能会给出不同的长度结果。
关于此主题以及其他类似主题的更多信息,您应该阅读此博客文章,其中涵盖了有关使用Java正确处理Unicode的各种基础知识。
如果你是指按照语言的语法规则计算字符串的长度,那么答案是否定的,Java 或者其他任何地方都没有这样的算法。
除非算法还进行了完整的语义分析。
例如,在匈牙利语中,sz
和 zs
可以被视为一个字母或两个字母,这取决于它们出现在单词中的组合。(例如:ország
是 5 个字母,而 torzság
是 7 个字母。)
更新:如果你只想要 Unicode 标准字符数(就像我指出的那样,不太准确),将字符串转换为 java.text.Normalizer
的 NFKC
表单可能是一种解决方案。
.indexOf()方法提供了一个提示:
int length = (yourString + "whatever").indexOf("whatever");
BreakIterator
计数,尽管我还没有弄清楚原因。 - skomisa