【问题标题】:Is there a way for DIR(path) in VBA to handle strings longer than 260?VBA中的DIR(路径)有没有办法处理超过260的字符串?
【发布时间】:2013-02-06 02:40:38
【问题描述】:

给定以下 sn-p:

Dim s As String: s = "S:\vic\bla\[..insert more here..]\data.xml"
Debug.Print Len(s)
Debug.Print Dir(s)

如果Len(s) >= 260 我收到一条错误消息,说明如下:

Run-time error '53':

File not found

如果字符串小于 260,则它可以正常工作并显示已找到和未找到文件的预期行为。

是否可以让 DIR 使用长 (>260) 路径名?

注意事项

  • 文件重组不是一种选择

  • 我在 Excel 2007 中运行它

【问题讨论】:

  • 作为短期修复,您可以重新映射最深的文件夹以访问文件
  • 为什么需要 Dir?您的示例中没有显示任何通配符,那么 FileSystemObject 是否可能适合您?
  • @Remou 非常好的问题。我只需要检查文件是否存在 - 所以如果我可以用另一种方法做到这一点,我愿意接受它
  • 根据您的上一条评论,我提供的代码将告诉您文件是否存在,无论目录结构的深度如何。您可能会考虑编辑您的问题,以便它反映您要问的内容......这样,对于不阅读 cmets 的人来说,答案会更有意义。

标签: vba excel


【解决方案1】:

简而言之(按标题回答):不。VBA 的 Dir 函数根本不适用于超过 260 个字符的路径。

长版:http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maximum_path_length(然后 Ctrl+F 并搜索“260”)

最大路径长度限制

在 Windows API 中(以下段落中讨论了一些例外情况),最大长度 路径是 MAX_PATH,定义为 260 个字符。本地人 路径按以下顺序构建:驱动器号、冒号、 反斜杠,由反斜杠分隔的名称组件和终止 空字符。例如,驱动器 D 上的最大路径是“D:\some 256 个字符的路径字符串" 其中 "" 表示不可见 当前系统代码页的终止空字符。 (这 字符 在此处用于视觉清晰,不能作为其中的一部分 一个有效的路径字符串。) 注意 Windows API 中的文件 I/O 函数 将“/”转换为“\”作为将名称转换为 NT 样式名称的一部分, 除非使用下面详述的“\?\”前缀 部分。 Windows API 有许多也有 Unicode 的函数 允许最大总路径的扩展长度路径的版本 长度为 32,767 个字符。这种类型的路径由 由反斜杠分隔的组件,每个组件都达到返回的值 GetVolumeInformation 的 lpMaximumComponentLength 参数 函数(此值通常为 255 个字符)。指定一个 扩展长度路径,使用“\?\”前缀。例如,“\?\D:\very 长路径”。注意 32,767 个字符的最大路径是 近似值,因为“\?\”前缀可能会扩展为更长的 系统在运行时字符串,此扩展适用于 总长度。

我认为关于Win32 File NameSpaces的部分值得一试:

对于文件 I/O,路径字符串的“\?\”前缀告诉 Windows 用于禁用所有字符串解析并发送后面的字符串的 API 它直接到文件系统。例如,如果文件系统 支持大路径和文件名,可以超过 MAX_PATH Windows API 强制执行的限制。更多 有关正常最大路径限制的信息,请参阅前面的 部分最大路径长度限制。

必须有一个 Win32 API 函数,您可以DECLARE 并使用,但这不是使用DIR 函数。抱歉,手头没有长路径名来测试任何东西...

