Сохранение смещения строк Java с помощью Unicode в Python - PullRequest
2 голосов
/ 23 мая 2019

Мы создаем программу на Python 3, которая вызывает программу на Java. Java-программа (сторонняя программа, которую мы не можем изменить) используется для токенизации строк (поиска слов) и предоставления других аннотаций. Эти аннотации представлены в виде смещения символов.

Например, мы можем предоставить программе строковые данные, такие как "lovely weather today". Это обеспечивает что-то вроде следующего вывода:

0,6
7,14
15,20

Где 0,6 - это смещения, соответствующие слову "lovely", 7,14 - это смещения, соответствующие слову "weather", и 15,20 - это смещения, соответствующие слову "today" в исходной строке. Мы читаем эти смещения в Python, чтобы извлечь текст в этих точках и выполнить дальнейшую обработку.

Все хорошо, пока персонажи находятся в базовой многоязычной плоскости ( BMP ). Однако, если это не так, смещения, сообщаемые этой Java-программой, обнаруживают все ошибки на стороне Python.

Например, учитывая строку "I feel ? today", программа на Java выведет:

0,1
2,6
7,9
10,15

На стороне Python они переводятся как:

0,1    "I"
2,6    "feel"
7,9    "? "
10,15  "oday"

Где последний индекс технически недействителен. Java видит «?» как длину 2, что приводит к тому, что все аннотации после этой точки отключаются на единицу с точки зрения программы Python.

Предположительно, это происходит потому, что Java кодирует строки внутренним образом UTF-16esqe, а все строковые операции действуют на эти кодовые единицы UTF-16esque . С другой стороны, строки Python работают с реальными символами Юникода (кодовые точки). Поэтому, когда символ появляется вне BMP, Java-программа видит его как длину 2, тогда как Python видит его как длину 1.

Итак, теперь возникает вопрос: как лучше всего «исправить» эти смещения до того, как Python их использует, чтобы подстроки аннотации соответствовали тому, что Java-программа намеревалась вывести?

Ответы [ 2 ]

2 голосов
/ 23 мая 2019

Вы можете преобразовать строку в байтовый массив в кодировке UTF16, а затем использовать смещения (умноженные на 2, поскольку на единицу кода UTF-16 приходится два байта) для индексации этого массива:

x = "I feel ? today"
y = bytearray(x, "UTF-16LE")

offsets = [(0,1),(2,6),(7,9),(10,15)]

for word in offsets:
  print(str(y[word[0]*2:word[1]*2], 'UTF-16LE'))

Выходные данные:

I
feel
?
today

В качестве альтернативы, вы можете преобразовать каждый символ питона в строке индивидуально в UTF-16 и подсчитать количество требуемых единиц кода.Это позволяет отображать индексы в единицах кода (из Java) в индексы в единицах символов Python:

from itertools import accumulate

x = "I feel ? today"
utf16offsets = [(0,1),(2,6),(7,9),(10,15)] # from java program

# map python string indices to an index in terms of utf-16 code units
chrLengths = [len(bytearray(ch, "UTF-16LE"))//2 for ch in x]
utf16indices = [0] + list(itertools.accumulate(chrLengths))
# reverse the map so that it maps utf16 indices to python indices
index_map = dict((x,i) for i, x in enumerate(utf16indices))

# convert the offsets from utf16 code-unit indices to python string indices
offsets = [(index_map[o[0]], index_map[o[1]]) for o in utf16offsets]

# now you can just use those indices as normal
for word in offsets:
  print(x[word[0]:word[1]])

Вывод:

I
feel
?
today

Приведенный выше код является грязным ивозможно, можно прояснить, но вы поняли идею.

1 голос
/ 23 мая 2019

Это решает проблему, учитывая правильное кодирование, которое в нашей ситуации выглядит как 'UTF-16BE':

def correct_offsets(input, offsets, encoding):
  offset_list = [{'old': o, 'new': [o[0],o[1]]} for o in offsets]

  for idx in range(0, len(input)):
    if len(input[idx].encode(encoding)) > 2:
      for o in offset_list:
        if o['old'][0] > idx:
          o['new'][0] -= 1
        if o['old'][1] > idx:
          o['new'][1] -= 1

  return [o['new'] for o in offset_list]

Это может быть довольно неэффективно.Я с радостью приветствую любые улучшения производительности.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...