Преобразование смещения символов в смещения байтов (в Python)
Предположим, у меня есть куча файлов в UTF-8, которые я отправляю на внешний API в Unicode. API работает с каждой строкой Unicode и возвращает список с (character_offset, substr)
кортежи.
Вывод, который мне нужен, это смещение байта начала и конца для каждой найденной подстроки. Если мне повезет, входной текст содержит только символы ASCII (смещение символов и байтов одинаково), но это не всегда так. Как найти начальные и конечные смещения байтов для известного начального смещения символа и подстроки?
Я сам ответил на этот вопрос, но с нетерпением жду других решений этой проблемы, которые будут более надежными, более эффективными и / или более читабельными.
2 ответа
Я решил бы это, используя смещение символов в словаре для сопоставления байтовых смещений, а затем просматривал смещения в этом.
def get_char_to_byte_map(unicode_string):
"""
Generates a dictionary mapping character offsets to byte offsets for unicode_string.
"""
response = {}
byte_offset = 0
for char_offset, character in enumerate(unicode_string):
response[char_offset] = byte_offset
byte_offset += len(character.encode('utf-8'))
return response
char_to_byte_map = get_char_to_byte_map(text)
for begin_offset, substring in api_response:
begin_offset = char_to_byte_map[character_offset]
end_offset = char_to_byte_map[character_offset + len(substring)]
# do something
Производительность этого решения по сравнению с вашим во многом зависит от размера ввода и количества задействованных подстрок. Локальный микро-бенчмаркинг предполагает, что кодирование каждого отдельного символа в тексте занимает примерно в 1000 раз больше времени, чем кодирование всего текста за один раз.
Чтобы при необходимости преобразовать смещения символов в смещения байтов, encode('utf8')
текст, ведущий к найденной подстроке, если во входном тексте есть какие-либо не-ASCII символы, и его длина берется как начальное смещение.
# Check if text contains non-ASCII characters
needs_offset_conversion = len(text) != len(text.encode('utf8'))
def get_byte_offsets(text, character_offset, substr, needs_conversion):
if needs_conversion:
begin_offset = len(text[:character_offset].encode('utf8'))
end_offset = begin_offset + len(substr.encode('utf8'))
else:
begin_offset = character_offset
end_offset = character_offset + len(substr)
return begin_offset, end_offset
Эта реализация работает, но она кодирует (большую) часть текста для каждой найденной подстроки.