os.walk() Python:目录结构的XML表示,递归。

6

我正在尝试使用os.walk()函数生成一个目录结构的XML表示。但是,我发现会出现很多重复的数据。在xml文件的前面部分,它正确地将目录放置在彼此之间,并将文件放置在正确的位置;然而,在正确完成这一部分之后,它却继续着错误的遍历。我不太确定原因在哪里……

以下是我的代码:

def dirToXML(self,directory):
        curdir = os.getcwd()
        os.chdir(directory)
        xmlOutput=""

        tree = os.walk(directory)
        for root, dirs, files in tree:
            pathName = string.split(directory, os.sep)
            xmlOutput+="<dir><name><![CDATA["+pathName.pop()+"]]></name>"
            if len(files)>0:
                xmlOutput+=self.fileToXML(files)
            for subdir in dirs:
                xmlOutput+=self.dirToXML(os.path.join(root,subdir))
            xmlOutput+="</dir>"

        os.chdir(curdir)
        return xmlOutput  

文件fileToXML只是简单地解析了列表,所以不需要担心那个。
目录结构很简单:
images/
images/testing.xml
images/structure.xml
images/Hellos
images/Goodbyes
images/Goodbyes/foo
images/Goodbyes/bar
images/Goodbyes/square

最终生成的XML文件如下:

<structure>
<dir>
<name>images</name>
  <files>
    <file>
      <name>structure.xml</name>
    </file>
    <file>
      <name>testing.xml</name>
    </file>
  </files>
  <dir>
    <name>Hellos</name>
  </dir>
  <dir>
    <name>Goodbyes</name>
    <dir>
      <name>foo</name>
    </dir>
    <dir>
      <name>bar</name>
    </dir>
    <dir>
      <name>square</name>
    </dir>
  </dir>
  <dir>
    <name>foo</name>
  </dir>
  <dir>
    <name>bar</name>
  </dir>
  <dir>
      <name>square</name>
    </dir>
  </dir>
  <dir>
    <name>Hellos</name>
  </dir>
  <dir>
    <name>Goodbyes</name>
    <dir>
      <name>foo</name>
    </dir>
    <dir>
      <name>bar</name>
    </dir>
    <dir>
      <name>square</name>
    </dir>
  </dir>
  <dir>
    <name>foo</name>
  </dir>
  <dir>
    <name>bar</name>
  </dir>
  <dir>
    <name>square</name>
  </dir>
</structure>

任何帮助都将不胜感激!
3个回答

9

我建议不要使用os.walk(),因为你需要做很多工作来处理它的输出。相反,只需使用一个递归函数,该函数使用os.listdir()os.path.join()os.path.isdir()等。

import os
from xml.sax.saxutils import escape as xml_escape

def DirAsXML(path):
    result = '<dir>\n<name>%s</name>\n' % xml_escape(os.path.basename(path))
    dirs = []
    files = []
    for item in os.listdir(path):
        itempath = os.path.join(path, item)
        if os.path.isdir(itempath):
            dirs.append(item)
        elif os.path.isfile(itempath):
            files.append(item)
    if files:
        result += '  <files>\n' \
            + '\n'.join('    <file>\n      <name>%s</name>\n    </file>'
            % xml_escape(f) for f in files) + '\n  </files>\n'
    if dirs:
        for d in dirs:
            x = DirAsXML(os.path.join(path, d))
            result += '\n'.join('  ' + line for line in x.split('\n'))
    result += '</dir>'
    return result

if __name__ == '__main__':
    print '<structure>\n' + DirAsXML(os.getcwd()) + '\n</structure>'

个人建议采用更简洁的XML模式,将名称放在属性中,并且取消<files>组:

import os
from xml.sax.saxutils import quoteattr as xml_quoteattr

def DirAsLessXML(path):
    result = '<dir name=%s>\n' % xml_quoteattr(os.path.basename(path))
    for item in os.listdir(path):
        itempath = os.path.join(path, item)
        if os.path.isdir(itempath):
            result += '\n'.join('  ' + line for line in 
                DirAsLessXML(os.path.join(path, item)).split('\n'))
        elif os.path.isfile(itempath):
            result += '  <file name=%s />\n' % xml_quoteattr(item)
    result += '</dir>'
    return result

if __name__ == '__main__':
    print '<structure>\n' + DirAsLessXML(os.getcwd()) + '\n</structure>'

这将产生以下输出:
<structure>
<dir name="local">
  <dir name=".hg">
    <file name="00changelog.i" />
    <file name="branch" />
    <file name="branch.cache" />
    <file name="dirstate" />
    <file name="hgrc" />
    <file name="requires" />
    <dir name="store">
      <file name="00changelog.i" />

如果 os.walk() 的工作方式更像 expat 的回调机制,你会更容易地处理它。


...而且你已经得出了相同的结论。不确定为什么我没有收到“1个新答案”的警告。-_-; - Mike DeSimone
哈哈,没关系...我会给你的!谢谢你的帮助 :)! - Parris
嗨 @MikeDeSimone,我对这个for循环的用法感到好奇: result += '\n'.join(' ' + line for line in DirAsLessXML(os.path.join(path, item)).split('\n'))我已经搜索了相关文档和例子,但是没有找到,你有任何链接吗? - Francis
1
@Francis 这不是技术上的“for循环”。在Python中,它被称为“推导式”,在这种情况下是“生成器”。在Python文档中搜索这些术语应该会给你很多信息。基本上,形式为expression for loop-var in sequence [ if condition ]的东西是一个“生成器”,它像带有yield语句的函数(生成器的基本形式)一样运作。 - Mike DeSimone
因此,在您的情况下,“DirAsLessXML(os.path.join(path,item))。split('\ n')”被调用,产生一系列行。这些行中的每一行按顺序分配给“line”,并用于评估表达式“' '+ line”,这些表达式的结果被作为序列馈送到join函数中。总体效果是向从DirAsLessXML函数输出的所有行添加缩进。 - Mike DeSimone

6

移除这两行代码:

        for subdir in dirs:
            xmlOutput+=self.dirToXML(os.path.join(root,subdir))

您正在递归到子目录中,但这是多余的,因为os.walk本身已经递归了。


那对我来说并不起作用...有没有办法在Python中只列出文件和目录...我需要XML文件更像树形结构。 - Parris

0

我曾尝试使用os.walk,但发现它无法处理我想在xml中创建的递归树结构。我修改了代码如下,最终得到了所需的结果:

def dirToXML(self,directory):
        curdir = os.getcwd()
        os.chdir(directory)
        xmlOutput=""

        pathName = string.split(directory, os.sep)
        xmlOutput+="<dir><name><![CDATA["+pathName.pop()+"]]></name>"
        for item in os.listdir(directory):
            if os.path.isfile(os.path.join(directory, item)):
                xmlOutput+="<file><name><![CDATA["+item+"]]></name></file>"
            else :
                xmlOutput+=self.dirToXML(os.path.join(directory,item))
        xmlOutput+="</dir>"

        os.chdir(curdir)
        return xmlOutput    

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