繁体   English   中英

无法在Windows上使用Python查找具有长名称的文件

[英]Unable to locate files with long names on Windows with Python

我需要在Windows中浏览具有长文件名的文件夹。

我尝试使用os.listdir() ,但它崩溃了很长的路径名,这很糟糕。

我尝试使用os.walk() ,但它忽略了长于~256的路径名,这更糟糕。

我尝试了这里描述的魔术解决方法,但它只适用于映射驱动器,而不适用于UNC路径名

下面是一个使用短路径名的示例,它显示UNC路径名不适用于魔术词诀。

>>> os.listdir('c:\\drivers')
['nusb3hub.cat', 'nusb3hub.inf', 'nusb3hub.sys', 'nusb3xhc.cat', 'nusb3xhc.inf', 'nusb3xhc.sys']
>>> os.listdir('\\\\Uni-hq-srv6\\router')
['2009-04-0210', '2010-11-0909', ... ]

>>> mw=u'\\\\?\\'
>>> os.listdir(mw+'c:\\drivers')
[u'nusb3hub.cat', u'nusb3hub.inf', u'nusb3hub.sys', u'nusb3xhc.cat', u'nusb3xhc.inf', u'nusb3xhc.sys']
>>> os.listdir(mw+'\\\\Uni-hq-srv6\\router')

Traceback (most recent call last):
  File "<pyshell#160>", line 1, in <module>
    os.listdir(mw+'\\\\Uni-hq-srv6\\router')
WindowsError: [Error 123] The filename, directory name, or volume label syntax is incorrect: u'\\\\?\\\\\\Uni-hq-srv6\\router\\*.*'

关于如何处理长路径名或使用unicode UNC路径名的任何想法?

编辑:

根据以下评论的建议,我创建了一些测试函数来比较Python 2.7和3.3,并在os.chdir之后添加了glob.globos.listdir的测试。

os.chdir并没有像预期的那样有所帮助(见本评论 )。

glob.glob是Python 3.3中唯一能够更好地工作的,但仅限于一种情况:使用魔术字和驱动器名称。

这是我使用的代码(它适用于2.7和3.3)。 我现在正在学习Python,我希望这些测试有意义:

from __future__ import print_function
import os, glob

mw = u'\\\\?\\'

def walk(root):
    n = 0
    for root, dirs, files in os.walk(root):
        n += len(files)
    return n

def walk_mw(root):
    n = 0
    for root, dirs, files in os.walk(mw + root):
        n += len(files)
    return n

def listdir(root):
    try:
        folders = [f for f in os.listdir(root) if os.path.isdir(os.path.join(root, f))]
        files = [f for f in os.listdir(root) if os.path.isfile(os.path.join(root, f))]
        n = len(files)
        for f in folders:
            n += listdir(os.path.join(root, f))
        return n
    except:
        return 'Crash'

def listdir_mw(root):
    if not root.startswith(mw):
        root = mw + root
    try:
        folders = [f for f in os.listdir(root) if os.path.isdir(os.path.join(root, f))]
        files = [f for f in os.listdir(root) if os.path.isfile(os.path.join(root, f))]
        n = len(files)
        for f in folders:
            n += listdir_mw(os.path.join(root, f))
        return n
    except:
        return 'Crash'

def listdir_cd(root):
    try:
        os.chdir(root)
        folders = [f for f in os.listdir('.') if os.path.isdir(os.path.join(f))]
        files = [f for f in os.listdir('.') if os.path.isfile(os.path.join(f))]
        n = len(files)
        for f in folders:
            n += listdir_cd(f)
        return n
    except:
        return 'Crash'

def listdir_mw_cd(root):
    if not root.startswith(mw):
        root = mw + root
    try:
        os.chdir(root)
        folders = [f for f in os.listdir('.') if os.path.isdir(os.path.join(f))]
        files = [f for f in os.listdir('.') if os.path.isfile(os.path.join(f))]
        n = len(files)
        for f in folders:
            n += listdir_cd(f) # the magic word can only be added the first time
        return n
    except:
        return 'Crash'

def glb(root):
    folders = [f for f in glob.glob(root + '\\*') if os.path.isdir(os.path.join(root, f))]
    files = [f for f in glob.glob(root + '\\*') if os.path.isfile(os.path.join(root, f))]
    n = len(files)
    for f in folders:
        n += glb(os.path.join(root, f))
    return n

def glb_mw(root):
    if not root.startswith(mw):
        root = mw + root
    folders = [f for f in glob.glob(root + '\\*') if os.path.isdir(os.path.join(root, f))]
    files = [f for f in glob.glob(root + '\\*') if os.path.isfile(os.path.join(root, f))]
    n = len(files)
    for f in folders:
        n += glb_mw(os.path.join(root, f))
    return n

