无法用Python在Windows上定位长名称的文件

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

我尝试使用os.listdir() ,但它与长path名崩溃,这是不好的。

我尝试使用os.walk() ,但它忽略了比〜256更长的path名,这更糟糕。

我尝试了这里描述的魔术字替代方法,但是它只能用于映射驱动器,而不能用于UNCpath名 。

下面是一个简短path名称的例子,它显示了UNCpath名不适用于魔术字技巧。

 >>> 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\\*.*' 

任何想法如何处理长path名或unicode UNCpath名?

编辑:

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

os.chdir没有像预期的那样帮助(看这个评论 )。

glob.glob是Python 3.3中唯一一个更好的工具,但只有一个条件:使用魔术字和驱动器名称。

这是我使用的代码(它在2.7和3.3都可以)。 我正在学习Python,我希望这些testing是有意义的:

 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调用放到你的目录中,而不是象我的例子那样嵌套。 我已经做了3次调用,因为如果你已经有了一个“太长”的路径,那么win32api.GetShortPathName也不能应付,但是你可以按照目录来做,并且保持在极限之下。

要在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)