我们正在构建一个调用Java程序的Python 3程序。 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 code units起作用。另一方面,Python字符串似乎可以对实际的unicode字符(代码点)进行操作。因此,当字符显示在BMP之外时,Java程序将其视为长度2,而Python将其视为长度1。
所以现在的问题是:在Python使用偏移之前“校正”这些偏移的最佳方法是什么,以使注释子字符串与Java程序打算输出的内容一致?
答案 0 :(得分:2)
您可以将字符串转换为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
或者,您可以将字符串中的每个python字符分别转换为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 :(得分:1)
这解决了给定正确编码的问题,在我们的情况下,该编码看起来是'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]
这可能效率很低。我非常欢迎任何性能方面的改进。