Возможные проблемы с реверсом String с использованием метода charAt
Я видел комментарий здесь, что все решения с charAt
ошибочны. Я не мог точно понять и найти что-то о charAt
в Интернете. Когда я смотрю исходный код, он просто возвращает элемент из массива char. Поэтому мой вопрос в том, что если есть какие-либо проблемы или проблемы с использованием charAt
?
Комментарий подобен
Строго говоря, все решения, основанные на charAt, неверны, поскольку charAt не дает вам "символ в", но "блок кода в", и есть единицы кода, которые не являются символами и символами, которые требуется несколько блоков кода.
Ответы
Ответ 1
Различные символы кодируются с разными номерами байтов (с использованием схемы UTF-16). Например, символ "A" представлен следующим образом:
01000001
Пока все хорошо.
Но если у вас есть символ 𝔴, у вас возникнет проблема. Его представление UTF-16 (BE):
11011000 00110101 11011101 00110100
И тогда charAt
действительно может вернуть второй код для этого символа.
См. реализацию JDK 7 String#charAt
:
public char charAt(int index) {
if ((index < 0) || (index >= count)) {
throw new StringIndexOutOfBoundsException(index);
}
return value[index + offset];
}
Ответ 2
В Java String
по существу является массивом char
. Аналогично, a char
является кодовой точкой UCS-2 (UTF-16).
Есть две проблемы с этим:
- Не все символы могут быть выражены с помощью одной кодовой точки в UTF-16.
- Unicode поддерживает объединение символов.
Переупорядочивающие символы, которые являются частью любой из этих ситуаций, приведут к неправильному String
.
StringBuilder
reverse учитывает первую ситуацию, но я не знаю ничего, что учитывает второе.
Ответ 3
То, что сказано выше, верно, некоторые единицы кода требуют представления двух символов. Поскольку Java использует 16-битные символы, это встречается нечасто; но, строго говоря, любой код, который использует charAt (...), без учета того, является ли доступ к char частью двух модулей кода char, подвергает себя обработке символов.
Чтобы проверить, работаете ли вы с двумя блоками кода char, вы должны проверить, находится ли начальное значение от charAt(...)
в диапазоне от 0xD800
до 0xDFFF
; так как этот диапазон указывает на начало двух блоков кода char.
Ответ 4
Как указывают другие ответы, некоторые символы могут принимать несколько единиц кода, и вы получите недопустимые символы, если попытаетесь интерпретировать любой из этих кодовых блоков самостоятельно или в сочетании с другими блоками кода.
Еще одна вещь, которую следует иметь в виду, состоит в том, что наличие символа в 2-х кодовых единицах в вашей строке будет смещать все последующие индексы на единицу, так, например, десятый символ будет charAt(10)
вместо charAt(9)
- поэтому, даже если вы не пострадали от проблем с кодировкой самого символа, вы можете обнаружить, что вы извлекаете неправильный символ по индексу позже в строке.
Ответ 5
Строго говоря, да, есть проблема, как указано в причине, которую вы выделили. Проблема в том, что для некоторых символов может потребоваться более 1 char
. Поэтому, используя String.charAt, когда вы меняете строку, у вас будет новый полуслучайный символ из-за переключения по порядку двух символов, которые составляют этот символ.
Но опять же, это, строго говоря,
Ответ 6
Существует множество распространенных ошибочных предположений о тексте, особенно если вы оставите нишу "только одной западной страны", которую вы используете при использовании юникода.
Просто, чтобы начать некоторые релевантные пункты, особенно при работе с UTF-16:
- Кодовой точкой может быть несколько кодовых блоков.
- Символом может быть несколько кодовых точек.
- Кодовая точка может быть несколько символов.
Дополнительная значимость при обратном тексте - переопределения LTR и RTL, для которых требуется специальная обработка.
Я предлагаю вам прочитать принятый ответ Почему современный Perl избегает UTF-8 по умолчанию?, в частности, раздел предполагает нарушение, эта часть является языком программирования- агностик.
Ответ 7
Метод String.charAt
безопасен (для некоторого определения "безопасный" ), но его можно использовать небезопасно, если ваша строка содержит символы вне Basic Многоязычный план, который имеет коды в диапазоне от 0 до 65535.
Вы можете реализовать разворот строк с помощью String.charAt
- AbstractStringBuilder
напрямую использовать char[]
, но это логически то же самое, что использовать String.charAt()
. Он в основном реализует два прохода:
- Первый разворачивает символы, но также проверяет любые суррогатные пары
- Второй повторяет пары суррогатов.
Ответ 8
Самый простой пример для вашего вопроса - это символы UTF-8, такие как:..
charAt() будет легко возвращать символы ASCII, поскольку символы ASCII занимают 1 байт. С другой стороны, символы UTF-8/UTF-16 могут занимать несколько байтов, и поэтому вы можете получить неожиданный вывод.
Многие языки имеют алфавиты/символы в формате UTF-8, поэтому давайте скажем, если ваше приложение предоставит определенную локальную информацию, вы можете использовать символы utf-8, и charAt() в этом случае сработает.