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

soc*_*soc 19 java string unicode standards character-encoding

我知道有String#length各种方法Character可以或多或少地处理代码单元/代码点.

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

Ste*_*n C 24

Java字符串长度的正常模型

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

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


字符串长度的替代模型.

要获取String使用中的Unicode代码点数量,str.codePointCount(0, str.length())请参阅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());
Run Code Online (Sandbox Code Playgroud)

"mum".length()没有返回代码点或者它不是语言上正确的字符数,这无关紧要.它使用适合于手头任务的模型来测量字符串的长度.它有效.

显然,当你进行多语言文本分析时,事情会变得复杂一些; 例如,搜索单词.但即便如此,如果你在开始之前规范化你的文本和参数,你可以在大多数时间安全地编写"代码单元"而不是"代码点"的代码; 即length()仍然有效.


1 - 此描述是针对该问题的某些版本.如果您有足够的重复点,请参阅编辑历史记录.


soc*_*soc 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
}
Run Code Online (Sandbox Code Playgroud)

运行它:

scala> val text = "Thi?s lo?o?ks we?ird!"
text: java.lang.String = Thi?s lo?o?ks we?ird!

scala> val length = length(text)
length: Int = 17

scala> val codepoints = text.codePointCount(0, text.length)
codepoints: Int = 21 
Run Code Online (Sandbox Code Playgroud)

使用代理对:

scala> val parens = "\uDBFF\uDFFCsurpi\u0301se!\uDBFF\uDFFD"
parens: java.lang.String = surpi?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
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这应该可以完成.


Emi*_*rey 6

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

  • String.length()返回的数量charsString。通常,这仅对编程相关任务(例如分配缓冲区)有用,因为多字节编码可能会导致问题,这意味着一个字节char并不意味着一个Unicode代码点
  • String.codePointCount(int, int)Character.codePointCount(CharSequence,int,int)都返回在Unicode代码点的数量String。通常,这仅对编程相关的任务有用,这些任务需要将a String看作一系列Unicode代码点,而不必担心多字节编码干扰。
  • BreakIterator.getCharacterInstance(Locale)可以用来获得下一个字形String对给定Locale。多次使用此功能可以让您计算一个字素中的字素数量String。因为字素基本上是字母(在大多数情况下),所以此方法对于获取String包含的可写字符数很有用。从本质上讲,此方法返回的数字与您手动计算中的字母数所获得的数字大致相同String,从而使它可用于调整用户界面大小和拆分Strings而不破坏数据的工作。

为了让您了解每个不同的方法如何为完全相同的数据返回不同的长度,我创建了此类,以快速生成此页面中包含的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
Run Code Online (Sandbox Code Playgroud)

如您所见,String如果使用String.length()或,则即使是“长相相同” 的长度也可能会得出不同的结果String.codePointCount(int,int)

有关此主题和其他类似主题的更多信息,您应该阅读此博客文章,其中涵盖了使用Java正确处理Unicode的各种基础知识。