我正在存储有关OSX HFS +文件系统上存在的文件的数据。我后来想迭代存储的数据并确定每个文件是否仍然存在。出于我的目的,我关心文件名区分大小写,所以如果文件名的大小写已经改变,我会认为该文件不再存在。
我开始尝试
os.path.isfile(filename)
但是在HFS +上正常安装OSX时,即使文件名大小写不匹配,也会返回True。我正在寻找一种方法来编写一个isfile()函数,即使在文件系统不关心的情况下也会关注它。
os.path.normcase()和os.path.realpath()都会返回文件名,无论我传递给它们。
编辑:
我现在有两个功能似乎适用于限制为ASCII的文件名。我不知道unicode或其他角色会如何影响这个。
第一个是基于omz和Alex L给出的答案。
def does_file_exist_case_sensitive1a(fname):
if not os.path.isfile(fname): return False
path, filename = os.path.split(fname)
search_path = '.' if path == '' else path
for name in os.listdir(search_path):
if name == filename : return True
return False
第二种可能效率更低。
def does_file_exist_case_sensitive2(fname):
if not os.path.isfile(fname): return False
m = re.search('[a-zA-Z][^a-zA-Z]*\Z', fname)
if m:
test = string.replace(fname, fname[m.start()], '?', 1)
print test
actual = glob.glob(test)
return len(actual) == 1 and actual[0] == fname
else:
return True # no letters in file, case sensitivity doesn't matter
以下是DSM的第三个答案。
def does_file_exist_case_sensitive3(fname):
if not os.path.isfile(fname): return False
path, filename = os.path.split(fname)
search_path = '.' if path == '' else path
inodes = {os.stat(x).st_ino: x for x in os.listdir(search_path)}
return inodes[os.stat(fname).st_ino] == filename
如果我在一个目录中有数千个文件,我不希望这些表现良好。我仍然希望能有更高效的东西。
我在测试这些内容时注意到的另一个缺点是他们只检查案例匹配的文件名。如果我传递一个包含目录名的路径,到目前为止这些函数都没有检查目录名的大小写。
答案 0 :(得分:3)
这是一个疯狂的想法。免责声明:我不太了解文件系统是否足以考虑边缘情况,因此将其视为恰好可行的东西。一旦。
>>> !ls
A.txt b.txt
>>> inodes = {os.stat(x).st_ino: x for x in os.listdir(".")}
>>> inodes
{80827580: 'A.txt', 80827581: 'b.txt'}
>>> inodes[os.stat("A.txt").st_ino]
'A.txt'
>>> inodes[os.stat("a.txt").st_ino]
'A.txt'
>>> inodes[os.stat("B.txt").st_ino]
'b.txt'
>>> inodes[os.stat("b.txt").st_ino]
'b.txt'
答案 1 :(得分:2)
您可以使用os.listdir
之类的内容,并检查列表中是否包含您要查找的文件名。
答案 2 :(得分:2)
从omz的帖子开始 - 这样的事情可能有用:
import os
def getcase(filepath):
path, filename = os.path.split(filepath)
for fname in os.listdir(path):
if filename.lower() == fname.lower():
return os.path.join(path, fname)
print getcase('/usr/myfile.txt')
答案 3 :(得分:2)
此答案通过提供改编自Alex L's answer的函数来补充现有答案:
import os, unicodedata
def gettruecasepath(path): # IMPORTANT: <path> must be a Unicode string
if not os.path.lexists(path): # use lexists to also find broken symlinks
raise OSError(2, u'No such file or directory', path)
isosx = sys.platform == u'darwin'
if isosx: # convert to NFD for comparison with os.listdir() results
path = unicodedata.normalize('NFD', path)
parentpath, leaf = os.path.split(path)
# find true case of leaf component
if leaf not in [ u'.', u'..' ]: # skip . and .. components
leaf_lower = leaf.lower() # if you use Py3.3+: change .lower() to .casefold()
found = False
for leaf in os.listdir(u'.' if parentpath == u'' else parentpath):
if leaf_lower == leaf.lower(): # see .casefold() comment above
found = True
if isosx:
leaf = unicodedata.normalize('NFC', leaf) # convert to NFC for return value
break
if not found:
# should only happen if the path was just deleted
raise OSError(2, u'Unexpectedly not found in ' + parentpath, leaf_lower)
# recurse on parent path
if parentpath not in [ u'', u'.', u'..', u'/', u'\\' ] and \
not (sys.platform == u'win32' and
os.path.splitdrive(parentpath)[1] in [ u'\\', u'/' ]):
parentpath = gettruecasepath(parentpath) # recurse
return os.path.join(parentpath, leaf)
def istruecasepath(path): # IMPORTANT: <path> must be a Unicode string
return gettruecasepath(path) == unicodedata.normalize('NFC', path)
gettruecasepath()
获取存储在指定路径(绝对路径或相对路径)的文件系统中的大小写精确表示形式(如果存在):
u
;例如,u'Motörhead'
; str变量:转换为例如strVar.decode('utf8')
.
和..
),除了多个路径分隔符已折叠,并且在Windows上,返回path始终使用\
作为路径分隔符。OSError
异常。 istruecasepath()
使用gettruecasepath()
来比较存储在文件系统中的路径的输入路径。
警告:由于这些函数需要检查输入路径的每个级别(如指定)的所有目录条目,因此它们将慢 - 不可预测的因此,作为性能将对应于检查的目录包含的项目数。继续阅读以获取背景信息。
很奇怪OSX和Windows都没有提供直接解决此问题的本机API方法。
虽然on Windows you can cleverly combine two API methods to solve the problem,但在OSX上,我无法理解 - 在上面使用的路径的每个级别上目录内容的缓慢枚举 - 不可预测 - 慢速枚举。
HFS +(OSX&#39;文件系统)以分解的 Unicode格式(NFD)存储文件名,这会在大多数编程语言中将这些名称与内存中的Unicode字符串进行比较时导致问题,通常采用编组 Unicode格式(NFC)。
例如,您在源代码中指定为文字的非ASCII字符ü
的路径将表示为单个 Unicode代码点,U+00FC
;这是 NFC 的一个例子:&#39; C&#39;代表撰写,因为字母基础字母u
及其变音符¨
(组合分音符)形成单字母。
相比之下,如果您使用ü
作为HFS + 文件名的一部分,则会将其翻译为 NFD 形式,从而导致 2 Unicode代码点:基本字母u
(U+0075
),后跟合并分类广告(̈
,U+0308
)作为单独的 codepoint; &#39; D&#39;代表分解,因为该字符被分解为基本字母及其相关的变音符号。
尽管Unicode标准认为这两个表示(规范)等效,但大多数编程语言(包括Python)都 不能识别这种等价。
对于Python,您必须使用unicodedata.normalize()
在比较之前将两个字符串转换为相同的表单。
(旁注:Unicode 普通表格与Unicode 编码分开,但不同数量的Unicode代码点通常也会影响字节的数量在上面的示例中,单码点ü
(NFC)需要 2个字节以UTF-8(U+00FC
- > 0xC3 0xBC
),而双码点ü
(NFD)需要 3个字节(U+0075
- &gt; 0x75
和{{1} } - &gt; U+0308
))。
答案 4 :(得分:1)
这个答案只是一个概念证明,因为它不会尝试转义特殊字符,处理非ASCII字符或处理文件系统编码问题。
从好的方面来说,答案不涉及在Python中循环遍历文件,而是正确处理检查导致最终路径段的目录名称。
此建议基于以下观察(至少在使用bash时),当且仅当/my/path
存在具有该确切框的情况时,以下命令才会发现路径/my/path
没有错误。
$ ls /[m]y/[p]ath
(如果任何路径部分中没有括号,那么该部分对套管中的更改不敏感。)
以下是基于此想法的示例函数:
import os.path
import subprocess
def does_exist(path):
"""Return whether the given path exists with the given casing.
The given path should begin with a slash and not end with a trailing
slash. This function does not attempt to escape special characters
and does not attempt to handle non-ASCII characters, file system
encodings, etc.
"""
parts = []
while True:
head, tail = os.path.split(path)
if tail:
parts.append(tail)
path = head
else:
assert head == '/'
break
parts.reverse()
# For example, for path "/my/path", pattern is "/[m]y/[p]ath".
pattern = "/" + "/".join(["[%s]%s" % (p[0], p[1:]) for p in parts])
cmd = "ls %s" % pattern
return_code = subprocess.call(cmd, shell=True)
return not return_code
答案 5 :(得分:-2)
您也可以尝试打开该文件。
try:open('test', 'r')
except IOError: print 'File does not exist'