Windows使用不区分大小写的文件名,因此我可以使用以下任何一个打开同一个文件:
r"c:\windows\system32\desktop.ini"
r"C:\WINdows\System32\DESKTOP.ini"
r"C:\WiNdOwS\SyStEm32\DeSkToP.iNi"
等。鉴于这些路径中的任何一条,我怎样才能找到真实案例?我希望他们都能产生:
r"C:\Windows\System32\desktop.ini"
os.path.normcase
没有这样做,它只是降低了一切。 os.path.abspath
返回绝对路径,但这些路径中的每一个都是绝对路径,因此它不会更改任何路径。 os.path.realpath
仅用于解析Windows没有的符号链接,因此它与Windows上的abspath相同。
有直接的方法吗?
答案 0 :(得分:13)
Ned的GetLongPathName
答案不太合适(至少不适合我)。您需要在GetLongPathName
的返回值上调用GetShortPathname
。使用pywin32简洁(ctypes解决方案看起来类似于Ned):
>>> win32api.GetLongPathName(win32api.GetShortPathName('stopservices.vbs'))
'StopServices.vbs'
答案 1 :(得分:7)
This python-win32 thread的答案不需要第三方软件包或走树:
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
答案 2 :(得分:7)
这是一个简单的,仅限stdlib的解决方案:
import glob
def get_actual_filename(name):
name = "%s[%s]" % (name[:-1], name[-1])
return glob.glob(name)[0]
答案 3 :(得分:7)
Ethan answer 仅更正文件名,而不更正路径上的子文件夹名称。 这是我的猜测:
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]
答案 4 :(得分:6)
这个统一,缩短并修复了几种方法: 仅限标准库;转换所有路径部分(驱动器号除外);相对或绝对路径;驱动信函是否; tolarant:
def casedpath(path):
r = glob.glob(re.sub(r'([^:/\\])(?=[/\\]|$)', r'[\1]', path))
return r and r[0] or path
此外还可以处理UNC路径:
def casedpath_unc(path):
unc, p = os.path.splitunc(path)
r = glob.glob(unc + re.sub(r'([^:/\\])(?=[/\\]|$)', r'[\1]', p))
return r and r[0] or path
答案 5 :(得分:4)
由于NTFS(或VFAT)文件系统上的“真实案例”的定义确实很奇怪,因此最好的方法是走路并与os.listdir()匹配。
是的,这似乎是一个人为的解决方案,但NTFS路径也是如此。我没有DOS机器来测试它。
答案 6 :(得分:2)
我更喜欢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]
答案 7 :(得分:2)
基于上面的几个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
答案 8 :(得分:1)
我会使用os.walk
,但我认为对于包含许多目录的diskw来说,这可能很耗时:
fname = "g:\\miCHal\\ZzZ.tXt"
if not os.path.exists(fname):
print('No such file')
else:
d, f = os.path.split(fname)
dl = d.lower()
fl = f.lower()
for root, dirs, files in os.walk('g:\\'):
if root.lower() == dl:
fn = [n for n in files if n.lower() == fl][0]
print(os.path.join(root, fn))
break
答案 9 :(得分:1)
我正在努力解决同样的问题。我不确定,但我认为以前的答案并未涵盖所有案例。我的实际问题是驱动器字母外壳与系统看到的不同。这是我的解决方案,也检查正确的驱动器号套管(使用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("\000")[:-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
答案 10 :(得分:0)
在Python 3中,您可以使用pathlib
的{{1}}:
resolve()
答案 11 :(得分:0)
我一直在寻找一个甚至更简单的版本(“技巧”),所以我做到了,它仅使用os.listdir()。
def casedPath(path):
path = os.path.normpath(path).lower()
parts = path.split(os.sep)
result = parts[0].upper()
# check that root actually exists
if not os.path.exists(result):
return
for part in parts[1:]:
actual = next((item for item in os.listdir(result) if item.lower() == part), None)
if actual is None:
# path doesn't exist
return
result += os.sep + actual
return result
edit:顺便说一句,它工作正常。不确定在不存在路径时返回None
的情况,但是我需要这种行为。我想这可能会引发错误。