在Python中保持Java String Offset liticode一致



我们正在构建一个调用Java程序的Python 3程序。Java程序(这是我们无法修改的第三方程序(用于标记字符串(查找单词(并提供其他注释。这些注释是字符偏移的形式。

作为一个例子,我们可能会为程序提供诸如"lovely weather today"之类的字符串数据。它提供了以下输出之类的东西:

0,6
7,14
15,20

0,6是与"可爱"一词相对应的偏移,而 7,14是与"天气"一词相对应的偏移,而 15,20是与源字符串中"今日"一词相对应的偏移。我们在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-16Sque代码单元作用。另一方面,Python字符串似乎在实际的Unicode字符(代码点(上工作。因此,当一个字符在BMP外显示时,Java程序将其视为长度2,而Python将其视为长度1。

现在的问题是:在Python使用它们之前"更正"这些偏移的最佳方法是什么,以便注释子字符串与Java程序打算输出的内容一致?

您可以将字符串转换为utf16编码中的bytearray,然后使用偏移量(乘以2乘以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

另外,您可以将字符串中的每个python字符单独转换为UTF-16,并计算其所需的代码单位数。这使您可以根据python字符映射代码单位(从Java(到索引:

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

上面的代码很混乱,可能会更清楚,但是您明白了。

这解决了给定适当编码的问题,在我们的情况下,这似乎是 '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]

这可能非常低效。我很高兴欢迎任何绩效改进。

最新更新