Python文件扩展名

5
如果一个文件夹同时包含“.m”和“.xml”文件,我希望脚本能够找到它们(目前没有这样做,而是转到“else”语句)。给定的参数应该在文件夹中查找所有文件。
python script.py --dir C:\\path\\path\\*.* #This should take all files (doesn't matter what type ex 'm', 'xml' 'txt' etc.).

如果用户只想获取 XML 格式的文件,他可以输入“*.xml”,对于 '.m' 文件同理。需要注意的是,如果用户只想获取“XML”或“m”格式的文件,脚本也能找到。
def main(argv):
    args = argumentParser(argv)
    if args.dirname.endswith('.m'):
        overrideM(args)
    elif args.dirname.endswith('.xml'):
        xmlOverride(args)
    elif args.dirname.endswith(('.m', '.xml')): #Can I do like this?
        #Here I want to run both of my function.
        overrideM()
        xmlOverride()
    else:
        print "Error can't find files"

我的“m”函数(部分内容)

def overrideM(args):
    for fileName in glob.glob(args.dirname):
        print fileName
        with open(fileName, 'r') as searchFile:
            my_files_content = searchFile.read()
        #...rest of my code

我的“XML”函数(其中一小部分)
def xmlOverride(args):
    for fileName in glob.glob(args.dirname):
        print fileName
        with open(fileName, 'r') as searchFile:
            my_files_content = searchFile.read()
        #...rest of my code

@PadraicCunningham 好吧,它不会这样做,相反它会给我“错误:找不到文件”。 - gants
实际上同时运行两者是不行的,args是什么?它怎么可能以两个不同的扩展名结尾呢? - Padraic Cunningham
请将参数 args 记录在某个地方,以便清晰可见。print(args) - Rudziankoŭ
@Rudziankoŭ 嗯,通过执行“print args”,它会给我返回 C:\path\path*.*。所以也许代码不理解我想要检查所有文件的意思? - gants
@gants,你的逻辑不可能起作用。如果args是一个字符串(它必须是这样,否则你的代码会出错),那么它不可能有两个不同的扩展名。如果你想给用户提供多个扩展名的选项,那么你需要获取一个扩展名元组并对其进行迭代。一个字符串怎么可能以“.m”和“.xml”结尾呢? - Padraic Cunningham
显示剩余2条评论
3个回答

1

elif args.dirname.endswith(('.m', '.xml')) 绝对行不通,如果args是一个字符串,它必须是这样的,否则你的代码会出错,那么它不可能有两个不同的扩展名,如果用户想要选择两个扩展名,你需要获取一个扩展名元组,类似于:

def main(argv):
    # make argumentParser return a tuple
    args = argumentParser(argv)
    if sorted(args) == ('.m', '.xml'):
        overrideM()
        xmlOverride()

更好的选择是使用一个通用函数,该函数接受一个文件扩展名并迭代args传入该扩展名:
def main(argv):
    args = argumentParser(argv)
    for ext in args:
        generic_search(ext)

如果您想匹配字符串以同时以 .m.xml 结尾,那么您无法使用 args.dirname.endswith(('.m', '.xml'))。我建议将路径和要搜索的扩展名作为单独的参数,然后可以分别对每个进行 glob 或使用 str.endswith 与 os.listdir 列出文件。

基本思路如下:

from argparse import ArgumentParser
import os

parser = ArgumentParser()
parser.add_argument("path")
parser.add_argument('ext', nargs='*')

args = parser.parse_args()
path = args.path
exts = args.ext

# what your glob is doing
for f in os.listdir(path):
    if f.endswith(tuple(exts)):
        with open(os.path.join(path, f)) as fle:
            print(fle.name)
            # do whatever

如果您允许用户搜索多个文件,除非每个函数都有非常特定的操作,否则最好使用endswith并在目录上执行一次遍历。您还可以与glob结合使用,以便在路径中搜索所有子目录:
from argparse import ArgumentParser
import os
from glob import iglob

parser = ArgumentParser()
parser.add_argument("path")
parser.add_argument('ext', nargs='*')

args = parser.parse_args()
path = args.path
exts = args.ext

for f in chain.from_iterable([iglob(path+"/*"), iglob(path+"/**/*")]):
    if f.endswith(tuple(exts)):
        with open(os.path.join(path, f)) as fle:
            print(fle.name)

