在目录中搜索特定文件并复制其文件夹路径的Python代码

-3 投票
2 回答
979 浏览
提问于 2025-04-17 19:41

我刚开始学习Python,每天都在进步。

我想做一个自动化脚本,帮助我完成工作。

我有一个特定的文件夹,里面有一些文件。我不想让我的工具只针对某个文件,因为我想重复使用这个脚本,所以我希望它能在文件中查找一个特定的标题。

比如说,文件中有一行是“paths”,下面有很多路径,比如“file path=某个路径”。

我希望我的脚本能去我指定的目录,找一个包含这个标题“paths”的文件,然后复制其中的一条路径(它们就在下面),就这样。

然后我会用这个路径来下载文件等等,但这部分我已经完成了。我只是还不知道怎么在给定的文件夹中查找特定的字符串,并复制我想要的字符串。

假设我有一个文件夹(C:\Folder),里面有三个文件(1、2、3)。

我想找一个包含“paths”这种模式的文件,并复制下面的路径,或者至少复制其中的一条。

这些路径会像这样:“file path=C:\somepath”。所以文件内容大概是:

blabla

blabla

paths

file path=C:\somepath

file path=C:\somepath2

blabla

我想复制C:\somepath,并把它用在我的工作中。

非常感谢所有帮助我的人,这对我来说非常重要。

2 个回答

-1

根据你的评论,你的数据实际上是XML格式的,你想要的是第一个paths节点中每个(或者第一个)file节点的path属性。

其实,这样写起来简单多了,而且在处理XML时也更可靠。

比如,这些可能都是有效的file节点:

<file path="C:\Foo\Bar" />
<file path="C:\Baz\Qux"/>
    <file path="C:\Foo\Bar" />
<file path="C:\Spam\Eggs\" alt="other attribute cruft" />
<file alt="other attribute cruft" path="C:\Spam\Eggs\" />
<file path="C:\Spam\Spam\"></file>

你甚至可能会看到这些,不管它们是否合法:

<file path='C:\Eggs\"Spam Spam Spam"\"Spammity Spam"'/>

你不想用普通文本去处理所有这些可能性。如果你不处理所有这些情况,墨菲定律会保证你最终会遇到一个你没有处理的文件。

有很多不同的XML解析器,甚至在标准库中也有,但我觉得最简单的是ElementTree。所以:

import os
import os.path
import xml.etree.ElementTree as ET

filepaths = {}
for filename in os.listdir(directory):
    try:
        doc = ET.parse(os.path.join(directory, filename))
        paths = doc.find('paths')
        filepaths[filename] = [f.attrib['path'] for f in paths.findall('file')]
    except Exception as e:
        # You may want to log something, treat different exceptions differently, etc.
        pass

你应该很容易就能搞明白怎么改成处理所有的paths节点,而不是只处理第一个,或者处理第一个paths下的file节点,而不是所有的,或者处理第一个有path属性的file节点,等等。

如果你使用的是Python 2.x,并且文件非常大,这可能会有点慢。不过你可以通过明确使用cElementTree来解决这个问题。这样做很常见:

try:
    import xml.etree.cElementTree as ET
except ImportError:
    import xml.etree.ElementTree as ET

这会在可能的情况下给你快速的"C"实现,如果不行就给你慢的实现,适用于CPython 2.5+(包括3.x,两个版本合并了),PyPy等。


与此同时,从其他评论中,你提到了一些在你原始帖子中没有提到的额外需求:

我只需要把反斜杠变成斜杠

这很简单。只需对每个路径s调用s.replace('\\', '/')即可。

不过,这样做有点奇怪。反向操作是很常见的(这甚至是标准库中的一部分——os.path.normpath在POSIX系统上会保持斜杠不变,但在Windows上会把它们转换为反斜杠),但从Windows格式转换到POSIX格式通常是更大操作的一部分,比如构建URL……在这种情况下,你可能想用更高级的函数。

