我们正在构建一个调用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]
这可能非常低效。我很高兴欢迎任何绩效改进。