#python #python-3.x #unicode
#python #python-3.x #юникод
Вопрос:
Если API принимает некоторое строковое значение с ограничением на количество байтов, но принимает Unicode, есть ли лучший способ сократить строку с допустимым Unicode?
def truncate(string: str, length: int):
"""Shorten an Unicode string to a certain length of bytes."""
if len(string.encode()) <= length:
return string
chars = list(string)
while sum(len(char.encode()) for char in chars) > length:
chars.pop(-1)
return "".join(chars)
Комментарии:
1. Непонятно, о чем вы спрашиваете. Вы используете мировой «байт», но в Unicode есть символы (или кодовые точки). Вы хотите иметь фиксированную длину в символах? Или вам нужна фиксированная длина в байтах (но это зависит от кодировки, вы имеете в виду UTF-8?) В этом случае решение может быть сложным. Является ли длина длинной?
2. То, что вы просите, не очень «юникодоническое» :-). То есть он не учитывает сложности сокращения коротких строк на разных языках, которые поддерживает Unicode. Прочитайте о «Символах и символах» в Unicode TR # 17 Модель кодирования символов Unicode . Простое усечение может привести к получению плохо сформированных последовательностей байтов или к повреждению глифов из-за пропущенных символов. Если вы укажете желаемое поведение глифа, мы сможем дать вам лучшие ответы о Pythonic способах его получения.
3. В данном конкретном случае мне все равно, Sendgrid лжет о принятии 50 символов для имени / фамилии, когда это 50 байт. В БД у нас есть до 128 символов, так что мы должны где-то их сократить. Тем не менее, если есть более чистое решение, пожалуйста, давайте послушаем его!
Ответ №1:
Это должно работать в Python-3:
bytes_ = string.encode()
try:
return bytes_[:length].decode()
except UnicodeDecodeError as err:
return bytes_[:err.start].decode()
В основном мы усекаем при первой ошибке декодирования.UTF-8 — это префиксный код. Поэтому декодер всегда должен иметь возможность видеть, когда строка усекается в середине символа. Странности могут возникать с акцентами и прочим. Я еще не продумал этот вопрос до конца. Возможно, нам тоже нужна некоторая нормализация.
В Python-2 обязательно укажите кодировку.
Комментарии:
1. Вероятно, это должно явно указывать кодировку. По умолчанию используется UTF-8 на большинстве нормальных платформ, но есть много пользователей, которые используют Windows, потому что они не знают ничего лучшего или потому, что должны.
2. @tripleee В Python 3, .encode() и .decode() по умолчанию имеют значение ‘utf8’ и в Windows.
3. К сожалению, символы / кодовые точки также не соответствуют графическим единицам. В большинстве случаев это будет работать нормально, но может привести к разрыву объединяющего символа из его базового блока или разделению последовательности смайликов / флагов или аналогичного. Это может быть приемлемым ограничением. Использование нормализации поможет удалить некоторые комбинирующие символы, если они доступны.
4. Я поддерживаю это, потому что это довольно Pythonic способ обрезать строку текста Unicode, закодированную как UTF-8, с числом, меньшим или равным ограничению в байтах. Однако этот вид произвольного отсечения не очень «уникод-оничный» 🙂 . См. Комментарий о модели символов и символов под основным вопросом.