路径名太长无法打开?

30

这是执行过程的截图:

在此输入图片描述

正如您所看到的,错误提示说目录 "JSONFiles/Apartment/Rent/dubizzleabudhabiproperty" 不存在。

但请看一下我的文件:

在此输入图片描述

该文件夹明显存在。

更新2

代码:

self.file = open("JSONFiles/"+ item["category"]+"/" + item["action"]+"/"+ item['source']+"/"+fileName + '.json', 'wb') # Create a new JSON file with the name = fileName parameter
        line = json.dumps(dict(item)) # Change the item to a JSON format in one line
        self.file.write(line) # Write the item to the file

更新

当我将文件名改为更短的名称时,它可以正常工作,因此问题是由于路径长度导致的。请问有什么解决方法?


1
请在使用Windows时,使用os.path.join()创建路径。 - Lafexlos
1
您的文件名可能包含“特殊”字符或过长。 - cdarke
2
我并不是很厉害,这只是猜测而已。 - cdarke
不要尝试将数据存储在文件名中。看起来您在文件名中重复了文件夹名称,这是不必要的。 - cdarke
@SRK Windows Server 2012 R2 - Marco Dinatsoli
显示剩余26条评论
5个回答

50
常规的DOS路径限制为MAX_PATH(260)个字符,包括字符串的终止符NUL。您可以通过使用以\\?\前缀开头的扩展长度路径来超过此限制。此路径必须是Unicode字符串,完全合格,并且只使用反斜杠作为路径分隔符。根据Microsoft的文件系统功能比较,最大扩展路径长度为32760个字符。单个文件或目录名称最长可达255个字符(UDF文件系统为127个字符)。扩展UNC路径也支持\\?\UNC\server\share
例如:
import os

def winapi_path(dos_path, encoding=None):
    if (not isinstance(dos_path, unicode) and 
        encoding is not None):
        dos_path = dos_path.decode(encoding)
    path = os.path.abspath(dos_path)
    if path.startswith(u"\\\\"):
        return u"\\\\?\\UNC\\" + path[2:]
    return u"\\\\?\\" + path

path = winapi_path(os.path.join(u"JSONFiles", 
                                item["category"],
                                item["action"], 
                                item["source"], 
                                fileName + ".json"))

>>> path = winapi_path("C:\\Temp\\test.txt")
>>> print path
\\?\C:\Temp\test.txt

请参阅MSDN上的以下页面:


背景

Windows调用NT运行库函数RtlDosPathNameToRelativeNtPathName_U_WithStatus将DOS路径转换为本地NT路径。如果我们在该函数上设置断点,然后使用open(即CreateFile)打开上述路径,我们可以看到它如何处理以\\?\前缀开头的路径。

Breakpoint 0 hit
ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus:
00007ff9`d1fb5880 4883ec58        sub     rsp,58h
0:000> du @rcx
000000b4`52fc0f60  "\\?\C:\Temp\test.txt"
0:000> r rdx
rdx=000000b450f9ec18
0:000> pt
ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus+0x66:
00007ff9`d1fb58e6 c3              ret

该结果将\\?\替换为NT DOS设备前缀\??\,并将字符串复制到本地的UNICODE_STRING中。
0:000> dS b450f9ec18
000000b4`536b7de0  "\??\C:\Temp\test.txt"

如果你使用//?/ 而不是 \\?\,那么路径仍然被限制在MAX_PATH个字符。如果太长,则RtlDosPathNameToRelativeNtPathName返回状态码STATUS_NAME_TOO_LONG(0xC0000106)。

如果在前缀中使用\\?\,但在路径的其余部分使用斜杠,则Windows不会为您将斜杠转换为反斜杠:

Breakpoint 0 hit
ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus:
00007ff9`d1fb5880 4883ec58        sub     rsp,58h
0:000> du @rcx
0000005b`c2ffbf30  "\\?\C:/Temp/test.txt"
0:000> r rdx
rdx=0000005bc0b3f068
0:000> pt
ntdll!RtlDosPathNameToRelativeNtPathName_U_WithStatus+0x66:
00007ff9`d1fb58e6 c3              ret
0:000> dS 5bc0b3f068
0000005b`c3066d30  "\??\C:/Temp/test.txt"

正斜杠是NT命名空间中的有效对象名称字符。它被Microsoft文件系统保留,但您可以在其他命名的内核对象中使用正斜杠,这些对象存储在\BaseNamedObjects\Sessions\[session number]\BaseNamedObjects中。此外,我认为I/O管理器不会强制执行设备和文件名中保留字符的策略。这取决于设备。也许有人拥有一个实现允许在名称中使用正斜杠的命名空间的Windows设备。至少,您可以创建包含正斜杠的DOS设备名称。例如:

>>> kernel32 = ctypes.WinDLL('kernel32')
>>> kernel32.DefineDosDeviceW(0, u'My/Device', u'C:\\Temp')
>>> os.path.exists(u'\\\\?\\My/Device\\test.txt')
True