def test():
    for txt1, root in [('drive ', r'C:\test'),
                    ('UNC   ', r'\\Uni-hq-srv6\router\test')]:
        for txt2, func in [('walk                    ', walk),
                           ('walk     magic word     ', walk_mw),
                           ('listdir                 ', listdir),
                           ('listdir  magic word     ', listdir_mw),
                           ('listdir              cd ', listdir_cd),
                           ('listdir  magic word  cd ', listdir_mw_cd),
                           ('glob                    ', glb),
                           ('glob     magic word     ', glb_mw)]:
            print(txt1, txt2, func(root))

test()

这是结果:

  • 数字8表示找到所有文件
  • 数字0意味着它甚至没有崩溃的尝试
  • 1到7之间的任何数字表示它在没有崩溃的情况下中途失败
  • Crash这个词意味着它崩溃了

-

Python 2.7
drive  walk                     5
drive  walk     magic word      8      * GOOD *
drive  listdir                  Crash
drive  listdir  magic word      8      * GOOD *
drive  listdir              cd  Crash
drive  listdir  magic word  cd  5
drive  glob                     5
drive  glob     magic word      0
UNC    walk                     6
UNC    walk     magic word      0
UNC    listdir                  5
UNC    listdir  magic word      Crash
UNC    listdir              cd  5
UNC    listdir  magic word  cd  Crash
UNC    glob                     5
UNC    glob     magic word      0

Python 3.3
drive  walk                     5
drive  walk     magic word      8      * GOOD *
drive  listdir                  Crash
drive  listdir  magic word      8      * GOOD *
drive  listdir              cd  Crash
drive  listdir  magic word  cd  5
drive  glob                     5
drive  glob     magic word      8      * GOOD *
UNC    walk                     6
UNC    walk     magic word      0
UNC    listdir                  5
UNC    listdir  magic word      Crash
UNC    listdir              cd  5
UNC    listdir  magic word  cd  Crash
UNC    glob                     5
UNC    glob     magic word      0

使用8.3回退来避免长路径名,在Win7资源管理器中浏览这似乎是Windows本身所做的,即每个长路径都有一个较短的'真实名称':

>>> long_unc="\\\\K53\\Users\\Tolan\\testing\\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\\xxxxxxxxxxxxxxxxxxxxxxxxdddddddddddddddddddddwgggggggggggggggggggggggggggggggggggxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\\esssssssssssssssssssssggggggggggggggggggggggggggggggggggggggggggggggeee"
>>> os.listdir(long_unc)
FileNotFoundError: [WinError 3]

但是你可以使用win32api(pywin32)来“构建”一个更短的版本,即

short_unc=win32api.GetShortPathName(win32api.GetShortPathName(win32api.GetShortPathName("\\\\K53\\Users\\Tolan\\testing\\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")+"\\xxxxxxxxxxxxxxxxxxxxxxxxdddddddddddddddddddddwgggggggggggggggggggggggggggggggggggxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") + "\\esssssssssssssssssssssggggggggggggggggggggggggggggggggggggggggggggggeee")
>>> print(short_unc)
\\K53\Users\Tolan\testing\XXXXXX~1\XXXXXX~1\ESSSSS~1
>>> import os
>>> os.listdir(short_unc)
['test.txt']

很明显,您可以将win32api.GetShortPathName调用折叠到您的dir探索中,而不是像我的示例中那样嵌套。 我已经完成了这样的3次调用,因为如果你已经有一个“太长”的路径,那么win32api.GetShortPathName也不会应付它,但你可以每个dir并且保持低于限制。

要在UNC路径上查找文件,魔术前缀是\\\\?\\UNC\\而不仅仅是\\\\?\\

参考: https//msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath

所以要访问//server/share/really/deep/path/etc/etc ,你需要

  1. 将其转换为unicode(使用unicode()构造函数)
  2. 添加魔术前缀( "\\\\?\\\\UNC\\" ),和
  3. 确保所有目录分隔符都是"\\" (请参阅os.path.normpath()

产生的unicode字符串: \\\\?\\UNC\\server\\share\\really\\deep\\path\\etc\\etc

我只尝试了一点(比@stenci少得多)但是使用Python 2.7它似乎与os.walk()一起工作,并且与os.listdir()失败。

警告:如果遍历的起始路径在MAX_PATH限制范围内,它只适用于os.walk(),并且起始路径中的所有子目录都不会超过限制。 这是因为os.walk()在顶层目录中使用os.listdir()。

在我之前的评论中,我说过不需要嵌套的GetShortPathName递归调用。 我发现大部分时间都不需要它,但偶尔它会崩溃。 我无法弄清楚什么时候,所以我做了这个功能已经运行了一段时间:

这是我现在使用的功能:

def short_name(name):
    try:
        return win32api.GetShortPathName(name)
    except win32api.error:
        dirname = os.path.dirname(name)
        basename = os.path.basename(name)
        short_dirname = win32api.GetShortPathName(dirname)
        return win32api.GetShortPathName(os.path.join(short_dirname, basename))

try:
    mtime = os.path.getmtime(name)
except FileNotFoundError:
    name = short_name(name)
    mtime = os.path.getmtime(name)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM