python - 查找包含另一个列表中的子字符串的列表元素的有效方法

标签 python list substring

list1 = ["happy new year", "game over", "a happy story", "hold on"]
list2 = ["happy", "new", "hold"]

假设我有两个字符串列表,我想使用一个新列表来存储这两个列表的匹配对,如下所示:

list3=[["happy new year","happy"],["happy new year","new"],["a happy story","happy"],["hold on","hold"]]

这意味着我需要获取一个列表中的所有字符串对及其子字符串在另一个列表中。

其实这是一些中国古文字的数据。第一个列表包含 10 至 13 世纪的人物姓名,第二个列表包含该时期所有诗歌的标题。中国古代人常常在作品的标题中记录他们的社会关系。例如,有人可能写一首诗,题为“献给我的 friend 王安石”。这样的话,第一个列表中的人“王安石”就应该与这个标题相匹配。还有像“献给我的 friend 王安石和苏轼”这样的案例,标题中包含了不止一个人。所以基本上这是一部涉及 30,000 人和 160,000 首诗的庞大作品。

以下是我的代码:

list3 = []

for i in list1:
        for j in list2:
            if str(i).count(str(j)) > 0:
                list3.append([i,j])

我使用 str(i) 因为 python 总是将我的中文字符串视为 float 。这段代码确实可以工作,但是太慢了。我必须想出另一种方法来做到这一点。谢谢!

最佳答案

使用 regular expression通过 re module 进行搜索。正则表达式引擎可以比嵌套的 for 循环更好地计算出文本搜索中的匹配元素。

我将在这里使用更好的变量名称,以使其更清楚列表的位置; titles 是您正在搜索的诗歌标题,names 是您尝试匹配的内容。 matched 是您想要生成的 (title, name) 对:

import re

titles = ["happy new year", "game over", "a happy story", "hold on"]
names = ["happy", "new", "hold"]

by_reverse_length = sorted(names, key=len, reverse=True)
pattern = "|".join(map(re.escape, by_reverse_length))
any_name = re.compile("({})".format(pattern))
matches = []

for title in titles:
    for match in any_name.finditer(title):
        matches.append((title, match.group()))

上面生成了您所需的输出:

>>> matches
[('happy new year', 'happy'), ('happy new year', 'new'), ('a happy story', 'happy'), ('hold on', 'hold')]

名称按长度反向排序,因此具有相同前缀的较长名称先于较短名称找到;例如Hollander 先于 Holland 找到,而 Holl 又先于 Holl 找到。

pattern 字符串是根据您的姓名创建的,以形成 ...|...|... 替代 模式,任何这些模式之一可以匹配,但正则表达式引擎会找到序列中较早列出的模式,而不是较晚列出的模式,因此需要按长度进行反向排序。整个名称模式周围的 (...) 括号告诉正则表达式引擎捕获一组文本中的该部分。然后,循环中的 match.group() 调用可以提取匹配的文本。

re.escape() function调用是为了防止名称中出现“元字符”,具有特殊含义的字符,例如 ^$() 等,避免被解释为它们特殊的正则表达式含义。

re.finditer() function (以及编译模式上的方法)然后按从左到右的顺序查找非重叠匹配,因此它永远不会匹配较短的子字符串,并使我们有机会提取 match object对于每个。如果您想了解starting positions of the matches,这将为您提供更多选择以及其他元数据,如果您需要的话。否则,re.findall()也可以用在这里。

如果您打算在带有西方字母的文本上使用上述内容,而不是在中文上使用,那么您可能还需要添加单词边界标记,\b:

any_name = re.compile("\b({})\b".format(pattern))

否则可以匹配较大单词的子字符串部分。由于中文没有单词边界字符(例如空格和标点符号),因此您不想在此类文本中使用 \b

关于python - 查找包含另一个列表中的子字符串的列表元素的有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56622687/

相关文章:

java - 实现 Java 的 indexOf 方法(子串搜索)

mysql - 记录难以辨认的文件

Python:使用urllib下载不存在的文件时处理异常

python - 插入排序不变断言失败

python 多进程未正确完成

python - 如何遍历列表?

Java:使用 Lambda 识别 ArrayList<String> 中的公共(public)路径

python - 属性错误: 'DataFrame' object has no attribute 'dtype'

c - 匹配(搜索)C 列表中的 URL 的最佳方法(实现白名单或黑名单)?

java - 从链表数组中提取链表