单词列表上的正则表达式作为输入

时间:2016-04-17 10:39:15

标签: python regex list

我有单词列表形式的句子,例如

sentence = ['if', 'it', 'will', 'rain', ',', 'I', 'will', 'stay', 'at', 'home']

现在我想找到条件句['if', 'it', 'will', 'rain']。原则上,我可以从句子创建一个字符串,例如s = ' '.join(sentence),我和使用正则表达式:

p = re.compile(r'(\bif\b[a-zA-z0-9\'\s]+)\s*(,*)\s*(then|,)')
for m in p.finditer(s):
    print m.start(1), m.end(1), '['+s[ m.start(1) : m.end(1) ]+']'

无需判断正则表达式,它只是快速勾画:)。这给了我输出:0 16 [if it will rain ]

到目前为止一切顺利。但现在我有点想念与我的orignal列表的连接。正则表达式给了我角色位置而不是单词/令牌位置。理想情况下,我会得到0和3所以我知道条件子句是sentence[0:3]。我确信我可以编写一个将字符位置映射到相应列表索引的方法,但我确信这样做会更好。

当然,我可以忽略正则表达式,遍历列表并提出正确的启动和停止条件。但是,由于他们“隐藏”以明确所需的条件,因此目前看似相当整洁。当条件子句由其他单词或短语表示时,它们也简化了这种情况,例如:

sentence = ['as', 'long', 'as', 'it', 'will', 'rain', ',', 'I', 'will', 'stay', 'at', 'home']

使用正则表达式很容易反映这一点,我认为使用循环会更烦人。

编辑:看到实际上没有一个非常简单的解决方案,我继续想到在句子之间创建一个映射作为正则表达式的字符串和原始单词列表:

def join(self, word_list, separator=' '):
    mapping = []
    string = separator.join(word_list)
    for idx, word in enumerate(word_list):
    for character in word:
        mapping.append(idx)
    for character in separator:
        mapping.append(idx)
    return string, mapping

将此方法应用于我的输入string, mapping = join(sentence)会导致:

mapping = [0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 9, 9]

现在,如果正则表达式将016作为匹配范围,我可以使用sentence和{查找原始mapping[0] = 0列表中的索引{1}}。到目前为止,这似乎运作得相当好。由于我使用字符串的正则表达式进行匹配,我可以轻松地支持条件子句的替代公式,例如:

mapping[16] = 4

同样,我并不是说正则表达式已经完美,但它同时支持多个句子,并且条件子句有不同的指示词。

2 个答案:

答案 0 :(得分:1)

注意: - 如果if

中只出现一次,thensentence

我已经修改了你的正则表达式以包含一个更多的捕获组

re.compile("((\\bif\\b)[a-zA-z0-9\\'\\s]+)\\s*(,*)\\s*(then|,)")

您可以将re.findall用作

arr = re.findall(p, s)

arr[0][1]包含第一个捕获组(字符串if),arr[0][3]包含第三个捕获组(字符串then,)。您可以使用index来查找这些2的索引

start = sentence.index(arr[0][1])
end = sentence.index(arr[0][3])

现在,您可以使用

形成字符串
stri = ' '.join(sentence[start: end])

注1: - 如果if中出现,thensentence多次(非重叠) ,你将不得不迭代所有元组

arr = re.findall(p, s)
pos = 0 #It stores the last occurrence of matched group
for i, x in enumerate(arr):
    start = sentence.index(x[1], pos)
    end = sentence.index(x[3], pos)
    stri = ' '.join(sentence[start: end])
    print(stri)
    pos = sentence.index(x[3], pos) + 1

<强> Ideone Demo

注意2: - 请注意,如果找不到字符串,index会引发异常。在上面做之前处理它

答案 1 :(得分:1)

切换到正则表达式和从正则表达式切换会产生问题,因为您还必须将输入切换到字符串和从字符串切换 - 并使它们保持同步。

你有一种OR的列表比较函数怎么样:

sentence = ['if', 'it', 'will', 'rain', ',', 'I', 'will', 'stay', 'at', 'home']
phrase = ['if', [',', 'then']]

def findPhrase(phrase, full):
  currentpos = 0
  isFirst = True
  result = []
  for part in phrase:
    if isinstance(part, list):
      partOffset = 999
      for subpart in part:
        if subpart in full[currentpos:]:
          if full[currentpos:].index(subpart) < partOffset:
            partOffset = full[currentpos:].index(subpart)
      if partOffset == 999:
        return []
      currentpos += partOffset
      if isFirst:
        result.append (currentpos)
      else:
        result[-1] = currentpos
      continue
    if not part in full[currentpos:]:
      return []
    currentpos = currentpos + full[currentpos:].index(part)
    if isFirst:
      result.append (currentpos)
    else:
      result[-1] = currentpos
    # check for a single word match; should still return a range
    # .. just duplicate last item
    if len(result) == 1:
      result.append(result[0])
  return result

res = findPhrase (phrase, sentence)
if res == []:
  print 'not found'
else:
  print res
  print sentence[res[0]:res[1]+1]

这会比较&#39;短语&#39;反对&#39;句子,一次一个字,如果没有匹配则返回[],如果有,则返回start:end范围。

这是

的输出
[0, 4]
['if', 'it', 'will', 'rain', ',']

可以使用&#39; optional&#39;等项目扩展findPhrase功能。并且&#39;任何匹配&#39;,但是您必须将基于简单数组的语法扩展为类似字典的内容。

目前,代码会从一个找到的单词跳到下一个单词,忽略其间的任何内容。如果您想添加明确的'*'&#39;短语&#39;项目,含义&#34;跳过任意数量的单词&#34;,你需要(1)测试它是否是匹配短语中的 last 项目(如果是,你可以发出sentence)的最后一项,和/或(2)实现一个单独的类似前瞻的函数来检查phrase中的 next 项是否出现在sentence中}。 (这非常接近于模仿正则表达式解析器。)