检查文件系统在 Python 中是不是不区分大小写

Posted

技术标签:

【中文标题】检查文件系统在 Python 中是不是不区分大小写【英文标题】:Check if file system is case-insensitive in Python检查文件系统在 Python 中是否不区分大小写 【发布时间】:2011-12-13 18:40:17 【问题描述】:

如果文件系统不区分大小写,是否有一种简单的方法可以检查 Python?我特别考虑像 HFS+ (OSX) 和 NTFS (Windows) 这样的文件系统,您可以在其中访问与 foo、Foo 或 FOO 相同的文件,即使文件大小写被保留。

【问题讨论】:

【参考方案1】:

我认为我们可以在 Python 3.5+ 上使用pathlib 一行完成此操作,而无需创建临时文件:

from pathlib import Path

def is_case_insensitive(path) -> bool:
    return Path(str(Path.home()).upper()).exists()

反之亦然:

def is_case_sensitive(path) -> bool:
    return not Path(str(Path.home()).upper()).exists()

【讨论】:

【参考方案2】:

关于不同文件系统等的好点,Eric Smith。但是为什么不使用带有 dir 参数的 tempfile.NamedTemporaryFile 并避免自己做所有的上下文管理器呢?

def is_fs_case_sensitive(path):
    #
    # Force case with the prefix
    #
    with tempfile.NamedTemporaryFile(prefix='TmP',dir=path, delete=True) as tmp_file:
        return(not os.path.exists(tmp_file.name.lower()))

我还应该提到,您的解决方案并不能保证您实际上是在测试是否区分大小写。除非您检查默认前缀(使用 tempfile.gettempprefix())以确保它包含小写字符。所以在这里包含前缀并不是可选的。

您的解决方案会清理临时文件。我同意这似乎很明显,但一个人永远不知道,做一个吗?

【讨论】:

【参考方案3】:

@Shrikant 答案的变体,适用于模块中(即不在 REPL 中),即使您的用户没有家:

import os.path
is_fs_case_insensitive = os.path.exists(__file__.upper()) and os.path.exists(__file__.lower())
print(f"is_fs_case_insensitive=")

输出(macOS):

is_fs_case_insensitive=True ?

还有 Linux 方面:

(ssha)vagrant ~$python3.8 test.py
is_fs_case_insensitive=False ?
(ssha)vagrant ~$lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04 LTS
Release:    20.04
Codename:   focal

FWIW,我通过以下方式检查了pathlibosos.path 的内容:

[k for k in vars(pathlib).keys() if "case" in k.lower()]

没有任何东西看起来像它,虽然它确实有一个pathlib.supports_symlinks,但没有区分大小写。

【讨论】:

【参考方案4】:

我认为有一个更简单(而且可能更快)的解决方案。以下似乎适用于我测试的地方:

import os.path
home = os.path.expanduser('~')
is_fs_case_insensitive = os.path.exists(home.upper()) and os.path.exists(home.lower())

【讨论】:

这适用于 macOS,但需要注意的是,如果以 no-login/no-shell 用户身份运行,您可能会遇到问题,有时出于安全原因,对于守护进程上的低权限用户(想想例如有人在用户nobody 下运行Django)。除此之外,这是一个不会与临时文件混淆的文件。【参考方案5】:

我相信这是对这个问题最简单的解决方案:

from fnmatch import fnmatch
os_is_case_insensitive = fnmatch('A','a')

发件人:https://docs.python.org/3.4/library/fnmatch.html

如果操作系统不区分大小写,那么这两个参数都将 在比较之前被标准化为所有小写或大写 执行。

【讨论】:

不幸的是,这不能处理每个路径的不敏感性。只有@eric-smith 的答案似乎在这里有效。 不。 OSX 不区分大小写并返回 False。【参考方案6】:

Amber 提供的答案会留下临时文件碎片,除非明确处理关闭和删除。为了避免这种情况,我使用:

import os
import tempfile

def is_fs_case_sensitive():
    #
    # Force case with the prefix
    #
    with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
        return(not os.path.exists(tmp_file.name.lower()))

