路径名太久无法打开?

Mar*_*oli 5 python windows python-2.7

这是执行的截图:

在此输入图像描述

如您所见,错误显示目录"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
Run Code Online (Sandbox Code Playgroud)

UPDATE

当我将文件名更改为较小的文件名时,它可以正常工作,因此问题在于路径的长度.请问是什么解决方案?

Ery*_*Sun 15

常规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"))
Run Code Online (Sandbox Code Playgroud)
>>> path = winapi_path("C:\\Temp\\test.txt")
>>> print path
\\?\C:\Temp\test.txt
Run Code Online (Sandbox Code Playgroud)

请参阅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
Run Code Online (Sandbox Code Playgroud)

结果替换\\?\为NT DOS设备前缀\??\,并将字符串复制到本机UNICODE_STRING:

0:000> dS b450f9ec18
000000b4`536b7de0  "\??\C:\Temp\test.txt"
Run Code Online (Sandbox Code Playgroud)

如果使用//?/而不是\\?\,则路径仍限于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"
Run Code Online (Sandbox Code Playgroud)

正斜杠是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
Run Code Online (Sandbox Code Playgroud)

你可能想知道是什么\??意思.这曾经是对象命名空间中DOS设备链接的实际目录,但从NT 5(或NT 4 w /终端服务)开始,这成为一个虚拟前缀.对象管理器首先检查目录中的登录会话的DOS设备链接\Sessions\0\DosDevices\[LOGON_SESSION_ID],然后检查目录中的系统范围的DOS设备链接来处理此前缀\Global??.

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

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
Run Code Online (Sandbox Code Playgroud)

NtCreateFile调用I/O管理器函数IoCreateFile,该函数又调用未记录的对象管理器API ObOpenObjectByName.这样做可以解析路径.对象管理器以\??\C:\Temp\test.txt.然后用它代替\Global??\C:Temp\test.txt.接下来,它解析C:符号链接,并且必须重新开始(重新分析)最终路径\Device\HarddiskVolume2\Temp\test.txt.

一旦对象管理器到达HarddiskVolume2设备对象,解析将被传递给I/O管理器,I/O管理器实现Device对象类型.所述ParseProcedure的I/O的Device创建File对象和一个I/O请求分组(IRP)与所述主功能码 IRP_MJ_CREATE对由该装置栈来处理(打开/创建操作).这通过发送到设备驱动程序IoCallDriver.如果设备实现重新分析点(例如,结点安装点,符号链接等)并且路径包含重新分析点,则必须将已解析的路径重新提交到对象管理器以从头开始进行解析.

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


小智 8

下面是关于@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
Run Code Online (Sandbox Code Playgroud)

#Python 3 将 unicode 类型重命名为 str,旧的 str 类型已被字节替换。NameError:全局名称“unicode”未定义 - 在 Python 3 中


Mri*_*wat 0

出现此错误的原因可能有多种。请确保以下事项:

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

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

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