Преобразование смещения символов в смещения байтов (в 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

Эта реализация работает, но она кодирует (большую) часть текста для каждой найденной подстроки.

Другие вопросы по тегам