Python 具有两个以上文件名的difflib

Python 具有两个以上文件名的difflib,python,regex,difflib,Python,Regex,Difflib,我尝试比较几个文件名。以下是一些例子: files = ['FilePrefix10.jpg', 'FilePrefix11.jpg', 'FilePrefix21.jpg', 'FilePrefixOoufhgonstdobgfohj#lwghkoph[]**^.jpg'] 我需要做的是从每个文件名中提取“FilePrefix”,它会根据目录的不同而变化。我有几个文件夹包含许多jpg。在每个文件夹中,每个jpg都有一个与该目录中其他jpg相同的文件前缀。我需要jpg文件名的变量部分。我无法提

我尝试比较几个文件名。以下是一些例子:

files = ['FilePrefix10.jpg', 'FilePrefix11.jpg', 'FilePrefix21.jpg', 'FilePrefixOoufhgonstdobgfohj#lwghkoph[]**^.jpg']
我需要做的是从每个文件名中提取“FilePrefix”,它会根据目录的不同而变化。我有几个文件夹包含许多jpg。在每个文件夹中,每个jpg都有一个与该目录中其他jpg相同的文件前缀。我需要jpg文件名的变量部分。我无法提前预测FilePrefix将是什么

我的想法是使用difflib(在Python中)比较两个文件名,然后以这种方式提取FilePrefix(以及随后的变量部分)。我遇到了以下问题:

>>>> comp1 = SequenceMatcher(None, files[0], files[1])
>>>> comp1.get_matching_blocks()
[Match(a=0, b=0, size=11), Match(a=12, b=12, size=4), Match(a=16, b=16, size=0)]

>>>> comp1 = SequenceMatcher(None, files[1], files[2])
>>>> comp1.get_matching_blocks()
[Match(a=0, b=0, size=10), Match(a=11, b=11, size=5), Match(a=16, b=16, size=0)]
如您所见,第一个
大小
不匹配。它混淆了十和数字的位置,使我很难匹配两个以上文件之间的差异。是否有正确的方法在目录中的所有文件中找到最小
大小
?或者,是否有更好的方法提取FilePrefix

谢谢。

这不是“混淆十和数字的位置”,而是在第一次匹配中,十的位置没有什么不同,所以它被认为是匹配前缀的一部分

对于您的用例,似乎有一个非常简单的解决方案来解决这种歧义:只需匹配所有相邻的对,并取最小值。像这样:

def prefix(x, y):
    comp = SequenceMatcher(None, x, y)
    matches = comp.get_matching_blocks()
    prefix_match = matches[0]
    prefix_size = prefix_match[2]
    return prefix_size

pairs = zip(files, files[1:])
matches = (prefix(x, y) for x, y in pairs)
prefixlen = min(matches)
prefix = files[0][:prefixlen]
prefix
函数非常简单,除了一件事:我让它使用两个值的一个元组,而不是两个参数,只是为了更容易使用
map
调用。我使用了
[2]
而不是
.size
,因为在2.7
difflib
中有一个恼人的错误,第二次调用
get\u matching_blocks
可能会返回一个
元组,而不是
namedtuple
。这不会影响代码的原样,但如果添加一些调试
print
s,它将中断

现在,
pairs
是所有相邻名称对的列表,由
names
names[1://code>共同创建。(如果这不清楚,
print(zip(名称,名称[1:])
。如果您使用的是Python 3.x,则需要
print(list(zip(名称,名称[1:]))
,因为
zip
返回一个惰性迭代器而不是可打印列表。)

现在我们只想对每一对调用
prefix
,然后取我们得到的最小值。这就是我们要做的。(我给它传递了一个,一开始这可能是一个棘手的概念,但如果你只是把它看作一个不构建列表的函数,它非常简单。)

显然,您可以将其压缩为两行或三行,同时仍保持可读性:

prefixlen = min(SequenceMatcher(None, x, y).get_matching_blocks()[0][2] 
                for x, y in zip(files, files[1:]))
prefix = files[0][:prefixlen]

然而,值得考虑的是,
SequenceMatcher
在这里可能有点过头了。它在任何地方寻找最长的匹配,而不仅仅是最长的前缀匹配,这意味着它在字符串长度上本质上是O(N^3),而只需要O(NM)其中M是结果的长度。另外,不难想象会有一个后缀比最长前缀长,所以它会返回错误的结果

那么,为什么不直接手动操作呢

def prefixes(name):
    while name:
        yield name
        name = name[:-1]

def maxprefix(names):
    first, names = names[0], names[1:]
    for prefix in prefixes(first):
        if all(name.startswith(prefix) for name in names):
            return prefix
前缀(first)
只提供
'FilePrefix10.jpg'
'FilePrefix10.jp',
'FilePrefix10.j
,等等,一直到
'F'。所以我们只需循环这些名称,检查每个名称是否也是所有其他名称的前缀,然后返回第一个名称


通过逐个字符而不是逐个前缀地思考,您可以更快地做到这一点:

def maxprefix(names):
    for i, letters in enumerate(zip(*names)):
        if len(set(letters)) > 1:
            return names[0][:i]
这里,我们只是检查第一个字符在所有名称中是否相同,然后检查第二个字符在所有名称中是否相同,依此类推。一旦我们找到一个失败的字符,前缀就是所有字符(从任何名称中)

zip
将名称列表重新组织为元组列表,其中第一个元组是每个名称的第一个字符,第二个元组是每个名称的第二个字符,依此类推。也就是说,
[('F','F','F','F'),('i','i','i'),…]

枚举
只给我们索引和值。因此,不是得到
('F','F','F','F')
而是得到
0,('F',F','F')
。我们需要在最后一步中使用该索引


现在,为了检查
('F'、'F'、'F'、'F'、'F')
是否都相同,我只把它们放在
集合中。如果它们都相同,集合将只有一个元素-
{'F'}
,然后是
{'I'}
,等等。如果它们不相同,它将有多个元素-
{'1',2'}
-这就是为什么我们知道我们已经超过了前缀。

唯一确定的方法是检查所有文件名。所以只需遍历所有文件名,并在运行时检查保留的最大匹配字符串

您可以尝试以下方法:

files = ['FilePrefix10.jpg',
         'FilePrefix11.jpg',
         'FilePrefix21.jpg',
         'FilePrefixOoufhgonstdobgfohj#lwghkoph[]**^.jpg',
         'FileProtector354.jpg
         ]
prefix=files[0]
max = 0
for f in files:
    for c in range(0, len(prefix)):
        if prefix[:c] != f[:c]:
            prefix = f[:c-1]
            max = c - 1
print prefix, max

请原谅这个解决方案的“非Pythonicness”,但我希望算法对任何级别的程序员来说都是显而易见的。

@mh00h:你想先了解哪一个?你知道
映射
、理解、生成器和
所有的
是如何工作的吗?我现在正在消化它。你让我去做很多文档(我宁愿这样做,也不愿先在这里发布);请耐心等待,我会消化所有问题!!答案很棒。这里有很多东西需要学习。@mh00h:好的,很酷。我会尝试在答案中添加一些解释。一些未来搜索同一问题的人可能没有你那么勤奋。如果你发现任何不清楚的地方,请告诉我,以便我们改进它。(另外,如果它们中的任何一个实际上不起作用,请告诉我;在最初编写和测试它们之后,我对它们进行了几次编辑…)看起来像是
matches=map(前缀,成对)
在开始时抛出了一个错误,因为prefix需要两个变量。虽然不是一个完整的插入式代码块,但它能让人理解这一点。@mh00h:我认为您可能是在混合来自两个不同变量的代码。@mh00h