再次运行时,它将适用于多个文件扩展名,并对目录进行一次遍历。glob适用于单个匹配或可能有几个匹配,但如果您有多个扩展名,则使用listdir和filer with endswith更加合理。

如果您真的想为每个扩展名使用不同的逻辑,可以提取扩展名并使用字典调用相应的函数,将扩展名映射到函数:

from argparse import ArgumentParser
import os
from glob import iglob

def xml(f):
    print(f)

def m(f):
    print(f)

def text(f):
   print(f)

mapped = {"m":m, "xml":xml, "text":text}

parser = ArgumentParser()
parser.add_argument("path")
parser.add_argument('ext', nargs='*')

args = parser.parse_args()
path = args.path
exts = args.ext


for f in chain.from_iterable([iglob(path + "/*"), iglob(path + "/**/*")]):
    ext = f.rsplit(".", 1)
    if len(ext) == 2 and ext[1] in mapped:
        mapped[ext[1]](f)

字典查找是O(1),因此除了简洁外,它还非常高效。
示例输出:
 $ python 3_payg.py  /home/padraic  .xml 
/home/padraic/sitemap.xml
/home/padraic/yacy/build.xml
/home/padraic/graphviz-master/graphviz.appdata.xml

现在最多只会有3个扩展名,所以我认为glob在这里是一个明智的选择?我也正在尝试您和其他人的建议,希望我能够解决它 :) - gants
@gants,你可以使用glob进行三次操作,或者使用os.listdir逻辑进行一次操作。实际上,如果只有一个扩展名,那么使用glob是最理想的选择,否则你需要检查每个文件*扩展名的数量。使用str.endswidth可以进行一次操作,尽管如果没有匹配项,则可能需要进行相同数量的检查,但这种情况不太可能发生。当你有多个扩展名时,listdir逻辑应该是最快的方法。 - Padraic Cunningham

0

1)我和 MS Windows 没有太多的交情,但它与 UNIX 相似,你应该让命令提示符知道你想要的是命令的产出而不仅仅是命令本身。在 UNIX 中,它看起来像:

 python script.py $(ls home/) 

2) 第二个问题是你的参数将会是一个字符串,例如:"file.xml, file.m, file.txt",并且这个字符串以.txt结尾。因此,在需要扩展名的文件中,你会错过这一点。

为了使其正常工作,你必须遍历文件列表。

files = args.split(",")
for file in files:
     main(file)

0

至少在Bash中,*通配符是由shell而不是正在运行的进程扩展的。 sys.argv将包含许多文件。 在Windows上可能会有所不同。

通过像这样传递要搜索的目录(我跳过了argparse,但您应该继续使用它),您将拥有更好的控制。

myscript.py c:\a\directory

使用glob或iglob来检索文件。
import sys
import glob
import os
import itertools

# retreive your directory to expore, from the arguments
# just for the example, better use argparse
dir = sys.argv[1]

# At this point, you might wish to use os.path.abspath & friends
# to have a normalized directory, and check its existence with
# os.path.exists.

# The patterns to search for:
patterns = "*.xml", "*.m"

# a generator expression yielding things like 
# "c:\\my\\dir\\*.xml" and "c:\\my\\dir\\*.m"
joint_patterns  = (os.path.join(dir, pt) for pt in patterns)

# Glob the joint patterns into a super-generator:
files = itertools.chain.from_iterable( glob.iglob(pt) for pt in joint_patterns )

# Show the result. the '*' is there to evaluate the super generator
# or else it'd print something like 
# "<itertools.chain object at 0x7fd92ac9efd0>"
print(*files)

这种解决方案的缺点是您无法从shell的通配符功能中受益。

编辑

在您的情况下,您不需要使用chain创建超级生成器。您只需为每个模式检索glob迭代器:

import sys
import glob
import os
import itertools

# retreive your directory to expore, from the arguments
# just for the example, better use argparse
dir = sys.argv[1]
# The patterns to search for:
patterns = "*.xml", "*.m"

# a LIST  holding things like 
# "c:\\my\\dir\\*.xml" and "c:\\my\\dir\\*.m"
joint_patterns = [os.path.join(dir, pt) for pt in patterns]

# Glob iterators.
globs = [glob.iglob(pt) for pt in joint_patterns]

for xml_file_path in globs[0]:
    do_xml_stuff(xml_file_path)

for m_file_path in globs[1]:
    do_m_stuff(m_file_path)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接