Я думаю, что в решении Rex Kerr есть 2 ошибки.
- Во-первых, он усекает до предела + 1, если не-ASCII символ находится непосредственно перед пределом. Усечение «123456789» 1 приведет к «123456789», который представлен в 11 символах в UTF-8.
- Во-вторых, я думаю, что он неверно истолковал стандарт UTF. https://en.wikipedia.org/wiki/UTF-8#Description показывает, что 110xxxxx в начале последовательности UTF говорит нам, что представление имеет длину 2 символа (в отличие от 3). По этой причине его реализация обычно не использует все доступное пространство (как отметил Ниссим Авитан).
Пожалуйста, найдите мою исправленную версию ниже:
public String cut(String s, int charLimit) throws UnsupportedEncodingException {
byte[] utf8 = s.getBytes("UTF-8");
if (utf8.length <= charLimit) {
return s;
}
int n16 = 0;
boolean extraLong = false;
int i = 0;
while (i < charLimit) {
// Unicode characters above U+FFFF need 2 words in utf16
extraLong = ((utf8[i] & 0xF0) == 0xF0);
if ((utf8[i] & 0x80) == 0) {
i += 1;
} else {
int b = utf8[i];
while ((b & 0x80) > 0) {
++i;
b = b << 1;
}
}
if (i <= charLimit) {
n16 += (extraLong) ? 2 : 1;
}
}
return s.substring(0, n16);
}
Я все еще думал, что это далеко не эффективно. Поэтому, если вам действительно не нужно String представление результата и байтовый массив подойдет, вы можете использовать это:
private byte[] cutToBytes(String s, int charLimit) throws UnsupportedEncodingException {
byte[] utf8 = s.getBytes("UTF-8");
if (utf8.length <= charLimit) {
return utf8;
}
if ((utf8[charLimit] & 0x80) == 0) {
// the limit doesn't cut an UTF-8 sequence
return Arrays.copyOf(utf8, charLimit);
}
int i = 0;
while ((utf8[charLimit-i-1] & 0x80) > 0 && (utf8[charLimit-i-1] & 0x40) == 0) {
++i;
}
if ((utf8[charLimit-i-1] & 0x80) > 0) {
// we have to skip the starter UTF-8 byte
return Arrays.copyOf(utf8, charLimit-i-1);
} else {
// we passed all UTF-8 bytes
return Arrays.copyOf(utf8, charLimit-i);
}
}
Забавно, что при реалистичном ограничении в 20-500 байт они выполняют почти одинаково ЕСЛИ вы снова создаете строку из массива байтов.
Обратите внимание, что оба метода предполагают допустимый ввод utf-8, который является допустимым предположением после использования функции getBytes () в Java.