你可能会想知道 \?? 代表什么。这曾经是DOS设备链接在对象命名空间中的一个实际目录,但从NT5开始(或NT4带终端服务),这变成了一个虚拟前缀。对象管理器通过首先检查登录会话在目录 \Sessions\0\DosDevices\[LOGON_SESSION_ID]中的DOS设备链接,然后检查全局DOS设备链接在\Global??目录中来处理此前缀。
请注意,前者是登录会话,而不是Windows会话。登录会话目录都在Windows会话0(即Vista+中的服务会话)的DosDevices目录下。因此,如果您有一个映射到非提升登录的驱动器,则会发现它在提升的命令提示符中不可用,因为您的提升令牌实际上是针对另一个登录会话的。
DOS设备链接的一个例子是\Global??\C: => \Device\HarddiskVolume2。在这种情况下,DOS C: 驱动器实际上是指向HarddiskVolume2设备的符号链接。
这是系统处理路径打开文件的简要概述。由于我们调用了WinAPI的CreateFile,它将已转换的NT UNICODE_STRING存储在OBJECT_ATTRIBUTES结构中,并调用系统函数NtCreateFile
0:000> g
Breakpoint 1 hit
ntdll!NtCreateFile:
00007ff9`d2023d70 4c8bd1          mov     r10,rcx
0:000> !obja @r8
Obja +000000b450f9ec58 at 000000b450f9ec58:
        Name is \??\C:\Temp\test.txt
        OBJ_CASE_INSENSITIVE

NtCreateFile 调用 I/O 管理器函数 IoCreateFile,后者又调用未记录的对象管理器 API ObOpenObjectByName。这个 API 完成了解析路径的工作。对象管理器从 \??\C:\Temp\test.txt 开始。然后将其替换为 \Global??\C:Temp\test.txt。接下来,它解析到 C: 符号链接并不得不重新开始 (重新解析) 最终路径 \Device\HarddiskVolume2\Temp\test.txt

一旦对象管理器到达HarddiskVolume2设备对象,解析就会移交给实现Device对象类型的I/O管理器。 I/O DeviceParseProcedure创建File对象和一个I/O请求包(IRP),其中包含主功能码IRP_MJ_CREATE(打开/创建操作),以由设备堆栈处理。这通过IoCallDriver发送给设备驱动程序。如果设备实现重分析点(例如连接点,符号链接等)并且路径包含重分析点,则必须重新提交已解析的路径以从头开始进行解析。
设备驱动程序将使用进程令牌(或模拟的线程)的SeChangeNotifyPrivilege(几乎总是存在且启用),以便在遍历目录时绕过访问检查。但是,最终对设备和目标文件的访问必须由安全描述符允许,该描述符通过SeAccessCheck进行验证。除了FAT32等简单文件系统不支持文件安全性外。

19

以下是关于 @Eryk Sun 解决方案的 Python 3 版本。

def winapi_path(dos_path, encoding=None):
    if (not isinstance(dos_path, str) and encoding is not None): 
        dos_path = dos_path.decode(encoding)
    path = os.path.abspath(dos_path)
    if path.startswith(u"\\\\"):
        return u"\\\\?\\UNC\\" + path[2:]
    return u"\\\\?\\" + path

#Python 3将unicode类型更名为str,旧的str类型已被bytes取代。NameError: global name 'unicode' is not defined - in Python 3


不幸的是,这个解决方案在我的情况下没有起作用。我也有一个超过259个字符的路径,在MSYS中使用Python3会出现“没有这样的文件或目录”错误。但是当我在WSL(Windows子系统Linux)下的Ubuntu中运行相同的Python脚本时,它可以工作。 - Michal Fapso

3
添加了帮助我解决类似问题的解决方案: python版本=3.9,windows版本=10专业版。
我遇到了一个问题,文件名太长,超出了python内置方法open的限制。我得到的错误是路径不存在,尽管我使用了'w+'模式打开(无论文件是否存在都会打开一个新文件)。
我找到了这个guide,通过快速更改Windows注册表编辑器(特别是组策略),解决了这个问题。向下滚动到“让Windows 10接受长文件路径”标题。
不要忘记更新操作系统组策略以立即生效,可以在here中找到指南。
希望这可以帮助未来的搜索,因为这篇文章已经相当老了。

0

你遇到这个错误可能有多种原因,请确保以下内容:

  1. 文件夹(JSONFiles)的父目录与Python脚本的目录相同。

  2. 即使文件夹存在,也不意味着单个文件存在。请验证并确保精确的文件名与您的Python代码尝试访问的文件名匹配。

如果仍然遇到问题,请共享您正在尝试访问的最内部文件夹上“dir”命令的结果。


由于您分享了代码,我建议您使用os.path.join()命令来连接您当前在打开函数中连接的不同路径变量,然后您可以使用os.path.exists()验证路径。 - Mrinal Ahlawat
请检查我的最新更新,关于路径长度。 - Marco Dinatsoli
1
路径名通常限制在260个字符,但是你可以通过使用魔法前缀'\?'将其扩展到约32k。有关更多详细信息,请参阅https://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maximum%5Fpath%5Flength。 - Mrinal Ahlawat
你的路径将采用以下形式: "\?\非常长的路径" - Mrinal Ahlawat
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Marco Dinatsoli
显示剩余7条评论

0

对我来说有效

import os
str1=r"C:\Users\sandeepmkwana\Desktop\folder_structure\models\manual\demodfadsfljdskfjslkdsjfklaj\inner-2djfklsdfjsdklfj\inner3fadsfksdfjdklsfjksdgjl\inner4dfhasdjfhsdjfskfklsjdkjfleioreirueewdsfksdmv\anotherInnerfolder4aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\5qbbbbbbbbbbbccccccccccccccccccccccccsssssssssssssssss\tmp.txt"
print(len(str1)) #346

path = os.path.abspath(str1)

if path.startswith(u"\\\\"):
    path=u"\\\\?\\UNC\\"+path[2:]
else:
    path=u"\\\\?\\"+path

with open(path,"r+") as f:
    print(f.readline())

如果您在Windows中遇到了路径过长(超过258个字符)的问题,请尝试以下方法。

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