路径名太长打不开?

新手上路,请多包涵

这是执行的屏幕截图:

在此处输入图像描述

如您所见,错误表明目录“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

更新

当我将文件名更改为较小的文件名时,它起作用了,所以问题是因为路径的长度。请问解决方法是什么?

原文由 Marco Dinatsoli 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 984
1 个回答

常规 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 设备链接的实际目录,但从 NT 5(或带有终端服务的 NT 4)开始,这变成了一个虚拟前缀。对象管理器通过首先检查目录 \Sessions\0\DosDevices\[LOGON_SESSION_ID] 中的登录会话的 DOS 设备链接来处理此前缀,然后检查 \Global?? 目录中的系统范围 DOS 设备链接。

请注意,前者是登录会话,而不是 Windows 会话。登录会话目录都在 DosDevices Windows会话0(即Vista+中的服务会话)目录下。因此,如果您有用于非提升登录的映射驱动器,您会发现它在提升命令提示符中不可用,因为提升令牌实际上用于不同的登录会话。

DOS 设备链接的示例是 \Global??\C: => \Device\HarddiskVolume2 。在这种情况下,DOS C: 驱动器实际上是指向 HarddiskVolume2 设备的符号链接。

下面是系统如何处理解析路径以打开文件的简要概述。 Given we’re calling WinAPI CreateFile , it stores the translated NT UNICODE_STRING in an OBJECT_ATTRIBUTES structure and calls the system function 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 这完成了解析路径的工作。对象管理器以 \??\C:\Temp\test.txt 开头。然后将其替换为 \Global??\C:Temp\test.txt 。接下来它解析到 C: 符号链接并且必须重新开始(重新解析)最终路径 \Device\HarddiskVolume2\Temp\test.txt

一旦对象管理器到达 HarddiskVolume2 设备对象,解析就会交给 I/O 管理器,后者实现 Device 对象类型。 The ParseProcedure of an I/O Device creates the File object and an I/O Request Packet (IRP) with the major function code IRP_MJ_CREATE (打开/创建操作)由设备堆栈处理。这通过 IoCallDriver 发送到设备驱动程序。如果设备实现了重解析点(例如连接挂载点、符号链接等)并且路径包含重解析点,则解析后的路径必须重新提交给对象管理器以便从头开始解析。

设备驱动程序将使用进程令牌(或模拟线程)的 SeChangeNotifyPrivilege (几乎始终存在并启用)在遍历目录时绕过访问检查。但是,最终必须通过安全描述符来允许访问设备和目标文件,该安全描述符通过 SeAccessCheck 验证。除了像 FAT32 这样的简单文件系统不支持文件安全。

原文由 Eryk Sun 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题