如何检查列表中一行的任何部分是否包含另一个列表的整行? PYTHON

How can I check if any part of a line from a list contains the full line of another list? PYTHON

我试图找到正确的代码并自己为此编写了多次尝试,但我希望这里有人可以提供帮助。

我有一个小文件和一个大文件: 小:90,000 行 大:1,200,000 行

如果大文件中的任何行包含小文件中的任何行,我正在尝试将大文件中的任何行打印到结果文件。另外我应该添加到小写,因为这无关紧要。

示例: 大文件行:'{"data": "SN@Whosebug"}'

小文件行(任意):'@Whosebug' -> 打印大行。

这应该会在结果文件中打印出大行。请注意,小文件行将是大文件行的子集。这是我的目标,因为我不是在寻找匹配行,而是在寻找包含小文件数据的行,然后将这些大文件行保存为结果。

我尝试使用嵌套的 for 循环来执行此操作,但它没有打印任何结果。此外,我将文件加载为列表,希望减少时间,因为这些行将存储在内存中。我也无法对这些行进行排序,因为行不统一。

results = open("results.txt", "w")

small = []
large = []


for line in open('small.txt'):
    small.append(line)
    
for line in open('large.txt'):
    large.append(line)
    
for j in large:
    for i in small:
        if i in j:
            results.write(j + '\n')

如果我能做些什么来澄清我的问题,请告诉我,很抱歉这是我的第一个问题post,我希望以后能写出更好的问题。

如果您的样本数据表明您正在使用的实际数据,那么只需比较它们的小写字母即可:

# ... your I/O code

for j in large:
    for i in small:
        if i.lower() in j.lower():
            results.write(j + '\n')

请注意 .lower() 调用,这是我对您的代码所做的唯一修改。

如果这仍然不起作用,请post从每个文件中再添加几行以帮助我们评估。

无需将所有大文件同时读入内存。在进行 in 测试之前,您几乎肯定还希望从 small 文件的行中删除换行符(您可以为此使用 strip,去除任何前导和尾随空格)。

查看您的示例字符串,似乎您还需要进行不区分大小写的比较,因此在此处使用 lower() 将两者都转换为小写,然后再进行比较。

即使小文件中有多行匹配,您也可能只写一次每个输出行,因此 break。另请注意,如果您没有从大文件的输入行中删除它,则不需要编写额外的换行符。

把这些放在一起会得到这样的东西。

small = []

with open('small.txt') as f:
    for line in f:
        small.append(line.strip().lower())

with open('results.txt', 'w') as fout:
    with open('large.txt') as fin:
        for line in fin:
            for i in small:
                if i in line.lower():
                    fout.write(line)
                    break  # breaks from inner loop only (for i in small)

您还可以通过执行以下操作来简化“文件到行列表”步骤:

small = open('small.txt', 'r').readlines()
large = open('large.txt', 'r').readlines()

然后迭代如下:

with open("results.txt", "w") as results:
    for j in large:
        for i in small:
            if i.lower() in j.lower():
                results.write(j)

祝你好运

问题可能是—— 正如您所说,尝试将所有 90000 行与另一个文件中的 1,200,000 行中的每一行进行匹配的幼稚方法,甚至在此期间执行 'contains' (Python in ) 运算符在大文件行上,将导致令人望而却步的处理成本。您基本上是在谈论 M x N x O 操作,M 是大文件大小,N 是小文件,O 是大文件中行的平均长度(减去小文件中行的平均长度)——这些是1 万亿次操作开始 - 如果计算机在 GHz 范围内运行,如果小文件可以放入内存,则在几个小时内是可行的。

更聪明的替代方法是对大文件中的每一行使用与位置无关的散列。与位置无关的散列可能很复杂——但是可以在常数时间 O(1) 内将较宽行中所有可能的单词子集的一些散列与包含较小文件中所有 90.000 行的字典进行匹配——对每个行执行一次可以在线性时间内完成 1,200,000 行 - 只需使用字符串规范化和 python 字典即可将此搜索从数小时或数天减少到几秒钟。

最后,这应该是所有需要的代码:

import re

def normalize(text):
    text = text.strip().lower()
    # strip punctuation
    text = re.sub('[^\w\ \d]', ' ', text)
    words = tuple(text.split())
    return words

def normalized_subsets(text):
    words = normalize(text)
    combinations = set()
    for i in range(len(words)):
        for j in range(i + 1, len(words) + 1):
            combinations.add(words[i: j])
    return combinations


def main():
    small = {normalize(line): linenum for
             linenum, line in enumerate(open("small.txt")) if line.strip()}
    with open("large.txt") as large,  open("results.txt", "w") as results:
        for line_num, line in large:
            for combination in combinations(line):
                if combination in small:
                    results.write(f"{linenum} {small[combination]} {line}")

main()

因此,虽然您仍会在此代码中看到嵌套的 for 循环,但嵌套版本仅循环遍历每行中可能的单词子集 - 对于包含 30 个单词的行,将少于 500 个组合 - 我们进行 500 次比较以匹配 90000 个较小文件字典中的任何这些词子组,而不是 90.000 次比较。

所以,它最终仍然是一个二次算法,但应该会快得多-(对于问题中的样本行,去除标点符号后,它会尝试匹配[=14=中的每个元素]

{('data',),
 ('data', 'sn'),
 ('data', 'sn', 'Whosebug'),
 ('sn',),
 ('sn', 'Whosebug'),
 ('Whosebug',)}

这只是 6 次比较(向下进行 90000 行的线性搜索)

(为了获得更大的价值,此代码还记录了结果文件中匹配行开头的大文件和小文件中的行号)