"Как вычислить длину строки Java в UTF-8 без её кодирования?"
Заголовок: Как определить длину бинарного представления строки в кодировке UTF-8 без ее генерации?
Текст вопроса: Здравствуйте, я ищу способ получить длину бинарного представления строки в кодировке UTF-8 с помощью стандартной библиотеки Java (любая версия), не создавая при этом само закодированное значение.
Например, мне нужно найти эффективный способ сделать то же самое, что и данный код:
"some really long string".getBytes("UTF-8").length
Я планирую использовать это для вычисления префикса длины для потенциально длинных сериализованных сообщений. Если у кого-то есть идеи или решения, буду очень благодарен!
2 ответ(ов)
Ваша ситуация вполне распространена, и подход, который вы предложили, является вполне разумным. Использование CharsetEncoder
для многократной записи в один и тот же временный буфер позволяет эффективно управлять буферизацией и избегать ненужных выделений памяти.
Ваш метод getEncodedLength
корректно обрабатывает кодирование символов из CharBuffer
с помощью заданного кодировщика. Кроме того, он правильно обрабатывает случаи, когда кодировщик выдает ошибку или когда буфер переполняется. Использование outputBuffer.clear()
после каждой итерации гарантирует, что вы будете перезаписывать буфер, а не накапливать данные.
Дополнительно, вы производите финальную очистку с помощью метода flush
, что также является хорошей практикой для завершения процесса кодирования.
Вот ваш код с комментариями для большей ясности:
public int getEncodedLength(CharBuffer src, CharsetEncoder encoder)
throws CharacterCodingException
{
// Проверяем, есть ли оставшиеся символы для кодирования
if (!src.hasRemaining())
return 0;
// Создаем временный буфер для кодирования
final ByteBuffer outputBuffer = ByteBuffer.allocate(1024);
// Итеративный процесс кодирования
int bytes = 0;
CoderResult status;
do
{
// Кодируем символы из CharBuffer в ByteBuffer
status = encoder.encode(src, outputBuffer, true);
if (status.isError())
status.throwException(); // Обрабатываем ошибки кодирования
// Суммируем количество записанных байтов
bytes += outputBuffer.position();
// Очищаем буфер для повторного использования
outputBuffer.clear();
}
while (status.isOverflow()); // Продолжаем, пока буфер переполнен
// Завершаем кодирование оставшегося состояния
status = encoder.flush(outputBuffer);
if (status.isError() || status.isOverflow())
status.throwException(); // Обрабатываем финальные ошибки
// Добавляем количество записанных байтов после flush
bytes += outputBuffer.position();
return bytes; // Возвращаем общее количество закодированных байтов
}
public int getUtf8Length(String str) throws CharacterCodingException
{
return getEncodedLength(CharBuffer.wrap(str),
Charset.forName("UTF-8").newEncoder());
}
Ваш метод getUtf8Length
прекрасно инкапсулирует вызов getEncodedLength
, что делает его удобным для использования. В целом, данный подход демонстрирует хорошую практику работы с кодировками и буферами в Java.
Вы можете перебрать строку следующим образом:
/**
* Устаревший метод: не поддерживает суррогатные символы.
*/
@Deprecated
public int countUTF8Length(String str) {
int count = 0;
for (int i = 0; i < str.length(); ++i) {
char c = str.charAt(i);
if (c < 0x80) {
count++;
} else if (c < 0x800) {
count += 2;
} else {
throw new UnsupportedOperationException("ещё не реализовано");
}
}
return count;
}
Имейте в виду, что данный код устарел и не учитывает суррогатные пары, которые используются для представления символов Unicode, находящихся за пределами базовой многобайтовой плоскости. Для корректного подсчета байтов в UTF-8 нужен более сложный подход, который будет учитывать все возможные символы. Рекомендуется использовать стандартные библиотеки Java для обработки строк, такие как getBytes("UTF-8")
, чтобы избежать подобных проблем.
Инициализация ArrayList в одну строчку
Что значит 'synchronized'?
Почему нет ConcurrentHashSet, если есть ConcurrentHashMap?
Как объявить массив в одну строку?
Какие проблемы следует учитывать при переопределении equals и hashCode в Java?