如何在Java中正确计算字符串的长度?

我知道有各种方法或多或少地在代码单元/代码点上工作。String#lengthCharacter

在Java中,建议以什么方式实际返回Unicode标准(UAX#29)指定的结果,同时考虑到语言/区域设置,规范化和字素簇之类的因素?


答案 1

Java 字符串长度的正常模型

String.length()指定为返回 String 中的值(“代码单位”)数。这是对Java字符串长度的最普遍有用的定义;见下文。char

基于支持数组/数组切片大小的语义的描述1 不正确。返回的值也是支持数组或数组片的大小这一事实仅仅是典型 Java 类库的实现细节。 不需要以这种方式实现。事实上,我想我已经看到Java String实现不是以这种方式实现的。lengthlength()String


字符串长度的替代模型。

要获取字符串中使用 Unicode 码位的数量 ,请参阅 javadocstr.codePointCount(0, str.length())

要获取特定编码(即字符集)中字符串的大小(以字节为单位),请使用 2.str.getBytes(charset).length

若要处理特定于区域设置的问题,可以使用 Normalizer 将 String 规范化为最适合您的用例的任何形式,然后按上述方式使用。但在某些情况下,即使这样也行不通;例如,Unicode标准显然不符合匈牙利字母计数规则。codePointCount


使用 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 - 此描述是关于该问题的某些版本。查看编辑历史记录...如果您有足够的代表点数。
2 - 使用 str.getBytes(charset).length 需要进行编码并将其丢弃。在没有该副本的情况下,可能有一种通用方法可以执行此操作。这需要将字符串包装为CharBuffer,创建一个没有支持的自定义ByteBuffer来充当字节计数器,然后使用Orger.encode(...)来计算字节数。注意:我还没有尝试过,除非你有明确的证据表明getBytes(字符集)是一个显着的性能瓶颈,否则我不建议尝试。


答案 2

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 =