Python - 比较列表中的值(不是1:1匹配)

时间:2015-06-08 17:13:11

标签: python list

我有2个txt文件,其结构如下:

档案1

LINK1;FILENAME1
LINK2;FILENAME2
LINK3;FILENAME3

文件2

FILENAME1
FILENAME2
FILENAME3

我使用此代码打印" unique"两个文件中包含的行:

with open('1.txt', 'r') as f1, open('2.txt', 'r') as f2:
    a = f1.readlines()
    b = f2.readlines()

non_duplicates = [line for line in a if line not in b]
non_duplicates += [line for line in b if line not in a]

for i in range(1, len(non_duplicates)):
    print non_duplicates[i]

问题在于,它以这种方式打印两个文件的所有行,我想要做的是搜索FILENAME1是否在文件1的某一行(具有链接和文件名的那一行)并删除此行。

3 个答案:

答案 0 :(得分:3)

您需要首先加载2.txt中的所有行,然后过滤1.txt中包含前者行的行。使用setfrozenset来整理“黑名单”,以便每个not in平均在O(1)中运行。另请注意,f1f2已经可以迭代:

with open('2.txt', 'r') as f2:
    blacklist = frozenset(f2)

with open('1.txt', 'r') as f1:
    non_duplicates = [x.strip() for x in f1 if x.split(";")[1] not in blacklist]

答案 1 :(得分:0)

如果file2不是太大,则创建一组所有行,拆分file1行并检查第二个元素是否在行集中:

import  fileinput
import sys
with open("file2.txt") as f:
    lines = set(map(str.rstrip,f)) # itertools.imap python2
    for line in fileinput.input("file1.txt",inplace=True): 
        # if FILENAME1 etc.. is not in the line write the line
        if line.rstrip().split(";")[1] not in lines:
            sys.stdout.write(line)

文件1:

LINK1;FILENAME1
LINK2;FILENAME2
LINK3;FILENAME3
LINK1;FILENAME4
LINK2;FILENAME5
LINK3;FILENAME6

file2的:

FILENAME1
FILENAME2
FILENAME3

file1之后:

LINK1;FILENAME4
LINK2;FILENAME5
LINK3;FILENAME6
带有inplace的

fileinput.input更改原始文件。您不需要将这些行存储在列表中。

您还可以写入临时文件,为其写入唯一的行,并使用shutil.move替换原始文件:

from tempfile import NamedTemporaryFile
from shutil import move

with open("file2.txt") as f, open("file1.txt") as f2, NamedTemporaryFile(dir=".",delete=False) as out:
    lines = set(map(str.rstrip,f))
    for line in f2:
        if line.rstrip().split(";")[1] not in lines:
            out.write(line)

move(out.name,"file1.txt")

如果您的代码错误,您将使用临时文件丢失原始文件中的任何数据。

使用一个集来存储行意味着我们平均有0(1)个查找,存储列表中的所有行会给你一个二次方而不是一个线性解决方案,对于较大的文件会给你一个更高效的解。也没有必要将其他文件的所有行存储在带有readlines的列表中,因为您可以在迭代文件对象并进行查找时编写。

答案 2 :(得分:0)

除非文件太大,否则你可以打印file2.txt中的行(我称之为entries),其文件名部分列在file2.txt中像这样:

with open('file1.txt') as f1:
    entries = f1.read().splitlines()

with open('file2.txt') as f2:
    filenames_to_delete = f2.read().splitlines()

print [entry for entry in entries if entry.split(';')[1] not in filenames_to_delete]

如果file1.txt很大且file2.txt很小,那么你可以将file2.txt中的文件名完全加载到内存中,然后打开file1.txt并查看它,检查内存列表。< / p>

如果file1.txt很小而且file2.txt很大,你可以反过来这样做。

如果file1.txt和file2.txt都过大,那么如果知道两个文件的行都是按文件名排序的,那么可以编写一些精心设计的代码来利用该排序来完成任务而不加载内存中的整个文件,如this SO question中所示。但如果这不是一个问题,你最好把所有内容加载到内存中并保持简单。

P.S。一旦没有必要同时打开这两个文件,我们就避免它;我们打开一个文件,读取它,关闭它,然后重复下一个文件。就像那样,代码更容易遵循。