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

时间:2011-07-26 09:39:06

标签: java string unicode character-encoding standards

我知道String#length中存在Character和各种方法,这些方法或多或少地处理代码单元/代码点。

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

5 个答案:

答案 0 :(得分:21)

Java字符串长度的正常模型

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

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


字符串长度的替代模型。

要使用str.codePointCount(0, str.length())获取字符串中的Unicode代码点数,请参阅the javadoc

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

要处理特定于语言环境的问题,您可以使用Normalizer将String规范化为最适合您的用例的任何形式,然后使用上面的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 - 此描述是针对该问题的某些版本。如果您有足够的重复点,请参阅编辑历史记录。

答案 1 :(得分:11)

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

在大多数情况下,这应该可以胜任。

答案 2 :(得分:4)

这完全取决于你所说的“字符串长度”:

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

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

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

正如您所看到的,如果您使用StringString.length(),即使是“相同的”String.codePointCount(int,int)也可能会给出不同的长度结果。

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

答案 3 :(得分:0)

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

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

如果这不是你想要的,那么也许你应该再详细说明这个问题。

答案 4 :(得分:0)

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

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

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

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