我需要在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.glob
和os.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()
结果如下:
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
,你需要
unicode()
构造函数) "\\?\\UNC\"
)和 "\"
(参见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)