我有单词列表形式的句子,例如
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]
现在,如果正则表达式将0
和16
作为匹配范围,我可以使用sentence
和{查找原始mapping[0] = 0
列表中的索引{1}}。到目前为止,这似乎运作得相当好。由于我使用字符串的正则表达式进行匹配,我可以轻松地支持条件子句的替代公式,例如:
mapping[16] = 4
同样,我并不是说正则表达式已经完美,但它同时支持多个句子,并且条件子句有不同的指示词。
答案 0 :(得分:1)
注意: - 如果if
,
和then
或sentence
我已经修改了你的正则表达式以包含一个更多的捕获组
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
中出现,
和then
或sentence
多次(非重叠) ,你将不得不迭代所有元组
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
中}。 (这非常接近于模仿正则表达式解析器。)