无法使用 Python 在 Windows 上找到具有长名称的文件
Posted
技术标签:
【中文标题】无法使用 Python 在 Windows 上找到具有长名称的文件【英文标题】:Unable to locate files with long names on Windows with Python 【发布时间】:2013-08-25 18:25:47 【问题描述】:我需要在 Windows 中浏览具有长文件名的文件夹。
我尝试使用os.listdir()
,但它会因路径名太长而崩溃,这很糟糕。
我尝试使用 os.walk()
,但它会忽略超过 ~256 的路径名,这更糟。
我尝试了here 中描述的魔术字解决方法,但它仅适用于映射驱动器,不适用于UNC pathnames。
这是一个短路径名的示例,它表明 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 路径名吗?
编辑:
根据下面cmets的建议,我创建了一些测试函数来比较Python 2.7和3.3,并在os.chdir
之后添加了glob.glob
和os.listdir
的测试。
os.chdir
没有按预期提供帮助(请参阅此comment)。
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
【问题讨论】:
如何使用net use
并为 UNC 分配驱动器号?
@iTayb:谢谢。这很丑陋,但它应该可以工作。我需要扫描几个网络驱动器,所以我应该先net use
它们,然后再net use /delete
它们。
我很想知道这是在 py2 还是 py3 上,如果是 py2,py3 上的行为是否不同?
我很久没用Windows了,只是好奇:glob.glob("*")
对你有用吗?
这是一个很好的问题。您能否使标题更具描述性,以便将来搜索的人更有可能找到它?类似“无法在 Windows 上使用 Python 定位长名称的文件”
【参考方案1】:
在我之前的评论中,我说过 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)
【讨论】:
调用short_name(dirname)
而不是GetShortPahtName(dirname)
会是一个很好的改进吗?它将路径分解为所需的最小值,然后添加所有剩余的位......【参考方案2】:
要在 UNC 路径上定位文件,魔术前缀是 \\?\UNC\
而不仅仅是 \\?\
。
参考:https://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
所以要访问//server/share/really/deep/path/etc/etc
,您需要
-
将其转换为 unicode(使用
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()。
【讨论】:
一个重要的技巧:如果你使用 Python 2.x,你的长文件名必须是unicode
而不是 str
类型:u'\\\\?\\UNC\\'
【参考方案3】:
使用 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 也无法应对,但您可以按目录执行并保持在限制以下。
【讨论】:
看起来永远不需要嵌套调用。调用win32api.GetShortPathName('C:\\test\\123456789 123456789 123456789 123456789 123456789\\123456789 123456789 123456789 123456789 123456789')
返回C:\test\123456~1\123456~1
。以上是关于无法使用 Python 在 Windows 上找到具有长名称的文件的主要内容,如果未能解决你的问题,请参考以下文章
Python无法在工作计算机上找到已安装的模块-Windows OS
无法让 pydot 在 Windows 10 上找到 graphviz
Kivy 错误,[CRITICAL] [Text] 无法找到任何有价值的文本提供程序(python 3.6.1)(windows 10)
如何在 Windows 上使用 python 3.4 在 ptqt5 中获取 QtWebEngineView?