问题描述
我编写了这个函数 findTokenOffset
,它在预先标记化的文本中查找给定单词的偏移量(作为间隔单词列表或根据某个标记器)。
导入重新,json
def word_regex_ascii(word):
return r"\b{}\b".format(re.escape(word))
def findTokenOffset(text,tokens):
seen = {} # map if a token has been see already!
items=[] # word tokens
my_regex = word_regex_ascii
# for each token word
for index_word,word in enumerate(tokens):
r = re.compile(my_regex(word),flags=re.I | re.X | re.UNICODE)
item = {}
# for each matched token in sentence
for m in r.finditer(text):
token=m.group()
characterOffsetBegin=m.start()
characterOffsetEnd=characterOffsetBegin+len(m.group()) - 1 # LP: star from 0
found=-1
if word in seen:
found=seen[word]
if characterOffsetBegin > found:
# store last word has been seen
seen[word] = characterOffsetEnd
item['index']=index_word+1 #// word index starts from 1
item['word']=token
item['characterOffsetBegin'] = characterOffsetBegin
item['characterOffsetEnd'] = characterOffsetEnd
items.append(item)
break
return items
text = "George Washington came to Washington"
tokens = text.split()
offsets = findTokenOffset(text,tokens)
print(json.dumps(offsets,indent=2))
但是,应该有像这里这样的多令牌方式的令牌:
text = "George Washington came to Washington"
tokens = ["George Washington","Washington"]
offsets = findTokenOffset(text,indent=2))
由于在不同标记中重复单词,偏移量无法正常工作:
[
{
"index": 1,"word": "George Washington","characterOffsetBegin": 0,"characterOffsetEnd": 16
},{
"index": 2,"word": "Washington","characterOffsetBegin": 7,"characterOffsetEnd": 16
}
]
如何添加对多令牌和重叠令牌正则表达式匹配的支持(感谢评论中对该问题确切名称的建议)?
解决方法
如果您不需要结果输出中的搜索短语/单词索引信息,您可以使用以下方法:
import re,json
def findTokenOffset(text,pattern):
items = []
for m in pattern.finditer(text):
item = {}
item['word']=m.group()
item['characterOffsetBegin'] = m.start()
item['characterOffsetEnd'] = m.end()
items.append(item)
return items
text = "George Washington came to Washington Washington.com"
tokens = ["George Washington","Washington"]
pattern = re.compile(fr'(?<!\w)(?:{"|".join(sorted(map(re.escape,tokens),key=len,reverse=True))})(?!\w)(?!\.\b)',re.I )
offsets = findTokenOffset(text,pattern)
print(json.dumps(offsets,indent=2))
Python demo 的输出:
[
{
"word": "George Washington","characterOffsetBegin": 0,"characterOffsetEnd": 17
},{
"word": "Washington","characterOffsetBegin": 26,"characterOffsetEnd": 36
}
]
主要部分是 pattern = re.compile(fr'(?<!\w)(?:{"|".join(sorted(map(re.escape,reverse=True))})\b(?!\.\b)',re.I )
,它执行以下操作:
-
map(re.escape,tokens)
- 转义tokens
字符串中的特殊字符 -
sorted(...,reverse=True)
- 按长度降序对转义的tokens
中的项目进行排序(以便Washigton Post
可以早于Washington
匹配) -
"|".join(...)
- 创建了一个tokens
、token1|token2|etc
的替代列表
-
(?<!\w)(?:...)(?!\w)(?!\.\b)
- 是将tokens
中的所有替代词作为整个单词匹配的最终模式。(?<!\w)
和(?!\w)
用于启用词边界检测,即使tokens
以特殊字符开头/结尾。
词界注意事项
您应该检查您的令牌边界要求。我添加了 (?!\.\b)
,因为你提到 Washington
不应该在 Washington.com
中匹配,所以我推断当它紧跟 .
和一个单词时,任何单词匹配都会失败边界。还有很多其他可能的解决方案,主要的解决方案是空白边界、(?<!\S)
和 (?!\S)
。
此外,请参阅Match a whole word in a string using dynamic regex。
,如果您想查找 Washington 而不是 George Washington,您可以从初始字符串中删除您找到的句子。因此,您可以按单词数量对“令牌”进行排序。这让你有机会先浏览句子,然后再浏览单词。