【讨论】:

    【解决方案2】:

    这里有一些代码无论深度如何都应该可以工作...... 基本上,它指定了相对路径——所以你永远不会用长字符串调用dir

    Function deepFileExists(longFileName As String)
    ' slowly make your way to the deepest folder...
    ' assuming "\" is used as separator
    ' you could add some code to replace "/" with "\"...
    
    Dim pathFragment As String, currentDir As String
    Dim slash As Integer, lastSlash As Integer
    
    slash = InStr(1, longFileName, "\")
    lastSlash = 0
    
    pathFragment = Mid(longFileName, 1, slash - 1)
    
    currentDir = CurDir        ' save the current directory
    ChDrive pathFragment       ' making sure we have the right drive
    ChDir pathFragment & "\"   ' be at the root of this drive's directory
    
    lastSlash = slash
    slash = InStr(slash + 1, longFileName, "\")
    
    While (slash > 0)
      pathFragment = ".\" & Mid(longFileName, lastSlash + 1, slash - lastSlash)
      ChDir pathFragment
      'MsgBox "changing directory to " & pathFragment
      lastSlash = slash
      slash = InStr(slash + 1, longFileName, "\")
    Wend
    
    ' now we can look for the file:
    Dim a
    a = Dir(Mid(longFileName, lastSlash + 1))
    If Len(a) > 0 Then
      deepFileExists = True
    Else
      deepFileExists = False
    End If
    
    End Function
    

    【讨论】:

    • 在尝试之前赞成这个答案......它在我的情况下不起作用:(当(绝对)路径越过 260- 时,ChDir pathFragment 行上的错误 76“找不到路径”字符阈值。
    • @raph82 - 这很奇怪...您使用的是什么平台(操作系统版本,Office)?
    【解决方案3】:

    我无法对此进行测试,因此您所拥有的只是一些关于可能方法的粗略说明。

    ''Reference: Windows Script Host Object Model
    Dim fs As New FileSystemObject
    Dim fl As Folder
    Dim fl2 As Folder
    
    Set fl = fs.GetFolder("Z:\Docs\test\ThisIsInOrderToCreate\ALongFilePath\")
    Set fl2 = fl.SubFolders("WithASubFolder")
    Debug.Print fl2.ShortPath
    For Each File In fl2.Files
        If File.Name = "file.txt" Then
            Debug.Print "Found"
        End If
    Next
    
    ''May be possible
    a = Dir(fl.ShortPath & "\file.*")
    

    另外,关于上面的评论:

    Set WshNetwork = CreateObject("WScript.Network")
    WshNetwork.MapNetworkDrive "L:", "\\mydrive\share"
    ''Important to destroy when you are finished
    Set WshNetwork = Nothing
    

    【讨论】:

      【解决方案4】:

      我找到了这个 MS 页面: Naming Files, Paths, and Namespaces

      最大路径长度限制 在 Windows API(以下段落中讨论的一些例外情况)中,路径的最大长度为 MAX_PATH,定义为 260 个字符。本地路径按以下顺序构造:驱动器号、冒号、反斜杠、由反斜杠分隔的名称组件和终止空字符。例如,驱动器 D 上的最大路径是“D:\some 256-character path string”,其中“”表示当前系统代码页的不可见终止空字符。 (字符 在此处用于视觉清晰,不能作为有效路径字符串的一部分。) 注意 Windows API 中的文件 I/O 函数将“/”转换为“\”作为将名称转换为 NT 样式名称的一部分,除非使用“\?\”前缀,如以下部分所述。

      Windows API 有许多函数也有 Unicode 版本,以允许最大总路径长度为 32,767 个字符的扩展长度路径。这种类型的路径由用反斜杠分隔的组件组成,每个组件的最大长度为 GetVolumeInformation 函数的 lpMaximumComponentLength 参数中返回的值(该值通常为 255 个字符)。要指定扩展长度的路径,请使用“\?\”前缀。例如,“\?\D:\非常长的路径”。 注意 最大路径 32,767 个字符是近似值,因为“\?\”前缀可能会在运行时被系统扩展为更长的字符串,并且此扩展适用于总长度。

      “\?\”前缀也可以用于根据通用命名约定 (UNC) 构建的路径。要使用 UNC 指定此类路径,请使用“\?\UNC\”前缀。例如,“\?\UNC\server\share”,其中“server”是计算机的名称,“share”是共享文件夹的名称。这些前缀不用作路径本身的一部分。它们表明路径应该以最少的修改传递给系统,这意味着您不能使用正斜杠来表示路径分隔符,或者使用句点来表示当前目录,或者使用双点来表示父目录。因为您不能将“\?\”前缀与相对路径一起使用,所以相对路径总是被限制为总共 MAX_PATH 个字符。

      因此,对于很长的 UNC 路径,我将路径的开头更改为如下所示,它可以正常工作。

         Const MAX_PATH_LENGTH As Integer = 260
      
          If Len(fname) > MAX_PATH_LENGTH Then
              fname = "\\?\UNC\" & Mid$(fname, 3)
          End If
          Set fsoObject = New Scripting.FileSystemObject
          FileExists = fsoObject.FileExists(fname)
      

      【讨论】:

        【解决方案5】:

        由于我无法回复包含 deepfileexists 代码的评论,这里修改了代码,以便您可以找到网络路径(因为他回答说他有网络位置)

        您需要一个调用 system32 的函数来执行到网络驱动器的直接路径。我从here得到代码

        这里是代码,在模块顶部插入私有函数,否则它将不起作用。它使函数专门与该模块相关联,如果您想将其打开到整个工作簿,请关闭私有。

        Private Declare Function SetCurrentDirectoryA Lib "kernel32" _
            (ByVal lpPathName As String) As Long
        

        然后是带有修改代码的函数,以在有以 \ 开头的网络驱动器时接受该函数

        Function deepFileExists(longFileName As String)
        ' slowly make your way to the deepest folder...
        ' assuming "\" is used as separator
        ' you could add some code to replace "/" with "\"...
        
        
        Dim pathFragment As String, currentDir As String
        Dim slash As Integer, lastSlash As Integer
        
        If Left(longFileName, 2) = "\\" Then
            slash = InStr(3, longFileName, "\")
            Else
            slash = InStr(1, longFileName, "\")
        End If
        
        lastSlash = 0
        
        pathFragment = Mid(longFileName, 1, slash - 1)
        
        currentDir = CurDir        ' save the current directory
        If Not Left(pathFragment, 2) = "\\" Then
            ChDrive pathFragment       ' making sure we have the right drive
            ChDir pathFragment & "\"   ' be at the root of this drive's directory
            Else
                SetCurrentDirectoryA (pathFragment)
        End If
        
        lastSlash = slash
        slash = InStr(slash + 1, longFileName, "\")
        
        While (slash > 0)
            pathFragment = ".\" & Mid(longFileName, lastSlash + 1, slash - lastSlash)
            If Not Left(longFileName, 2) = "\\" Then
                ChDir pathFragment
            Else
                SetCurrentDirectoryA (pathFragment)
            End If
            'MsgBox "changing directory to " & pathFragment
            lastSlash = slash
            slash = InStr(slash + 1, longFileName, "\")
        Wend
        
        ' now we can look for the file:
        Dim a As String
        Dim something As String
        something = Mid(longFileName, lastSlash + 1)
        
        a = Dir(something)
        If Len(a) > 0 Then
            deepFileExists = True
        Else
            deepFileExists = False
        End If
        
        End Function
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-07-02
          • 1970-01-01
          • 1970-01-01
          • 2019-04-11
          相关资源
          最近更新 更多