У меня есть строка 'abç', которая в UTF-8 равна b'ab\xc3\xa7'.
Я хочу это в UTF-16, но не так:
b'ab\xc3\xa7'.decode('utf-8').encode('utf-16-be')
что дает мне:
b'\x00a\x00b\x00\xe7'
Ответ, который мне нужен, - это кодовые единицы UTF-16, то есть список целых чисел:
[32, 33, 327]
Есть ли какой-нибудь простой способ сделать это?
И конечно, наоборот. Учитывая список целых чисел, которые являются кодовыми единицами UTF-16, как мне преобразовать его в UTF-8?
Кстати: 'abç'.encode('utf-16-be') намного проще и проще, чем b'ab\xc3\xa7'.decode('utf-8').encode('utf-16-be'). До сих пор не ясно, чего вы на самом деле хотите. Коды? 16-битные целые числа? В зависимости от того, почему вы этого хотите, это также может повлиять на лучший способ сделать это, поэтому было бы полезно, если бы вы предоставили эту информацию в своем вопросе, хотя бы в качестве контекста.
Технически они не являются кодовыми единицами.






Простое решение, которое может сработать во многих случаях, будет выглядеть примерно так:
def sort_of_get_utf16_code_units(s):
return list(map(ord, s))
print(sort_of_get_utf16_code_units('abç')
Выход:
[97, 98, 231]
Однако это не работает для персонажей за пределами Базовой многоязычной плоскости (BMP):
print(sort_of_get_utf16_code_units('😊'))
Выходные данные — это кодовая точка Unicode:
[128522]
Где вы могли ожидать кодовые единицы (как указано в вашем вопросе):
[55357, 56842]
Чтобы получить это:
def get_utf16_code_units(s):
utf16_bytes = s.encode('utf-16-be')
return [int.from_bytes(utf16_bytes[i:i+2]) for i in range(0, len(utf16_bytes), 2)]
print(get_utf16_code_units('😊'))
Выход:
[55357, 56842]
Обратное действие аналогично:
def utf16_code_units_to_string(code_units):
utf16_bytes = b''.join([unit.to_bytes(2, byteorder='big') for unit in code_units])
return utf16_bytes.decode('utf-16-be')
print(utf16_code_units_to_string([55357, 56842]))
Выход:
😊
По умолчанию порядок байтов равен 'big', но здесь не помешает уточнить.
Вы также можете использовать лямбда-функцию: get_utf16_code_units = lambda b: [int.from_bytes(b .encode('utf-16-be')[i:i+2]) for i in range(0, len(b .encode('utf-16-be')), 2)] и utf16_code_units_to_string = lambda c: (b''.join([unit.to_bytes(2, byteorder='big') for unit in c])).decode('utf-16-be').
Назначать лямбда-выражение имени — плохая практика, в этом нет никакой пользы, кроме сохранения строки в текстовом файле, и есть явные недостатки как в производительности, так и в читабельности.
@Grismar Как это влияет на производительность?
Да, он компилируется в точно такой же байт-код.
Используйте array.array, чтобы быстро распаковать байтовую строку как беззнаковые шорты и получить кодовые единицы UTF-16:
import array
def utf16_code_units(s):
return array.array('H', s.encode('utf-16le')).tolist()
print(utf16_code_units('abç💩'))
Выход:
[97, 98, 231, 55357, 56489]
Если вам нужна производительность, .tolist() не является обязательным, поскольку array.array имеет вид списка:
>>> array.array('H', 'abç💩'.encode('utf-16le'))
array('H', [97, 98, 231, 55357, 56489])
327 — это
'Ň', а не'ç', что равно 231, как вы можете определить из выходных данных (xe7— это 231 десятичное число). Кроме того,'a'и'b'— это 97 и 98, а не 32 и 33, что было бы пробелом и восклицательным знаком.