【问题标题】:Python find offsets of a word token in a textPython在文本中查找单词标记的偏移量
【发布时间】:2021-06-28 15:16:27
【问题描述】:

我编写了这个函数findTokenOffset,它在预先标记的文本中找到给定单词的偏移量(作为间隔单词列表或根据某个标记器)。

导入re,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,tokens)
print(json.dumps(offsets, indent=2)) 

由于在不同的标记中重复单词,偏移量无法正常工作:

[
  {
    "index": 1,
    "word": "George Washington",
    "characterOffsetBegin": 0,
    "characterOffsetEnd": 16
  },
  {
    "index": 2,
    "word": "Washington",
    "characterOffsetBegin": 7,
    "characterOffsetEnd": 16
  }
]

如何添加对多令牌重叠令牌正则表达式匹配的支持(感谢 cmets 中对这个确切问题名称的建议)?

【问题讨论】:

  • 您的预期输出是什么?这与重叠的正则表达式匹配有关吗?
  • 一次性完成。你的词汇量有多大(tokens 的数量)?
  • 嘿@Aaron 是的,这是一个“令牌重叠”正则表达式匹配,正确。
  • 如果您需要在生成的 JSON 中包含令牌索引,这种方法可能会变得非常混乱,请参阅ideone.com/jMEtRb。看起来不错,但您无法获取有关匹配令牌的信息。
  • 令牌边界是什么? Washington.com 中的Washington 是一个完整的单词,因为n. 之间存在单词边界。给定要求,这是一个有效的匹配。如果你想在一个单词后面有一个.并且右边有一个单词char的情况下匹配失败,你可以使用re.compile(fr'(?<!\w)(?:{"|".join(sorted(map(re.escape, tokens), key=len, reverse=True))})\b(?!\.\b)', re.I ),见demo

标签: python tokenize stringtokenizer


【解决方案1】:

如果您不需要结果输出中的搜索短语/单词索引信息,您可以使用以下方法:

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'(?&lt;!\w)(?:{"|".join(sorted(map(re.escape, tokens), key=len, reverse=True))})\b(?!\.\b)', re.I ),它执行以下操作:

  • map(re.escape, tokens) - 转义 tokens 字符串中的特殊字符
  • sorted(..., key=len, reverse=True) - 以降序对转义的tokens 中的项目进行排序(以便Washigton Post 可以早于Washington 匹配)
  • "|".join(...) - 创建了tokenstoken1|token2|etc 的备用列表
  • (?&lt;!\w)(?:...)(?!\w)(?!\.\b) - 是匹配tokens 中的所有替代项作为整个单词的最终模式。 (?&lt;!\w)(?!\w) 用于启用字边界检测,即使 tokens 以特殊字符开始/结束。

单词边界注释

您应该检查您的令牌边界要求。我添加了(?!\.\b),因为您提到Washington 不应该在Washington.com 中匹配,所以我推断想要在紧跟. 和单词边界时使任何单词匹配失败。还有很多其他可能的解决方案,主要的一种是空白边界(?&lt;!\S)(?!\S)

此外,请参阅Match a whole word in a string using dynamic regex

【讨论】:

    【解决方案2】:

    如果您想查找 Washington,而不是 George Washington,您可以从初始字符串中删除您找到的句子。因此,您可以按单词数量对“令牌”进行排序。这让您有机会先扫描句子,然后再扫描单词。

    【讨论】:

    • 我认为您建议在每个匹配步骤中使用占位符,对吧?
    猜你喜欢
    • 2013-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-02
    • 2015-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多