1

该程序的目的是收集计算机上所有程序的列表,并根据用户输入找到正确的路径。因此,如果输入是Audition程序将返回 C:\Adobe\Audition CC 2014\Audition CC 2014.exe

我需要它在 txt 文件中搜索与用户输入的任何内容最相似的行。我的代码如下:

import os
import subprocess
import getpass
import sys
import difflib
from difflib import SequenceMatcher as SM



user = getpass.getuser()

print(os.getcwd())
exeFile = (os.getcwd() + "/paths/programpaths.txt")


def get_filepaths(directory):

    file_paths = []  # List which will store all of the full filepaths.
    exes = open(os.getcwd() + "/paths/programpaths.txt", "w+")
    # Walk the tree.
    for root, directories, files in os.walk(directory):
        for filename in files:
            # Join the two strings in order to form the full filepath.
            filepath = os.path.join(root, filename)
            file_paths.append(filepath)  # Add it to the list.
            if filepath.endswith('exe') and "ninstall" not in filepath and "$RECYCLE.BIN" not in filepath:
                files = filepath.encode('cp850', errors='replace').decode('cp850')
                #print(files + "\n")
                exes.write(files + "\n")
    return file_paths  # Self-explanatory. 



if not os.path.exists(exeFile):
    print("List compilation should only happen once")
    print()
    print("Compiling list of installed programs")
    print("This may take a while")
    exes = open(os.getcwd() + "/paths/programpaths.txt", "a+")
    full_file_pathsx64 = get_filepaths('C:\Program Files')
    full_file_pathsx86 = get_filepaths('C:\Program Files (x86)')
    full_file_pathsgames = get_filepaths('G:\\')

# Run the above function and store its results in a variable.   
print("List compilation should only happen once")
print()



print("Done!")
pinput = input()



for line in open(exeFile):
    prog = line.split("\\")[-1]
    sim = difflib.get_close_matches(pinput, [prog], 1)
    print(sim)

但是,这会为文件中的每一行打印一个空白括号“[]”,而不仅仅是给我我需要的那个。

我知道这是因为我告诉它对每一行都这样做,但我不知道如何解决这个问题。

4

6 回答 6

3

get_close_matches(…, 1)调用将返回一个空列表或恰好一个匹配项的列表。

你想用英语做的是:

  • 如果它有一个元素,打印它。
  • 否则,不要做任何事情。

直接翻译成python:

if sim:
    print(sim[0])

(你可以写else: pass“否则,不要做任何事情”,或者你可以不写任何东西。)


这解决了“不要打印[]每一行,只打印匹配项”。

但这引发了另一个问题:你实际上并没有得到任何匹配。

正如 poke 在评论中解释的那样, to 的第二个参数get_close_matches是要检查的可能性列表,但是您传递的值prog, 是单个字符串。

如果不清楚为什么它是单个字符串,请查看以下行:

prog = line.split("\\")[-1]

split将字符串放入一个较小字符串的列表中,然后您只使用最后一个字符串[-1]

如果你很好奇为什么没有出错:字符串本身就是一个字符串序列,每个字符对应一个字符串。因此,如果progis "abcde",那么您要求它将其['a', 'b', 'c', 'd', 'e']视为 5 种不同的可能性,这是一件非常合理的事情,它只是不可能匹配任何东西。


认为您在这里想要的可能只是传递一种可能性的列表:

sim = difflib.get_close_matches(pinput, [prog], 1)

或者,您可以建立一个包含所有可能性的大列表,然后一次搜索所有可能性,而不是一次搜索每个可能性:

progs = []
for line in open(exefile):
    progs.append(line.split("\\")[-1])
sim = difflib.get_close_matches(pinput, progs, 1)

但这只会在整个文件中获得 1 个匹配项,而不是每行 1 个可能的匹配项。如果您想要超过1 个总数,您可以这样做,但我不确定它在处理大量数字时效果如何。(你可以随时尝试看看。)


