在目录中搜索特定文件并复制其文件夹路径的Python代码
我刚开始学习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 个回答
根据你的评论,你的数据实际上是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
,而你又把反斜杠转换成斜杠,那你就得不到想要的结果。
但希望我已经给了你足够的信息来构建你真正想要的东西。
第一步是查看一个文件夹里的所有文件。这可以用 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 字符串中需要转义反斜杠。)