虽然我的用例通常会不止一次地对此进行测试,所以我将结果存储起来以避免不得不多次接触文件系统。

def is_fs_case_sensitive():
    if not hasattr(is_fs_case_sensitive, 'case_sensitive'):
        with tempfile.NamedTemporaryFile(prefix='TmP') as tmp_file:
            setattr(is_fs_case_sensitive,
                    'case_sensitive',
                    not os.path.exists(tmp_file.name.lower()))
    return(is_fs_case_sensitive.case_sensitive)

如果只调用一次会稍微慢一点,而在其他情况下会明显更快。

【讨论】:

目前为止最好的解决方案,但是该函数应该将源目录作为输入参数,因为至少在 OSX 上,每个路径可能会有所不同。不是开玩笑。【参考方案7】:

从 Amber 的回答开始,我想出了这段代码。我不确定它是否完全健壮,但它试图解决原始版本中的一些问题(我将在下面提到)。

import os
import sys
import tempfile
import contextlib


def is_case_sensitive(path):
    with temp(path) as tmppath:
        head, tail = os.path.split(tmppath)
        testpath = os.path.join(head, tail.upper())
        return not os.path.exists(testpath)


@contextlib.contextmanager
def temp(path):
    tmphandle, tmppath = tempfile.mkstemp(dir=path)
    os.close(tmphandle)
    try:
        yield tmppath
    finally:
        os.unlink(tmppath)


if __name__ == '__main__':
    path = os.path.abspath(sys.argv[1])
    print(path)
    print('Case sensitive: ' + str(is_case_sensitive(path)))

没有在mkstemp中指定dir参数,区分大小写的问题就模糊了。您正在测试临时目录所在位置是否区分大小写,但您可能想了解特定路径。

如果您将从mkstemp 返回的完整路径转换为大写,您可能会错过路径中某处的转换。例如,我在 Linux 上使用 vfat 在/media/FLASH 安装了一个 USB 闪存驱动器。测试/MEDIA/FLASH 下是否存在任何东西总是会失败,因为/media 位于(区分大小写的)ext4 分区上,但闪存驱动器本身不区分大小写。挂载的网络共享可能是这样的另一种情况。

最后,也许在 Amber 的回答中不言而喻,您需要清理 mkstemp 创建的临时文件。

【讨论】:

【参考方案8】:
import os

if os.path.normcase('A') == os.path.normcase('a'):
    # case insensitive
else:
    # case sensitive

【讨论】:

至少在 Mac OS 上是错误的。文件系统不区分大小写,normcase 返回 2 个不同的结果 那么这会是 Python 的错误吗?【参考方案9】:
import os
import tempfile

# By default mkstemp() creates a file with
# a name that begins with 'tmp' (lowercase)
tmphandle, tmppath = tempfile.mkstemp()
if os.path.exists(tmppath.upper()):
    # Case insensitive.
else:
    # Case sensitive.

【讨论】:

如果 tmppath 恰好都是大写字母会怎样?规范保证不会发生这种情况吗? @LorinHochstein - 如代码中的 cmets 所述,mkstemp() 的“前缀”参数的默认值为 "tmp"(小写)。 docs.python.org/library/tempfile.html#tempfile.mkstemp 因此生成的文件名总是以 3 个小写字符开头。 @Lorin Hochstein:文档说默认前缀是'tmp',您可以检查gettempprefix() == gettempprefix.lower() 或在mkstemp() 中明确设置prefix 如果临时文件不在感兴趣的文件系统中会怎样? 之后别忘了删除文件! os.path.remove(tmppath)

以上是关于检查文件系统在 Python 中是不是不区分大小写的主要内容,如果未能解决你的问题,请参考以下文章

不区分大小写地检查字符串是不是存在于数组中

mac系统,git上刚刚checkout出来的文件,一检查,发现已经被修改过了,怎么破???

如何在 iOS 中读取、写入和检查不区分大小写的文件名?

如何在Java中以不区分大小写的方式检查一个字符串是不是包含另一个字符串?

区分大小写的文件扩展名和存在检查

mac系统创建文件夹是不区分大小写的吗