无论如何,希望你明白你真正想要什么,而不必猜测。:)

于 2015-04-17T23:25:38.977 回答
1

根据您现在发布的完整代码,这是我的解决方案,可能以最佳方式解决您的问题:

with open(exeFile) as f:
    programs = { path.rsplit('\\', 1)[-1].rstrip()[:-4].lower(): path.strip() for path in f }

sim = difflib.get_close_matches(pinput.lower(), programs.keys(), 1)
if sim:
    print(programs[sim[0]])

魔法发生在字典理解中。对于path文件中的每个,我们生成以下名称作为字典条目的键:

path.rsplit('\\', 1)[-1][:-4].lower()

所以假设一个像它这样的文件路径首先从右边用斜杠C:\Adobe\Audition CC 2014\Audition CC 2014.exe分割一次并取最后一个元素,所以我们会得到. 接下来,我们去掉空格,然后根据我们生成 exefile 的方式,我们知道这是文件名的一部分。所以我们有. 接下来,我们将其小写,以便我们有更好的可比性(因为区分大小写)。Audition CC 2014.exe.exeAudition CC 2014difflib

在比较中,我们只从字典的键中获取最接近的匹配项(这只是小写的程序名称)。我们将其与小写的用户输入进行比较。

一旦我们得到结果,我们就会打印属于匹配键的路径。这就是我们在上面构建字典的原因;否则我们将不得不再次搜索文件以找到完整路径。

于 2015-04-18T11:50:20.147 回答
0

尝试这个:

with open(exefile) as f:
    possibilities = [line.split("\\")[-1].rstrip("\n") for line in f]
print(difflib.get_close_matches(pinput, possibilities, 1, 0)[0])

这里的关键是 get_close_matches 根本不需要返回任何匹配项。从文档:

get_close_matches(单词,可能性[,n] [,截止])

cutoff (默认 0.6) 是 [0, 1] 范围内的浮点数。得分至少与单词相似的可能性将被忽略。

因此,如果您使用cutoff=0, get_close_matches 必须返回一些东西 - 最佳匹配。

于 2015-04-18T00:33:35.610 回答
0

了解您实际尝试做的事情总是很好

首先定义“最接近”的含义(通常使用字符串,这称为汉明距离)

def hamming_distance(s1,s2):
    #first elimate non-letters
    s1 = re.sub("[^a-zA-Z]","",s1)
    s2 = re.sub("[^a-zA-Z]","",s2)
    #the distance is the sum of all instance with differing letters in this case
    return sum(a!=b for a,b in izip_longest(s1,s2))

那么你只需要遍历文件并找到“最接近的匹配”

user_input = input("Enter String:")
print(min(open("file_of_strings.txt"),key=lambda x:hamming_distance(x,user_input)))

粗略的一旦你理解了魔法,你可能会使用 difflib 获得边际加速

于 2015-04-17T23:31:25.030 回答
0

如果您的exefile候选列表很小,则将它们预加载到内存中(在列表中),然后执行difflib.get_close_matches,例如:

import difflib

with open(exefile) as fin:
    progs = [line.rpartition('\\', line)[2] for line in fin]
    sim = difflib.get_close_matches(pinput, progs, 1)

另一方面,如果它非常大并且您无法支持 RAM,则使用 heapq:

import difflib, heapq

with open(exefile) as fin:
    progs = (line.rpartition('\\'), line)[2] for line in fin)
    sim = heapq.nlargest(1, progs, key=lambda L: difflib.SequenceMatcher(None, pinput, L))
于 2015-04-17T23:37:07.857 回答
0

我查找了 get_close_matches() 的文档。看来第二个参数应该是列表,所以你的代码可以写成:

for line in open(exefile):
    prog = line.split("\\")[-1]
    prog = [item for item in prog.split(' ')]
    sim = difflib.get_close_matches(pinput, prog, 1)
    print(sim) 
于 2015-04-17T23:37:27.857 回答