我当前的项目需要处理很多unix风格的路径,最重要的是加入路径和规范化路径。
通过规范化,我的意思是删除对当前目录(.
)的所有引用,对父目录(..
)的引用和冗余正斜杠(//
),同时保留原意路径的“含义”。
例如:路径/foo/bar
和/foo/./bar
以及/foo/baz/../bar
都指向同一目录。但是,进行简单的字符串比较显然表明它们是不同的路径。这就是为什么我试图规范路径,以便它们都被视为相同。
执行此操作的代码实际上并不是非常难以编写。您可以在问题的底部找到我的代码。但是仍有一个问题我遇到了麻烦。
目前,用户可以输入如下所示的路径:
input: /../../../foo/bar
normalized: /foo/bar
因为这是一个绝对路径,所以它正确地解析为根(foo/bar
)目录中的/
。
但是,当输入是相对路径时,我不可能回溯所需的步骤量,因为我不知道父目录的名称。
input: ../../../foo/bar
normalized: foo/bar
想象一下,完整路径是/a/b/c/d/../../../foo/bar
,在这种情况下算法产生:
input: /a/b/c/d/../../../foo/bar
normalized: /a/foo/bar
当出于某种原因,路径被分为/a/b/c/d
和../../../foo/bar
时,就会出现问题。
input: /a/b/c/d/
input: ../../../foo/bar
normalized: /a/b/c/d
normalized: foo/bar
joined: /a/b/c/d/foo/bar
正如您所看到的,当标准化输出值重新连接在一起时,路径已失去其原始含义。如果我没有删除前导父引用,就不会发生这种情况。
所以我猜我有三个选项,如果是具有未知父引用的相对路径:
我希望有些天才能想出更好的主意。但如果没有,你会做什么?
我还没有针对每个可能的用例测试我的代码,但它应该相对(双关语)稳定。
Public MustInherit Class UnixPath
Private Sub New()
End Sub
''' <summary>
''' Gets whether the specified path is an absolute path.
''' </summary>
Public Shared Function IsAbsolute(path As String) As Boolean
Return path.StartsWith(UnixPath.Separator, StringComparison.InvariantCulture)
End Function
''' <summary>
''' Normalizes a string path, taking care of '..' and '.' parts.
''' </summary>
Public Shared Function Normalize(path As String) As String
If String.IsNullOrEmpty(path) Then
Return String.Empty
End If
Dim oldPath = path.Split(New Char() {UnixPath.Separator}, StringSplitOptions.RemoveEmptyEntries)
Dim newPath As New Stack(Of String)
Dim skipCount As Integer = 0
For i = oldPath.GetUpperBound(0) To oldPath.GetLowerBound(0) Step -1
If String.Equals(oldPath(i), UnixPath.CurrentDirectory, StringComparison.InvariantCulture) Then
Continue For
ElseIf String.Equals(oldPath(i), UnixPath.ParentDirectory, StringComparison.InvariantCulture) Then
skipCount += 1
ElseIf skipCount > 0 Then
skipCount -= 1
Else
newPath.Push(oldPath(i))
End If
Next
If UnixPath.IsAbsolute(path) Then
Return UnixPath.Join(UnixPath.Separator, UnixPath.Join(newPath.ToArray))
Else
For i = 1 To skipCount
newPath.Push(UnixPath.ParentDirectory)
Next
Return UnixPath.Join(newPath.ToArray)
End If
End Function
''' <summary>
''' Combines an array of string paths.
''' </summary>
Public Shared Function Join(ParamArray paths As String()) As String
Dim builder As New StringBuilder
Dim count = paths.GetUpperBound(0)
For i = paths.GetLowerBound(0) To count
If String.IsNullOrEmpty(paths(i)) Then
Continue For
End If
builder.Append(paths(i).TrimEnd(UnixPath.Separator))
If i = count Then
Exit For
End If
builder.Append(UnixPath.Separator)
Next
Return builder.ToString
End Function
Public Shared ReadOnly Property CurrentDirectory As String
Get
Return "."
End Get
End Property
Public Shared ReadOnly Property ParentDirectory As String
Get
Return ".."
End Get
End Property
Public Shared ReadOnly Property Separator As Char
Get
Return "/"c
End Get
End Property
End Class
答案 0 :(得分:0)
仅删除重复的..
和.
,才能删除/
,不会保留原始路径的含义。但是,目前尚不清楚你想要做什么。可能你真的不需要以这种方式“规范化”你的路径。
无论如何,这是一个想法:
Dim inputPath As String = "/../../../foo/bar"
Dim outputPath As String = inputPath.Replace(".", "").Replace("..", "")
Dim outputPathLength As Integer
Do
outputPathLength = outputPath.Length
outputPath = outputPath.Replace("//", "/")
Loop Until outputPathLength = outputPath.Length
为了更好的实现 - 一个也会考虑你的情况#2,还有更多,你应该使用正则表达式。
编辑:可能会被某些人视为黑客攻击,但这是另一种选择:
Dim inputPath As String = "../../../foo/bar"
Dim outPath As String = IO.Path.GetFullPath(IO.Path.Combine("C:\", inputPath))
Dim newPath As String = outPath.Substring(outPath.IndexOf("\") + 1).Replace("\", "/")
If inputPath.StartsWith("/") Then newPath = "/" & newPath
MsgBox(newPath)
它使用文件系统规范化路径,并转换回unix样式。适用于上述所有测试用例。