microsoft-visualbasic-runtime/Extensions/IO/Extensions/PathExtensions.vb

951 lines
36 KiB
VB.net
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#Region "Microsoft.VisualBasic::9159e0f9fe95dd49cfa4575036194c42, Microsoft.VisualBasic.Core\Extensions\IO\Extensions\PathExtensions.vb"
' Author:
'
' asuka (amethyst.asuka@gcmodeller.org)
' xie (genetics@smrucc.org)
' xieguigang (xie.guigang@live.com)
'
' Copyright (c) 2018 GPL3 Licensed
'
'
' GNU GENERAL PUBLIC LICENSE (GPL3)
'
'
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
' GNU General Public License for more details.
'
' You should have received a copy of the GNU General Public License
' along with this program. If not, see <http://www.gnu.org/licenses/>.
' /********************************************************************************/
' Summaries:
' Module PathExtensions
'
' Function: BaseName, ChangeSuffix, DeleteFile, DIR, DirectoryExists
' DirectoryName, EnumerateFiles, (+2 Overloads) ExtensionSuffix, FileCopy, (+2 Overloads) FileExists
' FileLength, FileMove, FileName, FileOpened, GetDirectoryFullPath
' GetFullPath, ListDirectory, ListFiles, Long2Short, (+2 Overloads) NormalizePathString
' ParentDirName, ParentPath, PathCombine, PathIllegal, ReadDirectory
' (+2 Overloads) RelativePath, SafeCopyTo, SplitPath, TheFile, ToDIR_URL
' ToFileURL, TrimDIR, TrimSuffix, UnixPath
'
' Sub: MkDIR
'
' /********************************************************************************/
#End Region
Imports System.IO
Imports System.Math
Imports System.Reflection
Imports System.Runtime.CompilerServices
Imports System.Text
Imports Microsoft.VisualBasic.CommandLine.Reflection
Imports Microsoft.VisualBasic.FileIO
Imports Microsoft.VisualBasic.Language.Default
Imports Microsoft.VisualBasic.Language.UnixBash
Imports Microsoft.VisualBasic.Linq
Imports Microsoft.VisualBasic.Linq.Extensions
Imports Microsoft.VisualBasic.Scripting.MetaData
Imports Microsoft.VisualBasic.Serialization.JSON
Imports Microsoft.VisualBasic.Text
''' <summary>
''' Search the path from a specific keyword.(通过关键词来推测路径)
''' </summary>
''' <remarks></remarks>
<Package("Program.Path.Search", Description:="A utility tools for searching a specific file of its path on the file system more easily.")>
Public Module PathExtensions
''' <summary>
''' 修改路径字符串之中的文件名后缀拓展名为<paramref name="newSuffix"/>指定的值。<paramref name="newSuffix"/>不需要添加小数点
''' </summary>
''' <param name="path$"></param>
''' <param name="newSuffix$">新的文件拓展名,这个拓展名不带有小数点,例如需要修改为*.csv则直接赋值csv即可</param>
''' <returns></returns>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function ChangeSuffix(path$, newSuffix$) As String
Return path.TrimSuffix & "." & newSuffix
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function SplitPath(path As String) As String()
Return path.Replace("/"c, "\"c) _
.StringReplace("\\{2,}", "\") _
.Trim("\"c) _
.Split("\"c)
End Function
''' <summary>
''' Execute file delete
''' </summary>
''' <param name="path">The file path or the directory path.</param>
''' <param name="throwEx"></param>
''' <returns></returns>
<Extension>
Public Function DeleteFile(path$, Optional throwEx As Boolean = False) As Boolean
Try
If path.FileExists Then
Call FileIO.FileSystem.DeleteFile(
path, UIOption.OnlyErrorDialogs, RecycleOption.DeletePermanently
)
ElseIf path.DirectoryExists Then
Call FileIO.FileSystem.DeleteDirectory(
path, DeleteDirectoryOption.DeleteAllContents
)
End If
Return True
Catch ex As Exception
If throwEx Then
Throw New Exception(path, ex)
Else
Call App.LogException(ex, path)
End If
Return False
Finally
End Try
End Function
''' <summary>
''' 函数返回文件的拓展名后缀,请注意,这里的返回值是不会带有小数点的
''' </summary>
''' <param name="path$"></param>
''' <returns></returns>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function ExtensionSuffix(path$) As String
If path.StringEmpty Then
Return ""
Else
Return path.Split("."c).Last
End If
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function ExtensionSuffix(path$, ParamArray isAny As String()) As String
Return path.ExtensionSuffix.DoCall(Function(ext) isAny.Any(Function(s) s.TextEquals(ext)))
End Function
''' <summary>
''' Combine directory path.(这个主要是用于生成文件夹名称)
'''
''' ###### Example usage
'''
''' ```vbnet
''' Dim images As Dictionary(Of String, String) =
''' (ls - l - {"*.png", "*.jpg", "*.gif"} &lt;= PlatformEngine.wwwroot.DIR("images")) _
''' .ToDictionary(Function(file) file.StripAsId,
''' AddressOf FileName)
''' ```
''' </summary>
''' <param name="d"></param>
''' <param name="name"></param>
''' <returns></returns>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function DIR(d As DirectoryInfo, name$) As String
Return $"{d.FullName}/{name}"
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function UnixPath(path As String) As String
Return FileIO.FileSystem.GetFileInfo(path).FullName.Replace("\", "/")
End Function
''' <summary>
''' Make directory
''' </summary>
''' <param name="DIR"></param>
<Extension> Public Sub MkDIR(DIR$, Optional throwEx As Boolean = True)
If DIR.StringEmpty OrElse DIR = "./" OrElse DIR = ".\" Then
' 2017-12-25
' 当前文件夹
' 因为假若能够切换到当前文件夹的话,说明当前的文件夹已经存在了
' 则在这里是否应该跳过这个创建过程
' 还是不跳过吧
DIR = App.CurrentDirectory
End If
Try
Call FileIO.FileSystem.CreateDirectory(DIR)
Catch ex As Exception
ex = New Exception("DIR value is: " & DIR, ex)
If throwEx Then
Throw ex
Else
Call App.LogException(ex)
End If
End Try
End Sub
<Extension>
Public Function PathCombine(path As String, addTag As String) As String
If path.DirectoryExists Then
Return path.ParentPath & "/" & path.BaseName & addTag
Else
Return path.TrimSuffix & addTag
End If
End Function
ReadOnly allKinds As New [Default](Of String())({"*.*"}, Function(o) TryCast(o, String()).IsNullOrEmpty)
''' <summary>
''' 使用<see cref="FileIO.FileSystem.GetFiles"/>函数枚举
''' **当前的**(不是递归的搜索所有的子文件夹)文件夹之中的
''' 所有的符合条件的文件路径
''' </summary>
''' <param name="DIR">文件夹路径</param>
''' <param name="keyword">
''' Default is ``*.*`` for match any kind of files.
''' (文件名进行匹配的关键词)
''' </param>
''' <returns></returns>
<Extension>
Public Function EnumerateFiles(DIR$, ParamArray keyword$()) As IEnumerable(Of String)
Return FileIO.FileSystem.GetFiles(DIR, FileIO.SearchOption.SearchTopLevelOnly, keyword Or allKinds)
End Function
''' <summary>
''' ```
''' ls - l - r - pattern &lt;= DIR
''' ```
'''
''' 的简化拓展函数模式
''' </summary>
''' <param name="DIR$"></param>
''' <param name="pattern$"></param>
''' <returns></returns>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function ListFiles(DIR$, Optional pattern$ = "*.*") As IEnumerable(Of String)
Return ls - l - r - pattern <= DIR
End Function
''' <summary>
''' 这个函数是会直接枚举出所有的文件路径的
''' </summary>
''' <param name="DIR$"></param>
''' <param name="[option]"></param>
''' <returns></returns>
<Extension>
Public Iterator Function ReadDirectory(DIR$, Optional [option] As FileIO.SearchOption = FileIO.SearchOption.SearchTopLevelOnly) As IEnumerable(Of String)
Dim current As New DirectoryInfo(DIR)
For Each file In current.EnumerateFiles
Yield file.FullName
Next
If [option] = FileIO.SearchOption.SearchAllSubDirectories Then
For Each folder In current.EnumerateDirectories
For Each path In folder.FullName.ReadDirectory([option])
Yield path
Next
Next
End If
End Function
''' <summary>
''' Yield subfolders' FullName
''' </summary>
''' <param name="DIR">文件夹不存在,则返回空的列表</param>
''' <param name="[option]"></param>
''' <returns></returns>
<Extension>
Public Iterator Function ListDirectory(DIR$,
Optional [option] As FileIO.SearchOption = FileIO.SearchOption.SearchTopLevelOnly,
Optional fullName As Boolean = True) As IEnumerable(Of String)
Dim current As New DirectoryInfo(DIR)
If Not current.Exists Then
' 文件夹不存在,则返回空的列表
Return
End If
For Each folder In current.EnumerateDirectories
If fullName Then
Yield folder.FullName
Else
Yield folder.Name
End If
If [option] = FileIO.SearchOption.SearchAllSubDirectories Then
For Each path In folder.FullName.ListDirectory([option], fullName)
Yield path
Next
End If
Next
End Function
''' <summary>
''' 这个函数只会返回文件列表之中的第一个文件,故而需要提取某一个文件夹之中的某一个特定的文件,推荐使用这个函数(这个函数默认只查找第一级文件夹,不会进行递归搜索)
''' </summary>
''' <param name="DIR$"></param>
''' <param name="keyword$"></param>
''' <param name="opt"></param>
''' <param name="wildcard">The <paramref name="keyword"/> parameter value is a wildcard.</param>
''' <returns>当查找不到目标文件或者文件夹不存在的时候会返回空值</returns>
<Extension>
Public Function TheFile(DIR$, keyword$,
Optional opt As FileIO.SearchOption = FileIO.SearchOption.SearchTopLevelOnly,
Optional wildcard As Boolean = True) As String
If Not DIR.DirectoryExists Then
Return Nothing
Else
Dim check As Func(Of String, Boolean) = Nothing
If Not wildcard Then
check = Function(path)
Return path.FileName.TextEquals(keyword)
End Function
End If
If opt = FileIO.SearchOption.SearchAllSubDirectories Then
If wildcard Then
Return (ls - l - r - keyword <= DIR).FirstOrDefault
Else
Return (ls - l - r <= DIR).FirstOrDefault(check)
End If
Else
If wildcard Then
Return (ls - l - keyword <= DIR).FirstOrDefault
Else
Return (ls - l <= DIR).FirstOrDefault(check)
End If
End If
End If
End Function
''' <summary>
''' Gets the URL type file path.(获取URL类型的文件路径)
''' </summary>
''' <param name="Path"></param>
''' <returns></returns>
''' <remarks></remarks>
'''
<ExportAPI("Path2Url")>
<Extension> Public Function ToFileURL(path As String) As String
If String.IsNullOrEmpty(path) Then
Return ""
Else
path = FileIO.FileSystem.GetFileInfo(path).FullName
Return String.Format("file:///{0}", path.Replace("\", "/"))
End If
End Function
<ExportAPI("DIR2URL"), ExtensionAttribute>
Public Function ToDIR_URL(DIR As String) As String
If String.IsNullOrEmpty(DIR) Then
Return ""
Else
DIR = FileIO.FileSystem.GetDirectoryInfo(DIR).FullName
Return String.Format("file:///{0}", DIR.Replace("\", "/"))
End If
End Function
''' <summary>
''' 枚举所有非法的路径字符
''' </summary>
''' <remarks></remarks>
Public Const ILLEGAL_PATH_CHARACTERS As String = ":*?""<>|&"
Public Const ILLEGAL_FILENAME_CHARACTERS As String = "\/" & ILLEGAL_PATH_CHARACTERS
''' <summary>
''' 将目标字符串之中的非法的字符替换为"_"符号以成为正确的文件名字符串。当参数<paramref name="alphabetOnly"/>为真的时候,意味着所有的非字母或者数字的字符都会被替换为下划线,默认为真
''' </summary>
''' <param name="str"></param>
''' <param name="alphabetOnly">当本参数为真的时候仅26个字母0-9数字和下划线_以及小数点可以被保留下来</param>
''' <returns></returns>
''' <remarks></remarks>
<ExportAPI("NormalizePathString")>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension> Public Function NormalizePathString(str$, Optional alphabetOnly As Boolean = True) As String
Return NormalizePathString(str, "_", alphabetOnly)
End Function
<ExportAPI("NormalizePathString")>
<Extension> Public Function NormalizePathString(str$, normAs As String, Optional alphabetOnly As Boolean = True) As String
Dim sb As New StringBuilder(str)
For Each ch As Char In ILLEGAL_FILENAME_CHARACTERS
Call sb.Replace(ch, normAs)
Next
If alphabetOnly Then
For Each ch As Char In "()[]+-~!@#$%^&=;',"
Call sb.Replace(ch, normAs)
Next
End If
Return sb.ToString
End Function
Const PathTooLongException =
"System.IO.PathTooLongException: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
''' <summary>
''' 假设文件名过长发生在文件名和最后一个文件夹的路径之上
''' </summary>
''' <param name="path"></param>
''' <returns></returns>
''' <remarks>
''' System.IO.PathTooLongException: The specified path, file name, or both are too long.
''' The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
''' </remarks>
<Extension> Public Function Long2Short(path As String, <CallerMemberName> Optional caller As String = "") As String
Dim parent As String = path.ParentPath
Dim DIRTokens As String() = parent.Replace("\", "/").Split("/"c)
' 请注意由于path参数可能是相对路径所以在这里DIRname和name要分开讨论
Dim DIRname As String = DIRTokens.Last
' 因为相对路径最终会出现文件夹名称但在path里面可能是使用小数点来表示的
Dim name As String = path.Replace("\", "/").Split("/"c).Last
If parent.Length + name.Length >= 259 Then
DIRname = Mid(DIRname, 1, 20) & "~"
Dim ext As String = name.Split("."c).Last
name = Mid(name, 1, 20) & "~." & ext
parent = String.Join("/", DIRTokens.Take(DIRTokens.Length - 1).ToArray)
parent &= "/" & DIRname
parent &= "/" & name
Dim ex As Exception = New PathTooLongException(PathTooLongException)
ex = New Exception(path, ex)
ex = New Exception("But the path was corrected as: " & parent & " to avoid the crashed problem.", ex)
Call ex.PrintException
Call App.LogException(ex, caller & " -> " & MethodBase.GetCurrentMethod.GetFullName)
Return parent.Replace("\", "/")
Else
Return FileIO.FileSystem.GetFileInfo(path).FullName
End If
End Function
''' <summary>
''' + C:\
''' + AB:\
''' + AB2:\
''' + etc...
''' </summary>
Const DriveLabel$ = "[a-zA-Z]([a-zA-Z0-9])*"
''' <summary>
''' File path illegal?
''' </summary>
''' <param name="path"></param>
''' <returns></returns>
<ExportAPI("Path.Illegal?")>
<Extension> Public Function PathIllegal(path As String) As Boolean
Dim tokens$() = Strings.Split(path.Replace("\", "/"), ":/")
If tokens.Length > 2 Then ' 有多余一个的驱动器符,则肯定是非法的路径格式
Return False
ElseIf tokens.Length = 2 Then
' 完整路径
' 当有很多个驱动器的时候,这里会不止一个字母
If Not tokens(0).IsPattern(DriveLabel, RegexICSng) Then
' 开头的驱动器的符号不正确
Return False
Else
' 驱动器的符号也正确
End If
Else
' 只有一个,则是相对路径
End If
Dim fileName As String = tokens.Last
' 由于这里是判断文件是否合法所以之判断文件名就行了即token列表的最后一个元素
For Each ch As Char In ILLEGAL_PATH_CHARACTERS
If fileName.IndexOf(ch) > -1 Then
Return True
End If
Next
Return False
End Function
''' <summary>
''' Gets the file length, if the path is not exists, then returns -1.
''' (安全的获取文件的大小,如果目标文件不存在的话会返回-1)
''' </summary>
''' <param name="path"></param>
''' <returns></returns>
<Extension>
Public Function FileLength(path As String) As Long
If Not path.FileExists Then
Return -1&
Else
Return FileIO.FileSystem.GetFileInfo(path).Length
End If
End Function
''' <summary>
''' Safe file copy operation.
''' (请注意,<paramref name="copyTo"/>参数的字符串最末尾必须是``/``或者``\``才会被认作为目录路径)
''' </summary>
''' <param name="source$"></param>
''' <param name="copyTo$">
''' Can be file name or directory name.
'''
''' + If this paramter is a file path, then you can copy the
''' source file to another location with renamed.
''' + If this parameter is a directory location, then you can
''' copy the source file to another location with the
''' identical file name.
'''
''' Please notice that, the directory path should end with
''' path seperator symbol: ``\`` or ``/``.
''' </param>
''' <returns></returns>
<Extension> Public Function FileCopy(source$, copyTo$) As Boolean
Try
If copyTo.Last = "/"c OrElse copyTo.Last = "\"c Then
copyTo = copyTo & source.FileName
End If
If copyTo.FileExists Then
Call FileIO.FileSystem.DeleteFile(copyTo)
Else
Call copyTo.ParentPath.MkDIR
End If
Call FileIO.FileSystem.CopyFile(source, copyTo)
Catch ex As Exception
ex = New Exception({source, copyTo}.GetJson, ex)
App.LogException(ex)
Return False
End Try
Return True
End Function
<Extension>
Public Function FileMove(source$, target$) As Boolean
Try
Call My.Computer.FileSystem.MoveFile(source, target)
Return True
Catch ex As Exception
ex = New Exception("source: " & source, ex)
ex = New Exception("target: " & target, ex)
Call App.LogException(ex)
Return False
End Try
End Function
''' <summary>
''' Check if the target file object is exists on your file system or not.
''' (这个函数也会自动检查目标<paramref name="path"/>参数是否为空)
''' </summary>
''' <param name="path"></param>
''' <param name="ZERO_Nonexists">将0长度的文件也作为不存在</param>
''' <returns></returns>
''' <remarks></remarks>
#If FRAMEWORD_CORE Then
<ExportAPI("File.Exists")>
<Extension> Public Function FileExists(path$, Optional ZERO_Nonexists As Boolean = False) As Boolean
#Else
<Extension> Public Function FileExists(path As String) As Boolean
#End If
If path.StringEmpty Then
Return False
End If
If path.IndexOf(ASCII.CR) > -1 OrElse path.IndexOf(ASCII.LF) > -1 Then
' 包含有回车符或者换行符,则肯定不是文件路径了
Return False
End If
If Not String.IsNullOrEmpty(path) Then
Try
' 文件存在
If FileIO.FileSystem.FileExists(path) Then
If ZERO_Nonexists Then
Return FileSystem.FileLen(path) > 0
Else
Return True
End If
End If
Catch ex As Exception
Return False
End Try
End If
Return False
End Function
''' <summary>
''' Determine that the target directory is exists on the file system or not?(判断文件夹是否存在)
''' </summary>
''' <param name="DIR"></param>
''' <returns></returns>
<ExportAPI("DIR.Exists")>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function DirectoryExists(DIR As String) As Boolean
Return Not String.IsNullOrEmpty(DIR) AndAlso FileIO.FileSystem.DirectoryExists(DIR)
End Function
''' <summary>
''' Get the directory its name of the target <paramref name="dir"/> directory
''' </summary>
''' <param name="dir$"></param>
''' <returns></returns>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension> Public Function DirectoryName(dir$) As String
Return dir.TrimDIR _
.Split("\"c).Last _
.Split("/"c).Last
End Function
''' <summary>
''' Check if the file is opened by other code?(检测文件是否已经被其他程序打开使用之中)
''' </summary>
''' <param name="FileName">目标文件</param>
''' <returns></returns>
'''
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<ExportAPI("File.IsOpened")>
<Extension> Public Function FileOpened(FileName As String) As Boolean
Try
Using FileOpenDetect As New FileStream(
path:=FileName,
mode:=FileMode.Open,
access:=FileAccess.Read,
share:=FileShare.None
)
' Just detects this file is occupied, no more things needs to do....
End Using
Return True
Catch ex As Exception
Return False
End Try
End Function
''' <summary>
''' Gets the name of the target file or directory, if the target is a file, then the name without
''' the extension suffix name.
''' (获取目标文件夹的名称或者文件的不包含拓展名的名称)
''' </summary>
''' <returns></returns>
''' <remarks>
''' ###### 2017-2-14
'''
''' 原先的函数是依靠系统的API来工作的故而只能够工作于已经存在的文件之上
''' 所以在这里为了更加方便的兼容文件夹或者文件路径,在这使用字符串的方法来
''' 进行截取
''' </remarks>
<ExportAPI(NameOf(BaseName))>
<Extension> Public Function BaseName(fsObj$, Optional allowEmpty As Boolean = False) As String
If fsObj.StringEmpty Then
If allowEmpty Then
Return ""
Else
Throw New NullReferenceException(NameOf(fsObj) & " file system object handle is null!")
End If
End If
' 前面的代码已经处理好了空字符串的情况了,在这里不会出现空字符串的错误
Dim t$() = fsObj.Trim("\"c, "/"c) _
.Replace("\", "/") _
.Split("/"c) _
.Last _
.Split("."c)
If t.Length > 1 Then
' 文件名之中并没有包含有拓展名后缀则数组长度为1则不跳过了
' 有后缀拓展名则split之后肯定会长度大于1的
t = t.Take(t.Length - 1) _
.ToArray
End If
Dim name = String.Join(".", t)
Return name
End Function
''' <summary>
''' Gets the name of the file's parent directory, returns value is a name, not path.
''' (获取目标文件的父文件夹的文件夹名称,是名称而非路径)
''' </summary>
''' <param name="file"></param>
''' <returns></returns>
<Extension> Public Function ParentDirName(file As String) As String
Dim parentDir As String = FileIO.FileSystem.GetParentPath(file)
Dim parDirInfo = FileIO.FileSystem.GetDirectoryInfo(parentDir)
Return parDirInfo.Name
End Function
''' <summary>
''' Returns the directory path value of the parent directory.
''' (这个函数是返回文件夹的路径而非名称这个函数不依赖于系统的底层API
''' 因为系统的底层API对于过长的文件名会出错)
''' </summary>
''' <param name="file"></param>
''' <returns></returns>
''' <remarks>这个函数不依赖于系统的底层API因为系统的底层API对于过长的文件名会出错</remarks>
<ExportAPI(NameOf(ParentPath))>
<Extension> Public Function ParentPath(file$, Optional full As Boolean = True) As String
file = file.Replace("\", "/")
Dim parent As String = ""
Dim t As String() = file.Split("/"c)
If full Then
If InStr(file, "../") = 1 Then
parent = FileIO.FileSystem.GetParentPath(App.CurrentDirectory)
t = t.Skip(1).ToArray
parent &= "/"
ElseIf InStr(file, "./") = 1 Then
parent = App.CurrentDirectory
t = t.Skip(1).ToArray
parent &= "/"
Else
End If
If file.Last = "/"c Then ' 是一个文件夹
parent &= String.Join("/", t.Take(t.Length - 2).ToArray)
Else
parent &= String.Join("/", t.Take(t.Length - 1).ToArray)
End If
If parent.StringEmpty Then
' 用户直接输入了一个文件名,没有包含文件夹部分,则默认是当前的文件夹
parent = App.CurrentDirectory
End If
Else
parent = String.Join("/", t.Take(t.Length - 1).ToArray)
End If
Return parent
End Function
''' <summary>
''' Get the specific file system object its relative path to the application base directory.
'''
''' (获取相对于本应用程序的目标文件的相对路径(请注意,所生成的相对路径之中的字符串最后是没有文件夹的分隔符\或者/的))
''' </summary>
''' <param name="path"></param>
''' <returns></returns>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<ExportAPI(NameOf(RelativePath))>
Public Function RelativePath(path As String) As String
Return RelativePath(App.HOME, path)
End Function
''' <summary>
''' Gets the relative path of file system object <paramref name="pcTo"/> reference to the directory path <paramref name="pcFrom"/>.
''' (请注意,所生成的相对路径之中的字符串最后是没有文件夹的分隔符\或者/的)
''' </summary>
''' <param name="pcFrom">生成相对路径的参考文件夹</param>
''' <param name="pcTo">所需要生成相对路径的目标文件系统对象的绝对路径或者相对路径</param>
''' <param name="appendParent">是否将父目录的路径也添加进入相对路径之中?默认是</param>
''' <returns></returns>
<ExportAPI(NameOf(RelativePath))>
Public Function RelativePath(pcFrom$, pcTo$,
Optional appendParent As Boolean = True,
Optional fixZipPath As Boolean = False) As <FunctionReturns("The relative path string of pcTo file object reference to directory pcFrom")> String
Dim lcRelativePath As String = Nothing
Dim lcFrom As String = (If(pcFrom Is Nothing, "", pcFrom.Trim().Replace("\", "/")))
Dim lcTo As String = (If(pcTo Is Nothing, "", pcTo.Trim().Replace("\", "/")))
If lcFrom.Length = 0 OrElse lcTo.Length = 0 Then
Throw New InvalidDataException("One of the path string value is null!")
End If
If Not IO.Path.GetPathRoot(lcFrom.ToUpper()) _
.Equals(IO.Path.GetPathRoot(lcTo.ToUpper())) Then
Return pcTo
End If
' 两个路径都有值并且都在相同的驱动器下才会进行计算
Dim laDirSep As Char() = {"\"c}
Dim lcPathFrom As String = (If(IO.Path.GetDirectoryName(lcFrom) Is Nothing, IO.Path.GetPathRoot(lcFrom.ToUpper()), IO.Path.GetDirectoryName(lcFrom)))
Dim lcPathTo As String = (If(IO.Path.GetDirectoryName(lcTo) Is Nothing, IO.Path.GetPathRoot(lcTo.ToUpper()), IO.Path.GetDirectoryName(lcTo)))
Dim lcFileTo As String = (If(IO.Path.GetFileName(lcTo) Is Nothing, "", IO.Path.GetFileName(lcTo)))
Dim laFrom As String() = lcPathFrom.Split(laDirSep)
Dim laTo As String() = lcPathTo.Split(laDirSep)
Dim lnFromCnt As Integer = laFrom.Length
Dim lnToCnt As Integer = laTo.Length
Dim lnSame As Integer = 0
Dim lnCount As Integer = 0
While lnToCnt > 0 AndAlso lnSame < lnToCnt
If lnCount < lnFromCnt Then
If laFrom(lnCount).ToUpper().Equals(laTo(lnCount).ToUpper()) Then
lnSame += 1
Else
Exit While
End If
Else
Exit While
End If
lnCount += 1
End While
Dim lcEndPart As String = ""
For lnEnd As Integer = lnSame To lnToCnt - 1
If laTo(lnEnd).Length > 0 Then
lcEndPart += laTo(lnEnd) & "\"
Else
Exit For
End If
Next
Dim lnDiff As Integer = Abs(lnFromCnt - lnSame)
If lnDiff > 0 AndAlso laFrom(lnFromCnt - 1).Length > 0 Then
While lnDiff > 0
lnDiff -= 1
lcEndPart = "..\" & lcEndPart
End While
End If
lcRelativePath = lcEndPart & lcFileTo
If appendParent Then
Return "..\" & lcRelativePath
Else
If fixZipPath Then
' 2017-8-26
' 为Xlsx打包模块进行的修复
Return lcRelativePath _
.Split("\"c) _
.Skip(1) _
.JoinBy("\")
Else
Return lcRelativePath
End If
End If
End Function
''' <summary>
''' Gets the full path of the specific file.(为了兼容Linux这个函数会自动替换路径之中的\为/符号)
''' </summary>
''' <param name="file"></param>
''' <returns></returns>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<ExportAPI("File.FullPath")>
<Extension> Public Function GetFullPath(file As String) As String
Return FileIO.FileSystem.GetFileInfo(file).FullName.Replace("\", "/")
End Function
''' <summary>
''' Gets the full path of the specific directory.
''' (这个函数为了兼容linux的文件系统也会自动的将所有的``\``替换为``/``)
''' </summary>
''' <param name="dir"></param>
''' <param name="stack">当程序出错误的时候记录进入日志的一个追踪目标参数,调试用</param>
''' <returns></returns>
<ExportAPI("Dir.FullPath")>
<Extension> Public Function GetDirectoryFullPath(dir$, <CallerMemberName> Optional stack$ = Nothing) As String
Try
Return FileIO.FileSystem _
.GetDirectoryInfo(dir) _
.FullName _
.Replace("\", "/")
Catch ex As Exception
stack = stack & " --> " & NameOf(GetDirectoryFullPath)
If dir = "/" AndAlso Not App.IsMicrosoftPlatform Then
Return "/" ' Linux上面已经是全路径了root
Else
ex = New Exception(stack & ": " & dir, ex)
Call App.LogException(ex)
Call ex.PrintException
Return dir
End If
End Try
End Function
''' <summary>
''' Removes the file extension name from the file path.(去除掉文件的拓展名)
''' </summary>
''' <param name="file"></param>
''' <returns></returns>
<ExportAPI("File.Ext.Trim")>
<Extension> Public Function TrimSuffix(file As String) As String
Dim tokens$() = file.Replace("\"c, "/"c).Split("/"c)
Dim fileNameTokens = tokens.Last.Split("."c)
Dim directory = tokens.Take(tokens.Length - 1).JoinBy("/")
Dim fileName$ = fileNameTokens.Take(fileNameTokens.Length - 1).JoinBy(".")
Return $"{directory}/{fileName}"
End Function
''' <summary>
''' Removes the last \ and / character in a directory path string.
''' (使用这个函数修剪文件夹路径之中的最后一个分隔符,以方便生成文件名)
''' </summary>
''' <param name="DIR"></param>
''' <returns></returns>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function TrimDIR(DIR As String) As String
Return DIR.TrimEnd("/"c, "\"c)
End Function
''' <summary>
''' 返回``文件名称.拓展名``
''' </summary>
''' <param name="path"></param>
''' <returns></returns>
''' <remarks>
''' 这个函数为单纯的字符串解析函数不依赖于文件系统的API
''' </remarks>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<ExportAPI("File.Name")>
<Extension>
Public Function FileName(path As String) As String
If path.StringEmpty Then
Return ""
Else
Return path.StringSplit("(\\|/)").Last
End If
End Function
''' <summary>
''' 进行安全的复制,出现错误不会导致应用程序崩溃,大文件不推荐使用这个函数进行复制
''' </summary>
''' <param name="source"></param>
''' <param name="copyTo"></param>
''' <returns></returns>
<ExportAPI("SafeCopyTo")>
Public Function SafeCopyTo(source As String, copyTo As String) As Boolean
Try
Dim buf As Byte() = IO.File.ReadAllBytes(source)
Call buf.FlushStream(copyTo)
Catch ex As Exception
Dim pt As String = $"{source.ToFileURL} ===> {copyTo.ToFileURL}"
Call App.LogException(New Exception(pt, ex))
Return False
End Try
Return True
End Function
End Module