Как правильно вычислить длину строки в Java?
Я знаю, что есть String#length
и различные методы в Character
, которые более или менее работают с кодовыми единицами/кодовыми точками.
Каков предложенный способ в Java, чтобы фактически вернуть результат в соответствии со стандартами Unicode (UAX # 29), принимая такие вещи, как язык/язык, нормализация и графема кластеров?
Ответы
Ответ 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
Это должно выполнять работу в большинстве случаев.
Ответ 2
Нормальная модель длины строки Java
String.length()
указывается как возвращающее число значений char
( "единиц кода" ) в строке. Это наиболее общее определение длины строки Java; см. ниже.
Ваше описание 1 семантики length
на основе размера массива основы/массива неверно. Тот факт, что значение, возвращаемое length()
, также является размером массива поддержки или среза массива, является лишь деталью реализации типичных библиотек классов Java. String
не нужно выполнять таким образом. В самом деле, я думаю, что видел реализации Java String, где он НЕ реализован таким образом.
Альтернативные модели длины строки.
Чтобы получить количество кодовых точек Unicode в строке, используйте str.codePointCount(0, str.length())
- см. javadoc.
Чтобы получить размер (в байтах) строки в другой кодировке, используйте str.getBytes(charset).length
.
Для решения проблем, связанных с локальностью, вы можете использовать Normalizer
для нормализации строки в любой форме, наиболее подходящей для вашего использования, и затем используйте 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 - Это описание было в некоторых версиях вопроса. См. Историю изменений... если у вас есть достаточные точки ответа.
Ответ 3
Это зависит от того, что вы подразумеваете под "длиной [the] String":
-
String.length()
возвращает число chars
в String
. Обычно это полезно только для программирования связанных задач, таких как выделение буферов, потому что многобайтовая кодировка может вызвать проблемы, что означает, что char
не означает one Кодовая точка Unicode.
-
String.codePointCount(int, int)
и Character.codePointCount(CharSequence,int,int)
оба возвращают количество кодовых точек Unicode в String
. Обычно это полезно только для программирования связанных задач, требующих просмотра String
в виде последовательности кодов Unicode без необходимости беспокоиться о мешающем многобайтовом кодировании.
-
BreakIterator.getCharacterInstance(Locale)
можно использовать для получения следующего grapheme в String
для данного Locale
. Используя это несколько раз, вы можете подсчитать количество графемов в String
. Поскольку графемы - это в основном буквы (в большинстве случаев), этот метод полезен для получения количества записываемых символов, которые содержит String
. По сути этот метод возвращает примерно тот же номер, который вы получите, если вручную подсчитать количество букв в String
, что делает его полезным для таких вещей, как определение пользовательских интерфейсов и разделение Strings
без искажения данных.
Чтобы дать вам представление о том, как каждый из разных методов может возвращать разные длины для одних и тех же данных, я создал этот класс для быстро создайте длины текста Юникода, содержащиеся в этой странице, которая предназначена для всестороннего тестирования многих разных языков с неанглийскими символами, Ниже приведены результаты выполнения этого кода после нормализации входного файла тремя способами (без нормализации, 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
может давать разные результаты для длины, если вы используете либо String.length()
, либо String.codePointCount(int,int)
.
Для получения дополнительной информации по этой теме и другим подобным темам вы должны прочитать это сообщение в блоге, которое охватывает различные основы использования Java для правильной дескриптор Unicode.
Ответ 4
String.length()
не возвращает размер массива, поддерживающего строку, а фактическую длину строки, определяемую как "количество единиц кода Unicode в строке". (см. API docs).
(Как отметил Стивен С в комментариях, Unicode code units == Java chars)
Если это не то, что вы ищете, то, возможно, вы должны уточнить вопрос немного.
Ответ 5
Если вы имеете в виду подсчет длины строки в соответствии с грамматическими правилами языка, тогда ответ не будет, нет такого алгоритма в Java и нигде.
Нет, если алгоритм также не выполняет полный семантический анализ текста.
В венгерском, например, sz
и zs
может считаться одной буквой или двумя, что зависит от состава слова, в котором они появляются. (Например: ország
- 5 букв, тогда как torzság
равно 7.)
Uodate. Если все, что вам нужно, это стандартное количество символов Unicode (что, как я уже указывал, неточно), преобразование вашей строки в форму NFKC
с помощью java.text.Normalizer
может быть решением.