windows 在 Python 中,如何获取文件的正确大小写路径?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3692261/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
In Python, how can I get the correctly-cased path for a file?
提问by Ned Batchelder
Windows uses case-insensitive file names, so I can open the same file with any of these:
Windows 使用不区分大小写的文件名,因此我可以使用以下任一文件打开同一个文件:
r"c:\windows\system32\desktop.ini"
r"C:\WINdows\System32\DESKTOP.ini"
r"C:\WiNdOwS\SyStEm32\DeSkToP.iNi"
etc. Given any of these paths, how can I find the true case? I want them all to produce:
等等。给定这些路径中的任何一条,我怎样才能找到真实的案例?我希望他们都能生产:
r"C:\Windows\System32\desktop.ini"
os.path.normcase
doesn't do it, it simply lowercases everything. os.path.abspath
returns an absolute path, but each of these is already absolute, and so it doesn't change any of them. os.path.realpath
is only used to resolve symbolic links, which Windows doesn't have, so it's the same as abspath on Windows.
os.path.normcase
不这样做,它只是将所有内容小写。os.path.abspath
返回一个绝对路径,但这些路径中的每一个都已经是绝对路径,因此它不会改变它们中的任何一个。 os.path.realpath
仅用于解析符号链接,Windows 没有,所以它与 Windows 上的 abspath 相同。
Is there a straightforward way to do this?
有没有直接的方法来做到这一点?
采纳答案by Ethan Furman
Here's a simple, stdlib only, solution:
这是一个简单的、仅限标准库的解决方案:
import glob
def get_actual_filename(name):
name = "%s[%s]" % (name[:-1], name[-1])
return glob.glob(name)[0]
回答by Paul Moore
Ned's GetLongPathName
answer doesn't quite work (at least not for me). You need to call GetLongPathName
on the return value of GetShortPathname
. Using pywin32 for brevity (a ctypes solution would look similar to Ned's):
奈德的GetLongPathName
回答不太有效(至少对我来说不是)。您需要调用GetLongPathName
的返回值GetShortPathname
。使用 pywin32 为简洁起见(ctypes 解决方案看起来类似于 Ned 的解决方案):
>>> win32api.GetLongPathName(win32api.GetShortPathName('stopservices.vbs'))
'StopServices.vbs'
回答by xvorsx
Ethan answer correct only file name, not subfolders names on the path. Here is my guess:
Ethan只回答正确的文件名,而不是路径上的子文件夹名称。这是我的猜测:
def get_actual_filename(name):
dirs = name.split('\')
# disk letter
test_name = [dirs[0].upper()]
for d in dirs[1:]:
test_name += ["%s[%s]" % (d[:-1], d[-1])]
res = glob.glob('\'.join(test_name))
if not res:
#File not found
return None
return res[0]
回答by kxr
This one unifies, shortens and fixes several approaches: Standard lib only; converts all path parts (except drive letter); relative or absolute paths; drive letter'ed or not; tolarant:
这个统一、缩短和修复了几种方法: 仅标准库;转换所有路径部分(驱动器号除外);相对或绝对路径;驱动器盘符与否;容忍:
def casedpath(path):
r = glob.glob(re.sub(r'([^:/\])(?=[/\]|$)', r'[]', path))
return r and r[0] or path
And this one handles UNC paths in addition:
而且这个还处理 UNC 路径:
def casedpath_unc(path):
unc, p = os.path.splitunc(path)
r = glob.glob(unc + re.sub(r'([^:/\])(?=[/\]|$)', r'[]', p))
return r and r[0] or path
回答by Ned Batchelder
This python-win32 threadhas an answer that doesn't require third-party packages or walking the tree:
这个 python-win32 线程有一个不需要第三方包或遍历树的答案:
import ctypes
def getLongPathName(path):
buf = ctypes.create_unicode_buffer(260)
GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
rv = GetLongPathName(path, buf, 260)
if rv == 0 or rv > 260:
return path
else:
return buf.value
回答by msw
Since the definition of "true case" on NTFS (or VFAT) filesystems is truly bizarre, it seems the best way would be to walk the path and match against os.listdir().
由于 NTFS(或 VFAT)文件系统上“真实案例”的定义确实很奇怪,似乎最好的方法是走路径并匹配os.listdir()。
Yes, this seems like a contrived solution but so are NTFS paths. I don't have a DOS machine to test this on.
是的,这似乎是一个人为的解决方案,但 NTFS 路径也是如此。我没有 DOS 机器来测试这个。
回答by TheAl_T
In Python 3 you can use the pathlib
's resolve()
:
在 Python 3 中,您可以使用pathlib
's resolve()
:
>>> from pathlib import Path
>>> str(Path(r"C:\WiNdOwS\SyStEm32\DeSkToP.iNi").resolve())
r'C:\Windows\System32\desktop.ini'
回答by Dobedani
I prefer the approach of Ethan and xvorsx. AFAIK, the following wouldn't also harm on other platforms:
我更喜欢 Ethan 和 xvorsx 的方法。AFAIK,以下内容也不会对其他平台造成伤害:
import os.path
from glob import glob
def get_actual_filename(name):
sep = os.path.sep
parts = os.path.normpath(name).split(sep)
dirs = parts[0:-1]
filename = parts[-1]
if dirs[0] == os.path.splitdrive(name)[0]:
test_name = [dirs[0].upper()]
else:
test_name = [sep + dirs[0]]
for d in dirs[1:]:
test_name += ["%s[%s]" % (d[:-1], d[-1])]
path = glob(sep.join(test_name))[0]
res = glob(sep.join((path, filename)))
if not res:
#File not found
return None
return res[0]
回答by Brendan Abel
Based off a couple of the listdir/walk examples above, but supports UNC paths
基于上面的几个 listdir/walk 示例,但支持 UNC 路径
def get_actual_filename(path):
orig_path = path
path = os.path.normpath(path)
# Build root to start searching from. Different for unc paths.
if path.startswith(r'\'):
path = path.lstrip(r'\')
path_split = path.split('\')
# listdir doesn't work on just the machine name
if len(path_split) < 3:
return orig_path
test_path = r'\{}\{}'.format(path_split[0], path_split[1])
start = 2
else:
path_split = path.split('\')
test_path = path_split[0] + '\'
start = 1
for i in range(start, len(path_split)):
part = path_split[i]
if os.path.isdir(test_path):
for name in os.listdir(test_path):
if name.lower() == part.lower():
part = name
break
test_path = os.path.join(test_path, part)
else:
return orig_path
return test_path
回答by lutecki
I was just struggling with the same problem. I'm not sure, but I think the previous answers do not cover all cases. My actual problem was that the drive letter casing was different than the one seen by the system. Here is my solution that also checks for the correct drive letter casing (using win32api):
我只是在努力解决同样的问题。我不确定,但我认为之前的答案并未涵盖所有情况。我的实际问题是驱动器字母外壳与系统看到的不同。这是我的解决方案,它还检查正确的驱动器字母大小写(使用 win32api):
def get_case_sensitive_path(path):
"""
Get case sensitive path based on not - case sensitive path.
Returns:
The real absolute path.
Exceptions:
ValueError if the path doesn't exist.
Important note on Windows: when starting command line using
letter cases different from the actual casing of the files / directories,
the interpreter will use the invalid cases in path (e. g. os.getcwd()
returns path that has cases different from actuals).
When using tools that are case - sensitive, this will cause a problem.
Below code is used to get path with exact the same casing as the
actual.
See http://stackoverflow.com/questions/2113822/python-getting-filename-case-as-stored-in-windows
"""
drive, path = os.path.splitdrive(os.path.abspath(path))
path = path.lstrip(os.sep)
path = path.rstrip(os.sep)
folders = []
# Make sure the drive number is also in the correct casing.
drives = win32api.GetLogicalDriveStrings()
drives = drives.split("##代码##0")[:-1]
# Get the list of the the form C:, d:, E: etc.
drives = [d.replace("\", "") for d in drives]
# Now get a lower case version for comparison.
drives_l = [d.lower() for d in drives]
# Find the index of matching item.
idx = drives_l.index(drive.lower())
# Get the drive letter with the correct casing.
drive = drives[idx]
# Divide path into components.
while 1:
path, folder = os.path.split(path)
if folder != "":
folders.append(folder)
else:
if path != "":
folders.append(path)
break
# Restore their original order.
folders.reverse()
if len(folders) > 0:
retval = drive + os.sep
for folder in folders:
found = False
for item in os.listdir(retval):
if item.lower() == folder.lower():
found = True
retval = os.path.join(retval, item)
break
if not found:
raise ValueError("Path not found: '{0}'".format(retval))
else:
retval = drive + os.sep
return retval