如何正确计算Java中String的长度?

我知道String#lengthCharacter的各种方法或多或少都适用于代码单元/代码点。

在Java中实际返回Unicode标准( UAX#29 )指定的结果的建议方式是什么,将语言/语言环境,规范化和字形集群考虑在内?

Java字符串长度的正常模型

String.length()指定为返回String中的char值(“代码单元”)的数量。 这是Java String长度最常用的定义; 见下文。

您的描述1基于后备arrays/arrays切片大小的length语义不正确。 length()返回的值也是后备数组或数组切片的大小这一事实仅仅是典型Java类库的实现细节。 String不需要以这种方式实现。 实际上,我认为我已经看到了Java String实现,它没有以这种方式实现。


字符串长度的替代模型。

要获取String中的Unicode代码点数,请使用str.codePointCount(0, str.length()) – 请参阅javadoc 。

要在某些其他编码中获取String的大小(以字节为单位),请使用str.getBytes(charset).length

要处理特定于语言环境的问题,可以使用Normalizer将String规范化为最适合您的用例的任何forms,然后使用上面的codePointCount

但在某些情况下,即使这样也行不通; 例如,计算Unicode标准显然不适合的规则的匈牙利字母。


使用String.length()通常可以

大多数应用程序使用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 – 此描述是针对该问题的某些版本。 如果您有足够的重复点,请参阅编辑历史记录。

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()返回Stringchars数。 这通常仅用于编程相关任务(如分配缓冲区),因为多字节编码可能会导致问题,这意味着一个char不代表一个Unicode代码点 。
  • String.codePointCount(int, int)Character.codePointCount(CharSequence,int,int)都返回String的Unicode代码点数。 这通常仅用于编程相关任务,这些任务需要将String视为一系列Unicode代码点,而无需担心多字节编码干扰。
  • BreakIterator.getCharacterInstance(Locale)可用于获取给定LocaleString的下一个字形 。 多次使用此function可以计算String的数量。 由于字形基本上是字母(在大多数情况下),因此该方法对于获取String包含的可写字符数非常有用。 基本上,如果您手动计算String的字母数,此方法返回的数字大致相同,这使得它可以用于调整用户界面和分割Strings而不破坏数据。

为了让您了解每个不同的方法如何为完全相同的数据返回不同的长度,我创建了这个类来快速生成此页面中包含的Unicode文本的长度,该文本旨在提供对许多方法的全面测试不同语言的非英语字符。 以下是在以三种不同方式(无规范化, NFC , NFD )规范化输入文件后执行该代码的结果:

 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.length()String.codePointCount(int,int) ,即使是“相同的” String也可能为长度提供不同的结果。

有关此主题和其他类似主题的更多信息,请阅读本博文 ,其中介绍了使用Java正确处理Unicode的各种基础知识。

String.length()不返回支持字符串的数组的大小,而是返回字符串的实际长度,定义为“字符串中Unicode代码单元的数量”。 (请参阅API文档 )。

(正如Stephen C在评论中所指出的, Unicode代码单元 == Java字符)

如果这不是你想要的,那么也许你应该再详述一下这个问题。

如果你的意思是,根据语言的语法规则计算字符串的长度,那么答案是否定的,Java中没有这样的算法,也没有其他任何算法。

除非算法也对文本进行完整的语义分析。

例如,在匈牙利语中, szzs可以算作一个字母或两个字母,这取决于它们出现的单词的组成。(例如: ország是5个字母,而torzság是7.)

Uodate :如果您想要的只是Unicode标准字符数(正如我所指出的那样,这是不准确的),使用java.text.Normalizer将您的字符串转换为NFKC表单可能是一种解决方案。