pathlib中的Path.rglob在Windows上处理长文件路径时失败

5
我试图返回特定位置的所有文件和子文件夹列表。我的代码如下:
from pathlib import Path
FOLDER_PATH = Path(r'C:\long\file\path\of\138\characters\')

我遇到了错误:FileNotFoundError: [WinError 3] 系统找不到指定的路径:

这个错误出现在文件夹路径上,而不是文件上,所以我不确定这是否可能是原因。

当我手动进入该文件夹并尝试打开其中的PDF文件时,会提示“发生错误,无法打开此文档。找不到该文件。”

同样,当我尝试打开XLSX文件时,会提示“无法访问此文件。请尝试以下操作:(确保它存在,不是只读的,没有超过218个字符等)”

这个文件夹中的文件路径肯定超过了218个字符,我知道这可能会导致Excel出问题,但我不明白为什么pathlib.Path.rglob列出它们也会出问题,请问是否有人能理解这一点?

然而,如果我使用CMDdir /s /b > files.txt),我可以得到列表。

另外,如果我将files.txt导入python的Path对象列表paths,并尝试执行[x.is_file() for x in paths],它将无法正确识别某些较长的路径作为文件。

我已经验证,如果我将该目录复制到本地(其中存在较短的路径),则可以通过Excel和pathlib.Path.rglob访问这些文件。

如何解决这个问题,为什么会出现这个问题?


微软文档指出,Windows API 中大多数函数的最大路径长度为260个字符。 - Barmar
1个回答

12

问题在于大多数Windows文件系统函数不接受看起来像这样的路径:

r'C:\long\file\path\of\256\characters'

因此,pathlib和Excel都发现它们无法使用这些Windows函数打开文件或读取目录。

好消息是Windows函数确实接受以下路径:

r'\\?\C:\long\file\path\of\256\characters'

坏消息是,pathlib并不总是能正确地连接这种路径:

>>> Path(r'\\?\foo').joinpath(r'\\?\bar')
WindowsPath('//?/foo/bar')  # correct
>>> Path(r'\\?\foo', r'\\?\bar')
WindowsPath('//?/bar')  # incorrect
>>> Path(r'\\?\c:\foo').joinpath(r'c:\bar')
WindowsPath('c:/bar')  # correct, but not the result we want
另外一个坏消息是这样的路径有一些限制:当去到Windows文件系统函数的路径以\\?\开头时,您不能使用正斜杠、单点或双点。
好消息是,像下面这样的函数可以将你想出的任何混乱的路径转换为可用的路径:
def longname(path):
    return pathlib.Path('\\\\?\\' + os.fspath(path.resolve()))

请注意,resolve() 只会在 path 确实存在时才从其开头删除 \\?\,因此在 path 不存在且已经有 \\?\ 的情况下,上述代码将无法运行。 因此,请确保您的程序使用没有前缀的“普通”路径,并在执行任何实际文件操作之前调用 longname(),或者增强 longname()

def longname(path):
    normalized = os.fspath(path.resolve())
    if not normalized.startswith('\\\\?\\'):
        normalized = '\\\\?\\' + normalized
    return pathlib.Path(normalized)

Windows 的行为已经由 Microsoft 文件详细记录:https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file#maximum-path-length-limitation


从您分享的链接看来,点和双点似乎可以作为文件名的一部分(例如\file..txt),只是不会像\..\file.txt一样被解释为相对路径。 - Dr_Zaszuś
我们能调整一下,让它在Windows和Linux上都能运行吗? :) - Roelant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接