在文件中是 - 我想要的结果是 C:\folder\folder

在这里,听起来你想去掉任何结尾的反斜杠。再说一次,这样做有点奇怪,你可能实际上想做一些更高级的操作(比如os.path.dirname?),但很简单:s.rstrip('\\')

当然,这最后两个需求是矛盾的——如果你想要的结果是C:\folder\folder,而你又把反斜杠转换成斜杠,那你就得不到想要的结果。

但希望我已经给了你足够的信息来构建你真正想要的东西。

1

第一步是查看一个文件夹里的所有文件。这可以用 os.listdir 来实现。

接下来,你需要在一个循环中打开每个文件。到目前为止,我们有:

for filename in os.listdir(directory):
    with open(filename) as f:

那么,我们对每个文件该怎么做呢?有几种不同的选择——我们可以读取整个文件(或者用 mmap),然后使用 str.find 或正则表达式的方法来解析它,或者我们可以逐行读取并跟踪我们的状态,或者我们可以用 itertools 的函数来处理这些行,或者我们可以构建一个状态机并运行它,或者……

我认为对初学者来说,最简单的方法是手动逐行读取。但我们把它封装成一个函数吧。所以:

def parse_file(f):
    paths = []
    found_paths = False
    for line in f:
        line = line.strip()
        if not found_paths:
            if line == 'paths':
                found_paths = True
        else:
            if line.startswith('file path='):
                paths.append(line[len('file path='):])
            else:
                break
    return paths

paths = []
for filename in os.listdir(directory):
    with open(filename) as f:            
        paths.append(parse_file(f))

我怎么能在找到第一行后停止?

只需在读取第一行后使用 break。所以,不要这样做:

if line.startswith('file path='):
    paths.append(line[len('file path='):])
else:
    break

这样做:

if line.startswith('file path='):
    paths.append(line[len('file path='):])
break

我怎么能修复它,把路径放在字典的不同索引中,因为它把所有路径都放在了第一个字段里?

其实,现在你并不是在创建一个字典,而是在创建一个列表。

如果你想要一个字典,比如把每个文件映射到该文件中的文件路径列表,这很简单。不要这样做:

paths = []
for filename in os.listdir(directory):
    with open(filename) as f:
        paths.append(parse_file(f))

这样做:

paths = {}
for filename in os.listdir(directory):
    with open(filename) as f:            
        paths[filename] = parse_file(f)

不过,如果你只想要一个值,可能根本就不需要先创建一个列表。你只需要在找到一个路径时返回它,如果没有找到,就返回一个不可能是路径的东西(比如 None)。

我怎么修改有问题的路径。因为文件中的路径是以这种格式保存的——我只想复制 C:\folder\folder。

首先,我的代码根本找不到这种格式的东西。你要求找到像 file path=… 这样的行,所以我用了 startswith,但 <file path= 并不是以这个开头。所以你首先需要修改你检查的 startswith。同时,你还需要处理引号和尖括号。

到这个时候,单纯的文本处理可能不是正确的答案。这看起来像是 XML。解析 XML 文档最简单的方法是使用 XML 解析器,比如 xml.elementtree。如果这不是一个 XML 文档,而只是一个包含 XML 节点的行式文档,你仍然可以尝试将每一行作为 XML 文档解析,但使用 re 和合适的正则表达式可能更简单(例如,r'<file path="(.*?)"/>' 将只匹配引号之间的部分)。在不知道你实际输入文本是什么样的情况下,我无法给你更具体的建议。

最后,在你完成那一步之后,看起来你想要去掉目录路径末尾的反斜杠,这样即使文件里是 C:\folder\folder\,你也能得到 C:\folder\folder。你可以使用 os.path 中的函数来做到这一点,但如果你确定路径总是以 Windows 格式出现,简单地告诉它去掉任何末尾的反斜杠可能更简单,使用 rstrip('\\')。 (注意这里的双反斜杠,因为在 Python 字符串中需要转义反斜杠。)

撰写回答