1 #Region "Microsoft.VisualBasic::1c42bd61c25b24e15da0d0b8a9122cae, Microsoft.VisualBasic.Core\Extensions\IO\PathSearchTool.vb"
2
3 ' Author:
4
5 '       asuka (amethyst.asuka@gcmodeller.org)
6 '       xie (genetics@smrucc.org)
7 '       xieguigang (xie.guigang@live.com)
8
9 ' Copyright (c) 2018 GPL3 Licensed
10
11
12 ' GNU GENERAL PUBLIC LICENSE (GPL3)
13
14
15 ' This program is free software: you can redistribute it and/or modify
16 ' it under the terms of the GNU General Public License as published by
17 ' the Free Software Foundation, either version 3 of the License, or
18 ' (at your option) any later version.
19
20 ' This program is distributed in the hope that it will be useful,
21 ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23 ' GNU General Public License for more details.
24
25 ' You should have received a copy of the GNU General Public License
26 ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30 ' /********************************************************************************/
31
32 ' Summaries:
33
34 Module ProgramPathSearchTool
35
36 '     Function: BaseName, BatchMd5Renamed, BranchRule, ChangeSuffix, Delete
37 '               DIR, DirectoryExists, DirectoryName, EnumerateFiles, ExtensionSuffix
38 '               FileCopy, (+2 Overloads) FileExists, FileLength, FileMove, FileName
39 '               FileOpened, GetBaseName, GetDirectoryFullPath, GetFile, GetFullPath
40 '               GetMostAppreancePath, ListDirectory, ListFiles, LoadEntryList, (+3 Overloads) LoadSourceEntryList
41 '               Long2Short, (+2 Overloads) NormalizePathString, ParentDirName, ParentPath, PathCombine
42 '               PathIllegal, ReadDirectory, (+2 Overloads) RelativePath, SafeCopyTo, SearchDirectory
43 '               SearchDrive, SearchProgram, SearchScriptFile, SourceCopy, SplitPath
44 '               TheFile, ToDIR_URL, ToFileURL, TrimDIR, TrimSuffix
45 '               UnixPath
46
47 '     Sub: MkDIR
48
49 ' /********************************************************************************/
50
51 #End Region
52
53 Imports System.Collections.ObjectModel
54 Imports System.IO
55 Imports System.Math
56 Imports System.Reflection
57 Imports System.Runtime.CompilerServices
58 Imports System.Text
59 Imports System.Text.RegularExpressions
60 Imports Microsoft.VisualBasic.CommandLine.Reflection
61 Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
62 Imports Microsoft.VisualBasic.FileIO
63 Imports Microsoft.VisualBasic.Language
64 Imports Microsoft.VisualBasic.Language.Default
65 Imports Microsoft.VisualBasic.Language.UnixBash
66 Imports Microsoft.VisualBasic.Linq.Extensions
67 Imports Microsoft.VisualBasic.Scripting.MetaData
68 Imports Microsoft.VisualBasic.Serialization.JSON
69 Imports Microsoft.VisualBasic.Text
70
71 ''' <summary>
72 ''' Search the path from a specific keyword.(通过关键词来推测路径)
73 ''' </summary>
74 ''' <remarks></remarks>
75 <Package("Program.Path.Search", Description:="A utility tools for searching a specific file of its path on the file system more easily.")>
76 Public Module ProgramPathSearchTool
77
78     ''' <summary>
79     ''' 修改路径字符串之中的文件名后缀拓展名为<paramref name="newSuffix"/>指定的值。<paramref name="newSuffix"/>不需要添加小数点
80     ''' </summary>
81     ''' <param name="path$"></param>
82     ''' <param name="newSuffix$">新的文件拓展名,这个拓展名不带有小数点,例如需要修改为*.csv,则直接赋值csv即可</param>
83     ''' <returns></returns>
84     ''' 
85     <MethodImpl(MethodImplOptions.AggressiveInlining)>
86     <Extension>
87     Public Function ChangeSuffix(path$, newSuffix$) As String
88         Return path.TrimSuffix & "." & newSuffix
89     End Function
90
91     <MethodImpl(MethodImplOptions.AggressiveInlining)>
92     <Extension>
93     Public Function SplitPath(path As StringAs String()
94         Return path.Replace("/"c, "\"c) _
95                    .StringReplace("\\{2,}""\") _
96                    .Trim("\"c) _
97                    .Split("\"c)
98     End Function
99
100     ''' <summary>
101     ''' Execute file delete
102     ''' </summary>
103     ''' <param name="path$"></param>
104     ''' <param name="throwEx"></param>
105     ''' <returns></returns>
106     <Extension> Public Function Delete(path$, Optional throwEx As Boolean = FalseAs Boolean
107         Try
108             If path.FileExists Then
109                 Call FileIO.FileSystem.DeleteFile(
110                    path, UIOption.OnlyErrorDialogs, RecycleOption.DeletePermanently
111                 )
112             ElseIf path.DirectoryExists Then
113                 Call FileIO.FileSystem.DeleteDirectory(
114                     path, DeleteDirectoryOption.DeleteAllContents
115                 )
116             End If
117
118             Return True
119         Catch ex As Exception
120             If throwEx Then
121                 Throw New Exception(path, ex)
122             Else
123                 Call App.LogException(ex, path)
124             End If
125
126             Return False
127         Finally
128         End Try
129     End Function
130
131     ''' <summary>
132     ''' 函数返回文件的拓展名后缀,请注意,这里的返回值是不会带有小数点的
133     ''' </summary>
134     ''' <param name="path$"></param>
135     ''' <returns></returns>
136     <MethodImpl(MethodImplOptions.AggressiveInlining)>
137     <Extension>
138     Public Function ExtensionSuffix(path$) As String
139         Return path.Split("."c).Last
140     End Function
141
142     ''' <summary>
143     ''' Combine directory path.(这个主要是用于生成文件夹名称)
144     ''' 
145     ''' ###### Example usage
146     ''' 
147     ''' ```vbnet
148     ''' Dim images As Dictionary(Of StringString) =
149     '''     (ls - l - {"*.png", "*.jpg", "*.gif"} &lt;= PlatformEngine.wwwroot.DIR("images")) _
150     '''     .ToDictionary(Function(file) file.StripAsId,
151     '''                   AddressOf FileName)
152     ''' ```
153     ''' </summary>
154     ''' <param name="d"></param>
155     ''' <param name="name"></param>
156     ''' <returns></returns>
157     <MethodImpl(MethodImplOptions.AggressiveInlining)>
158     <Extension>
159     Public Function DIR(d As DirectoryInfo, name$) As String
160         Return $"{d.FullName}/{name}"
161     End Function
162
163     <MethodImpl(MethodImplOptions.AggressiveInlining)>
164     <Extension>
165     Public Function UnixPath(path As StringAs String
166         Return FileIO.FileSystem.GetFileInfo(path).FullName.Replace("\""/")
167     End Function
168
169     ''' <summary>
170     ''' Make directory
171     ''' </summary>
172     ''' <param name="DIR"></param>
173     <Extension> Public Sub MkDIR(DIR$, Optional throwEx As Boolean = True)
174         If DIR.StringEmpty OrElse DIR = "./" OrElse DIR = ".\" Then
175             ' 2017-12-25
176             ' 当前文件夹
177             ' 因为假若能够切换到当前文件夹的话,说明当前的文件夹已经存在了
178             ' 则在这里是否应该跳过这个创建过程
179             ' 还是不跳过吧
180             DIR = App.CurrentDirectory
181         End If
182
183         Try
184             Call FileIO.FileSystem.CreateDirectory(DIR)
185         Catch ex As Exception
186             ex = New Exception("DIR value is: " & DIR, ex)
187
188             If throwEx Then
189                 Throw ex
190             Else
191                 Call App.LogException(ex)
192             End If
193         End Try
194     End Sub
195
196     <Extension>
197     Public Function PathCombine(path As String, addTag As StringAs String
198         If path.DirectoryExists Then
199             Return path.ParentPath & "/" & path.BaseName & addTag
200         Else
201             Return path.TrimSuffix & addTag
202         End If
203     End Function
204
205     ReadOnly allKinds As New DefaultValue(Of String())({"*.*"}, Function(o) TryCast(o, String()).IsNullOrEmpty)
206
207     ''' <summary>
208     ''' 使用<see cref="FileIO.FileSystem.GetFiles"/>函数枚举
209     ''' **当前的**(不是递归的搜索所有的子文件夹)文件夹之中的
210     ''' 所有的符合条件的文件路径
211     ''' </summary>
212     ''' <param name="DIR">文件夹路径</param>
213     ''' <param name="keyword">
214     ''' Default is ``*.*`` for match any kind of files.
215     ''' (文件名进行匹配的关键词)
216     ''' </param>
217     ''' <returns></returns>
218     <Extension>
219     Public Function EnumerateFiles(DIR$, ParamArray keyword$()) As IEnumerable(Of String)
220         Return FileIO.FileSystem.GetFiles(DIR, FileIO.SearchOption.SearchTopLevelOnly, keyword Or allKinds)
221     End Function
222
223     ''' <summary>
224     ''' ```
225     ''' ls - l - r - pattern &lt;= DIR
226     ''' ```
227     ''' 
228     ''' 的简化拓展函数模式
229     ''' </summary>
230     ''' <param name="DIR$"></param>
231     ''' <param name="pattern$"></param>
232     ''' <returns></returns>
233     <MethodImpl(MethodImplOptions.AggressiveInlining)>
234     <Extension>
235     Public Function ListFiles(DIR$, Optional pattern$ = "*.*"As IEnumerable(Of String)
236         Return ls - l - r - pattern <= DIR
237     End Function
238
239     ''' <summary>
240     ''' 这个函数是会直接枚举出所有的文件路径的
241     ''' </summary>
242     ''' <param name="DIR$"></param>
243     ''' <param name="[option]"></param>
244     ''' <returns></returns>
245     <Extension>
246     Public Iterator Function ReadDirectory(DIR$, Optional [option] As FileIO.SearchOption = FileIO.SearchOption.SearchTopLevelOnly) As IEnumerable(Of String)
247         Dim current As New DirectoryInfo(DIR)
248
249         For Each file In current.EnumerateFiles
250             Yield file.FullName
251         Next
252
253         If [option] = FileIO.SearchOption.SearchAllSubDirectories Then
254             For Each folder In current.EnumerateDirectories
255                 For Each path In folder.FullName.ReadDirectory([option])
256                     Yield path
257                 Next
258             Next
259         End If
260     End Function
261
262     <Extension>
263     Public Iterator Function ListDirectory(DIR$, Optional [option] As FileIO.SearchOption = FileIO.SearchOption.SearchTopLevelOnly) As IEnumerable(Of String)
264         Dim current As New DirectoryInfo(DIR)
265
266         For Each folder In current.EnumerateDirectories
267             Yield folder.FullName
268
269             If [option] = FileIO.SearchOption.SearchAllSubDirectories Then
270                 For Each path In folder.FullName.ListDirectory([option])
271                     Yield path
272                 Next
273             End If
274         Next
275     End Function
276
277     ''' <summary>
278     ''' 这个函数只会返回文件列表之中的第一个文件,故而需要提取某一个文件夹之中的某一个特定的文件,推荐使用这个函数(这个函数默认只查找第一级文件夹,不会进行递归搜索)
279     ''' </summary>
280     ''' <param name="DIR$"></param>
281     ''' <param name="keyword$"></param>
282     ''' <param name="opt"></param>
283     ''' <returns>当查找不到目标文件或者文件夹不存在的时候会返回空值</returns>
284     <Extension>
285     Public Function TheFile(DIR$, keyword$, Optional opt As FileIO.SearchOption = FileIO.SearchOption.SearchTopLevelOnly) As String
286         If Not DIR.DirectoryExists Then
287             Return Nothing
288         End If
289         Return FileIO.FileSystem.GetFiles(DIR, opt, keyword).FirstOrDefault
290     End Function
291
292     ''' <summary>
293     ''' Gets the URL type file path.(获取URL类型的文件路径)
294     ''' </summary>
295     ''' <param name="Path"></param>
296     ''' <returns></returns>
297     ''' <remarks></remarks>
298     '''
299     <ExportAPI("Path2Url"Info:="Gets the URL type file path.")>
300     <Extension> Public Function ToFileURL(Path As StringAs String
301         If String.IsNullOrEmpty(Path) Then
302             Return ""
303         Else
304             Path = FileIO.FileSystem.GetFileInfo(Path).FullName
305             Return String.Format("file:///{0}", Path.Replace("\""/"))
306         End If
307     End Function
308
309     <ExportAPI("DIR2URL"), ExtensionAttribute>
310     Public Function ToDIR_URL(DIR As StringAs String
311         If String.IsNullOrEmpty(DIR) Then
312             Return ""
313         Else
314             DIR = FileIO.FileSystem.GetDirectoryInfo(DIR).FullName
315             Return String.Format("file:///{0}", DIR.Replace("\""/"))
316         End If
317     End Function
318
319     ''' <summary>
320     ''' 枚举所有非法的路径字符
321     ''' </summary>
322     ''' <remarks></remarks>
323     Public Const ILLEGAL_PATH_CHARACTERS_ENUMERATION As String = ":*?""<>|&"
324     Public Const ILLEGAL_FILENAME_CHARACTERS As String = "\" & ILLEGAL_PATH_CHARACTERS_ENUMERATION
325
326     ''' <summary>
327     ''' 将目标字符串之中的非法的字符替换为"_"符号以成为正确的文件名字符串。当参数<paramref name="OnlyASCII"/>为真的时候,意味着所有的非字母或者数字的字符都会被替换为下划线,默认为真
328     ''' </summary>
329     ''' <param name="str"></param>
330     ''' <param name="OnlyASCII">当本参数为真的时候,仅26个字母,0-9数字和下划线_以及小数点可以被保留下来</param>
331     ''' <returns></returns>
332     ''' <remarks></remarks>
333     <ExportAPI("NormalizePathString")>
334     <MethodImpl(MethodImplOptions.AggressiveInlining)>
335     <Extension> Public Function NormalizePathString(str$, Optional OnlyASCII As Boolean = TrueAs String
336         Return NormalizePathString(str, "_"OnlyASCII)
337     End Function
338
339     <ExportAPI("NormalizePathString")>
340     <Extension> Public Function NormalizePathString(str$, normAs As StringOptional onlyASCII As Boolean = TrueAs String
341         Dim sb As New StringBuilder(str)
342         For Each ch As Char In ILLEGAL_FILENAME_CHARACTERS
343             Call sb.Replace(ch, normAs)
344         Next
345
346         If onlyASCII Then
347             For Each ch As Char In "()[]+-~!@#$%^&=;',"
348                 Call sb.Replace(ch, normAs)
349             Next
350         End If
351
352         Return sb.ToString
353     End Function
354
355     Const PathTooLongException =
356         "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."
357
358     ''' <summary>
359     ''' 假设文件名过长发生在文件名和最后一个文件夹的路径之上
360     ''' </summary>
361     ''' <param name="path"></param>
362     ''' <returns></returns>
363     ''' <remarks>
364     ''' System.IO.PathTooLongException: The specified path, file name, or both are too long.
365     ''' The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.
366     ''' </remarks>
367     <Extension> Public Function Long2Short(path As String, <CallerMemberName> Optional caller As String = ""As String
368         Dim parent As String = path.ParentPath
369         Dim DIRTokens As String() = parent.Replace("\""/").Split("/"c)
370         Dim DIRname As String = DIRTokens.Last  ' 请注意,由于path参数可能是相对路径,所以在这里DIRname和name要分开讨论
371         Dim name As String = path.Replace("\""/").Split("/"c).Last  ' 因为相对路径最终会出现文件夹名称,但在path里面可能是使用小数点来表示的
372
373         If parent.Length + name.Length >= 259 Then
374             DIRname = Mid(DIRname, 1, 20) & "~"
375             Dim ext As String = name.Split("."c).Last
376             name = Mid(name, 1, 20) & "~." & ext
377             parent = String.Join("/", DIRTokens.Take(DIRTokens.Length - 1).ToArray)
378             parent &= "/" & DIRname
379             parent &= "/" & name
380
381             Dim ex As Exception = New PathTooLongException(PathTooLongException)
382             ex = New Exception(path, ex)
383             ex = New Exception("But the path was corrected as:   " & parent & "  to avoid the crashed problem.", ex)
384             Call ex.PrintException
385             Call App.LogException(ex, caller & " -> " & MethodBase.GetCurrentMethod.GetFullName)
386
387             Return parent.Replace("\""/")
388         Else
389             Return FileIO.FileSystem.GetFileInfo(path).FullName
390         End If
391     End Function
392
393     ''' <summary>
394     ''' + C:\
395     ''' + AB:\
396     ''' + AB2:\
397     ''' + etc...
398     ''' </summary>
399     Const DriveLabel$ = "[a-zA-Z]([a-zA-Z0-9])*"
400
401     ''' <summary>
402     ''' File path illegal?
403     ''' </summary>
404     ''' <param name="path"></param>
405     ''' <returns></returns>
406     <ExportAPI("Path.Illegal?")>
407     <Extension> Public Function PathIllegal(path As StringAs Boolean
408         Dim tokens$() = Strings.Split(path.Replace("\""/"), ":/")
409
410         If tokens.Length > 2 Then  ' 有多余一个的驱动器符,则肯定是非法的路径格式
411             Return False
412         ElseIf tokens.Length = 2 Then
413             ' 完整路径
414             ' 当有很多个驱动器的时候,这里会不止一个字母
415             If Not tokens(0).IsPattern(DriveLabel, RegexICSng) Then
416                 ' 开头的驱动器的符号不正确
417                 Return False
418             Else
419                 ' 驱动器的符号也正确
420             End If
421         Else
422             ' 只有一个,则是相对路径
423         End If
424
425         Dim fileName As String = tokens.Last
426
427         ' 由于这里是判断文件是否合法,所以之判断文件名就行了,即token列表的最后一个元素
428         For Each ch As Char In ILLEGAL_PATH_CHARACTERS_ENUMERATION
429             If fileName.IndexOf(ch) > -1 Then
430                 Return True
431             End If
432         Next
433
434         Return False
435     End Function
436
437     ''' <summary>
438     ''' Gets the file length, if the path is not exists, then returns -1.
439     ''' (安全的获取文件的大小,如果目标文件不存在的话会返回-1)
440     ''' </summary>
441     ''' <param name="path"></param>
442     ''' <returns></returns>
443     <Extension>
444     Public Function FileLength(path As StringAs Long
445         If Not path.FileExists Then
446             Return -1&
447         Else
448             Return FileIO.FileSystem.GetFileInfo(path).Length
449         End If
450     End Function
451
452     ''' <summary>
453     ''' Safe file copy operation.
454     ''' (请注意,<paramref name="copyTo"/>参数的字符串最末尾必须是``/``或者``\``才会被认作为目录路径)
455     ''' </summary>
456     ''' <param name="source$"></param>
457     ''' <param name="copyTo$">
458     ''' Can be file name or directory name.
459     ''' 
460     ''' + If this paramter is a file path, then you can copy the 
461     '''   source file to another location with renamed.
462     ''' + If this parameter is a directory location, then you can 
463     '''   copy the source file to another location with the 
464     '''   identical file name.
465     ''' 
466     ''' Please notice that, the directory path should end with 
467     ''' path seperator symbol: ``\`` or ``/``.
468     ''' </param>
469     ''' <returns></returns>
470     <Extension> Public Function FileCopy(source$, copyTo$) As Boolean
471         Try
472             If copyTo.Last = "/"OrElse copyTo.Last = "\"Then
473                 copyTo = copyTo & source.FileName
474             End If
475
476             If copyTo.FileExists Then
477                 Call FileIO.FileSystem.DeleteFile(copyTo)
478             Else
479                 Call copyTo.ParentPath.MkDIR
480             End If
481
482             Call FileIO.FileSystem.CopyFile(source, copyTo)
483         Catch ex As Exception
484             ex = New Exception({source, copyTo}.GetJson, ex)
485             App.LogException(ex)
486
487             Return False
488         End Try
489
490         Return True
491     End Function
492
493     <Extension>
494     Public Function FileMove(source$, target$) As Boolean
495         Try
496             Call My.Computer.FileSystem.MoveFile(source, target)
497             Return True
498         Catch ex As Exception
499             ex = New Exception("source: " & source, ex)
500             ex = New Exception("target: " & target, ex)
501
502             Call App.LogException(ex)
503
504             Return False
505         End Try
506     End Function
507
508     ''' <summary>
509     ''' Check if the target file object is exists on your file system or not.
510     ''' (这个函数也会自动检查目标<paramref name="path"/>参数是否为空)
511     ''' </summary>
512     ''' <param name="path"></param>
513     ''' <param name="ZERO_Nonexists">将0长度的文件也作为不存在</param>
514     ''' <returns></returns>
515     ''' <remarks></remarks>
516 #If FRAMEWORD_CORE Then
517     <ExportAPI("File.Exists"Info:="Check if the target file object is exists on your file system or not.")>
518     <Extension> Public Function FileExists(path$, Optional ZERO_Nonexists As Boolean = FalseAs Boolean
519 #Else
520     <Extension> Public Function FileExists(path As StringAs Boolean
521 #End If
522
523         If path.StringEmpty Then
524             Return False
525         End If
526         If path.IndexOf(ASCII.CR) > -1 OrElse path.IndexOf(ASCII.LF) > -1 Then
527             Return False ' 包含有回车符或者换行符,则肯定不是文件路径了
528         End If
529
530         If Not String.IsNullOrEmpty(path) AndAlso
531             FileIO.FileSystem.FileExists(path) Then  ' 文件存在
532
533             If ZERO_Nonexists Then
534                 Return FileSystem.FileLen(path) > 0
535             Else
536                 Return True
537             End If
538         Else
539             Return False
540         End If
541     End Function
542
543     ''' <summary>
544     ''' Determine that the target directory is exists on the file system or not?(判断文件夹是否存在)
545     ''' </summary>
546     ''' <param name="DIR"></param>
547     ''' <returns></returns>
548     <ExportAPI("DIR.Exists"Info:="Determine that the target directory is exists on the file system or not?")>
549     <MethodImpl(MethodImplOptions.AggressiveInlining)>
550     <Extension>
551     Public Function DirectoryExists(DIR As StringAs Boolean
552         Return Not String.IsNullOrEmpty(DIR) AndAlso
553             FileIO.FileSystem.DirectoryExists(DIR)
554     End Function
555
556     ''' <summary>
557     ''' Get the directory its name of the target <paramref name="dir"/> directory
558     ''' </summary>
559     ''' <param name="dir$"></param>
560     ''' <returns></returns>
561     ''' 
562     <MethodImpl(MethodImplOptions.AggressiveInlining)>
563     <Extension> Public Function DirectoryName(dir$) As String
564         Return dir.TrimDIR _
565             .Split("\"c).Last _
566             .Split("/"c).Last
567     End Function
568
569     ''' <summary>
570     ''' Check if the file is opened by other code?(检测文件是否已经被其他程序打开使用之中)
571     ''' </summary>
572     ''' <param name="FileName">目标文件</param>
573     ''' <returns></returns>
574     ''' 
575     <MethodImpl(MethodImplOptions.AggressiveInlining)>
576     <ExportAPI("File.IsOpened"Info:="Detect while the target file is opened by someone process.")>
577     <Extension> Public Function FileOpened(FileName As StringAs Boolean
578         Try
579             Using FileOpenDetect As New FileStream(
580                 path:=FileName,
581                 mode:=FileMode.Open,
582                 access:=FileAccess.Read,
583                 share:=FileShare.None
584             )
585                 ' Just detects this file is occupied, no more things needs to do....
586             End Using
587             Return True
588         Catch ex As Exception
589             Return False
590         End Try
591     End Function
592
593     ''' <summary>
594     ''' Gets the name of the target file or directory, if the target is a file, then the name without the extension name.
595     ''' (获取目标文件夹的名称或者文件的不包含拓展名的名称)
596     ''' </summary>
597     ''' <returns></returns>
598     ''' <remarks>
599     ''' ###### 2017-2-14 
600     ''' 原先的函数是依靠系统的API来工作的,故而只能够工作于已经存在的文件之上,
601     ''' 所以在这里为了更加方便的兼容文件夹或者文件路径,在这使用字符串的方法来
602     ''' 进行截取
603     ''' </remarks>
604     <ExportAPI(NameOf(BaseName), Info:="Gets the name of the target directory/file object.")>
605     <Extension> Public Function BaseName(fsObj As StringOptional allowEmpty As Boolean = FalseAs String
606         If fsObj.StringEmpty Then
607             If allowEmpty Then
608                 Return ""
609             Else
610                 Throw New NullReferenceException(NameOf(fsObj) & " file system object handle is null!")
611             End If
612         End If
613
614         ' 前面的代码已经处理好了空字符串的情况了,在这里不会出现空字符串的错误
615         Dim t$() = fsObj.Trim("\"c, "/"c).Replace("\""/").Split("/"c)
616         t = t.Last.Split("."c)
617         If t.Length > 1 Then
618             ' 文件名之中并没有包含有拓展名后缀,则数组长度为1,则不跳过了
619             ' 有后缀拓展名,则split之后肯定会长度大于1的
620             t = t.Take(t.Length - 1).ToArray
621         End If
622
623         Dim name = String.Join(".", t)
624         Return name
625     End Function
626
627     ''' <summary>
628     ''' <see cref="basename"/> shortcuts extension.
629     ''' </summary>
630     ''' <param name="path"></param>
631     ''' <returns></returns>
632     ''' 
633     <MethodImpl(MethodImplOptions.AggressiveInlining)>
634     <Extension> Public Function GetBaseName(path As StringAs String
635         Return BaseName(path)
636     End Function
637
638     ''' <summary>
639     ''' Gets the name of the file's parent directory, returns value is a name, not path.
640     ''' (获取目标文件的父文件夹的文件夹名称,是名称而非路径)
641     ''' </summary>
642     ''' <param name="file"></param>
643     ''' <returns></returns>
644     <Extension> Public Function ParentDirName(file As StringAs String
645         Dim parentDir As String = FileIO.FileSystem.GetParentPath(file)
646         Dim parDirInfo = FileIO.FileSystem.GetDirectoryInfo(parentDir)
647         Return parDirInfo.Name
648     End Function
649
650     ''' <summary>
651     ''' Returns the directory path value of the parent directory.
652     ''' (这个函数是返回文件夹的路径而非名称,这个函数不依赖于系统的底层API,
653     ''' 因为系统的底层API对于过长的文件名会出错)
654     ''' </summary>
655     ''' <param name="file"></param>
656     ''' <returns></returns>
657     ''' <remarks>这个函数不依赖于系统的底层API,因为系统的底层API对于过长的文件名会出错</remarks>
658     <ExportAPI(NameOf(ParentPath))>
659     <Extension> Public Function ParentPath(file$, Optional full As Boolean = TrueAs String
660         file = file.Replace("\""/")
661
662         Dim parent As String = ""
663         Dim t As String() = file.Split("/"c)
664
665         If full Then
666             If InStr(file, "../") = 1 Then
667                 parent = FileIO.FileSystem.GetParentPath(App.CurrentDirectory)
668                 t = t.Skip(1).ToArray
669                 parent &= "/"
670             ElseIf InStr(file, "./") = 1 Then
671                 parent = App.CurrentDirectory
672                 t = t.Skip(1).ToArray
673                 parent &= "/"
674             Else
675
676             End If
677
678             If file.Last = "/"Then ' 是一个文件夹
679                 parent &= String.Join("/", t.Take(t.Length - 2).ToArray)
680             Else
681                 parent &= String.Join("/", t.Take(t.Length - 1).ToArray)
682             End If
683
684             If parent.StringEmpty Then
685                 ' 用户直接输入了一个文件名,没有包含文件夹部分,则默认是当前的文件夹
686                 parent = App.CurrentDirectory
687             End If
688         Else
689             parent = String.Join("/", t.Take(t.Length - 1).ToArray)
690         End If
691
692         Return parent
693     End Function
694
695     ''' <summary>
696     '''
697     ''' </summary>
698     ''' <param name="DIR"></param>
699     ''' <param name="keyword"></param>
700     ''' <param name="ext">元素的排布是有顺序的</param>
701     ''' <returns></returns>
702     ''' <remarks></remarks>
703     '''
704     <ExportAPI("Get.File.Path")>
705     <Extension> Public Function GetFile(DIR As String,
706                                        <Parameter("Using.Keyword")> keyword As String,
707                                        <Parameter("List.Ext")> ParamArray ext As String()) _
708                                     As <FunctionReturns("A list of file path which match with the keyword and the file extension name.")> String()
709
710         Dim Files As IEnumerable(Of String) = ls - l - wildcards(ext) <= DIR
711         Dim matches = (From Path As String
712                        In Files.AsParallel
713                        Let NameID = BaseName(Path)
714                        Where InStr(NameID, keyword, CompareMethod.Text) > 0
715                        Let ExtValue = Path.Split("."c).Last
716                        Select Path,
717                            ExtValue)
718         Dim LQuery =
719             From extType As String
720             In ext
721             Select From path
722                    In matches
723                    Where InStr(extType, path.ExtValue, CompareMethod.Text) > 0
724                    Select path.Path
725         Return LQuery.IteratesALL.Distinct.ToArray
726     End Function
727
728     ''' <summary>
729     ''' 这个方法没得卵用
730     ''' </summary>
731     ''' <param name="DIR"></param>
732     ''' <returns></returns>
733     <ExportAPI("Md5.Renamed")>
734     Public Function BatchMd5Renamed(DIR As StringAs Boolean
735         DIR = FileIO.FileSystem.GetDirectoryInfo(DIR).FullName
736
737         For Each path As String In FileIO.FileSystem.GetFiles(DIR)
738             On Error Resume Next
739
740             Dim Md5 As String = SecurityString.GetMd5Hash(path)
741             Dim ext As String = IO.Path.GetExtension(path)
742             Dim FileName As String = DIR & "/" & Md5 & ext
743
744             Call IO.File.Move(path, FileName)
745         Next
746
747         Return True
748     End Function
749
750     ''' <summary>
751     ''' [<see cref="FileIO.SearchOption.SearchAllSubDirectories"/>,这个函数会扫描目标文件夹下面的所有文件。]
752     ''' 请注意,本方法是不能够产生具有相同的主文件名的数据的。假若目标GBK是使用本模块之中的方法保存或者导出来的,
753     ''' 则可以使用本方法生成Entry列表;(在返回的结果之中,KEY为文件名,没有拓展名,VALUE为文件的路径)
754     ''' </summary>
755     ''' <param name="source"></param>
756     ''' <returns></returns>
757     ''' <remarks></remarks>
758     '''
759     <ExportAPI("Load.ResourceEntry",
760                Info:="Load the file from a specific directory from the source parameter as the resource entry list.")>
761     <Extension>
762     Public Function LoadSourceEntryList(<Parameter("Dir.Source""The source directory which will be searchs for file.")> source As String,
763                                         <Parameter("List.Ext""The list of the file extension.")> ext As String(),
764                                         Optional topLevel As Boolean = TrueAs Dictionary(Of StringString)
765
766         If ext.IsNullOrEmpty Then
767             ext = {"*.*"}
768         End If
769
770         Dim LQuery = (From path As String
771                       In If(topLevel, ls - l, ls - l - r) - wildcards(ext) <= source
772                       Select ID = BaseName(path),
773                           path
774                       Group By ID Into Group).ToArray
775
776         ext = LinqAPI.Exec(Of String) <= From value As String
777                                          In ext
778                                          Select value.Split(CChar(".")).Last.ToLower
779
780         Dim res As Dictionary(Of StringString) = LQuery _
781             .ToDictionary(Function(x) x.ID,
782                           Function(x)
783
784                               Return LinqAPI.DefaultFirst(Of String) _
785  _
786                                 () <= From path
787                                       In x.Group
788                                       Let pathValue = path.path
789                                       Let extValue As String = pathValue.Split("."c).Last.ToLower
790                                       Where Array.IndexOf(ext, extValue) > -1
791                                       Select pathValue
792
793                           End Function)
794
795         With From entry
796              In res
797              Where Not String.IsNullOrEmpty(entry.Value)
798              Select entry
799
800             res = .ToDictionary(Function(x) x.Key,
801                                 Function(x) x.Value)
802         End With
803
804         Call $"{NameOf(ProgramPathSearchTool)} load {res.Count} source entry...".__DEBUG_ECHO
805
806         Return res
807     End Function
808
809     ''' <summary>
810     ''' 可以使用本方法生成Entry列表;(在返回的结果之中,KEY为文件名,没有拓展名,VALUE为文件的路径)
811     ''' 请注意,这个函数会搜索目标文件夹下面的所有的文件夹的
812     ''' </summary>
813     ''' <param name="source"></param>
814     ''' <param name="ext">文件类型的拓展名称</param>
815     ''' <returns></returns>
816     ''' <remarks></remarks>
817     <Extension> Public Function LoadSourceEntryList(source$, ParamArray ext$()) As Dictionary(Of StringString)
818         If Not FileIO.FileSystem.DirectoryExists(source) Then
819             Return New Dictionary(Of StringString)
820         End If
821
822         Dim LQuery = From path As String
823                      In FileIO.FileSystem.GetFiles(source, FileIO.SearchOption.SearchAllSubDirectories, ext)
824                      Select ID = BaseName(path),
825                           path
826                      Group By ID Into Group
827         Dim dict As Dictionary(Of StringString) =
828             LQuery.ToDictionary(Function(x) x.ID,
829                                 Function(x) x.Group.First.path)
830         Return dict
831     End Function
832
833     ''' <summary>
834     ''' 允许有重复的数据
835     ''' </summary>
836     ''' <param name="DIR"></param>
837     ''' <param name="exts"></param>
838     ''' <returns></returns>
839     ''' <remarks></remarks>
840     <MethodImpl(MethodImplOptions.AggressiveInlining)>
841     <ExportAPI("Load.ResourceEntry")>
842     <Extension> Public Function LoadEntryList(<Parameter("Dir.Source")> DIR$, ParamArray exts$()) As NamedValue(Of String)()
843         Return LinqAPI.Exec(Of NamedValue(Of String)) _
844  _
845             () <= From path As String
846                   In ls - l - ShellSyntax.r - wildcards(exts) <= DIR
847                   Select New NamedValue(Of StringWith {
848                       .Name = path.BaseName,
849                       .Value = path
850                   }
851
852     End Function
853
854     <ExportAPI("Load.ResourceEntry"Info:="Load the file from a specific directory from the source parameter as the resource entry list.")>
855     <Extension>
856     Public Function LoadSourceEntryList(source As IEnumerable(Of String)) As Dictionary(Of StringString)
857         Dim LQuery = From path As String
858                      In source
859                      Select ID = BaseName(path),
860                          path
861                      Group By ID Into Group
862         Dim res As Dictionary(Of StringString) =
863             LQuery.ToDictionary(Function(x) x.ID,
864                                 Function(x) x.Group.First.path)
865         Return res
866     End Function
867
868     ''' <summary>
869     ''' 将不同来源<paramref name="source"></paramref>的文件复制到目标文件夹<paramref name="copyto"></paramref>之中
870     ''' </summary>
871     ''' <param name="source"></param>
872     ''' <param name="copyto"></param>
873     ''' <returns>返回失败的文件列表</returns>
874     ''' <remarks></remarks>
875     <ExportAPI("Source.Copy",
876                Info:="Copy the file in the source list into the copyto directory, function returns the failed operation list.")>
877     Public Function SourceCopy(source As IEnumerable(Of String), CopyTo As StringOptional [Overrides] As Boolean = FalseAs String()
878         Dim failedList As New List(Of String)
879
880         For Each file As String In source
881             Try
882                 Call FileIO.FileSystem.CopyFile(file, CopyTo & "/" & FileIO.FileSystem.GetFileInfo(file).Name, [Overrides])
883             Catch ex As Exception
884                 Call failedList.Add(file)
885                 Call App.LogException(New Exception(file, ex))
886             End Try
887         Next
888
889         Return failedList.ToArray
890     End Function
891
892     <ExportAPI("Get.FrequentPath",
893                Info:="Gets a directory path which is most frequent appeared in the file list.")>
894     Public Function GetMostAppreancePath(files As IEnumerable(Of String)) As String
895         If files Is Nothing Then
896             Return ""
897         End If
898
899         Dim LQuery = From strPath As String
900                      In files
901                      Select FileIO.FileSystem.GetParentPath(strPath)
902         Return LQuery _
903             .TokenCount(ignoreCase:=True) _
904             .OrderByDescending(Function(x) x.Value) _
905             .FirstOrDefault _
906             .Key
907     End Function
908
909     ''' <summary>
910     ''' Invoke the search session for the program file using a specific keyword string value.(使用某个关键词来搜索目标应用程序)
911     ''' </summary>
912     ''' <param name="DIR"></param>
913     ''' <param name="Keyword"></param>
914     ''' <returns></returns>
915     ''' <remarks></remarks>
916     <ExportAPI("File.Search.Program",
917                Info:="Invoke the search session for the program file using a specific keyword string value.")>
918     Public Function SearchProgram(DIR As String, Keyword As StringAs String()
919         Dim ExeNameRule As String = String.Format("*{0}*.exe", Keyword)
920         Dim DllNameRule As String = String.Format("*{0}*.dll", Keyword)
921
922         Dim Files = FileIO.FileSystem.GetFiles(DIR, FileIO.SearchOption.SearchTopLevelOnly, ExeNameRule, DllNameRule)
923         Dim binDIR As String = String.Format("{0}/bin/", DIR)
924         Dim ProgramDIR As String = String.Format("{0}/Program", DIR)
925         Dim buffer As New List(Of String)
926
927         If FileIO.FileSystem.DirectoryExists(binDIR) Then
928             buffer += FileIO.FileSystem.GetFiles(
929                 binDIR,
930                 FileIO.SearchOption.SearchTopLevelOnly,
931                 ExeNameRule, DllNameRule)
932         End If
933         If FileIO.FileSystem.DirectoryExists(ProgramDIR) Then
934             buffer += FileIO.FileSystem.GetFiles(
935                 ProgramDIR,
936                 FileIO.SearchOption.SearchTopLevelOnly,
937                 ExeNameRule, DllNameRule)
938         End If
939
940         buffer += Files
941
942         Return buffer.ToArray
943     End Function
944
945     ''' <summary>
946     '''
947     ''' </summary>
948     ''' <param name="DIR"></param>
949     ''' <param name="Keyword"></param>
950     ''' <param name="withExtension">脚本文件的文件拓展名</param>
951     ''' <returns></returns>
952     ''' <remarks></remarks>
953     '''
954     <ExportAPI("Search.Scripts"Info:="Search for the path of a script file with a specific extension name.")>
955     Public Function SearchScriptFile(DIR As String, Keyword As StringOptional withExtension As String = ""As String()
956         Dim ScriptFileNameRule As String = String.Format("*{0}*{1}", Keyword, withExtension)
957         Dim Files = FileIO.FileSystem.GetFiles(DIR, FileIO.SearchOption.SearchTopLevelOnly, ScriptFileNameRule)
958         Dim binDIR As String = String.Format("{0}/bin/", DIR)
959         Dim ProgramDIR As String = String.Format("{0}/Program", DIR)
960         Dim ScriptsDIR As String = String.Format("{0}/scripts", DIR)
961         Dim fileList As New List(Of String)
962
963         If FileIO.FileSystem.DirectoryExists(binDIR) Then fileList += (ls - l - wildcards(ScriptFileNameRule) <= binDIR)
964         If FileIO.FileSystem.DirectoryExists(ProgramDIR) Then fileList += (ls - l - wildcards(ScriptFileNameRule) <= ProgramDIR)
965         If FileIO.FileSystem.DirectoryExists(ScriptsDIR) Then fileList += (ls - l - wildcards(ScriptFileNameRule) <= ScriptsDIR)
966
967         Call fileList.AddRange(Files)
968
969         If String.IsNullOrEmpty(withExtension) Then
970             Return LinqAPI.Exec(Of String) _
971  _
972                 () <= From strPath As String
973                       In fileList
974                       Let ext As String = FileIO.FileSystem.GetFileInfo(strPath).Extension
975                       Where String.IsNullOrEmpty(ext)
976                       Select strPath
977         Else
978             Return fileList.ToArray
979         End If
980     End Function
981
982     ''' <summary>
983     '''
984     ''' </summary>
985     ''' <param name="SpecificDrive">所制定进行搜索的驱动器,假若希望搜索整个硬盘,请留空字符串</param>
986     ''' <returns></returns>
987     ''' <remarks></remarks>
988     '''
989     <ExportAPI("DIR.Search.Program_Directory",
990                Info:="Search for the directories which its name was matched the keyword pattern.")>
991     Public Function SearchDirectory(Keyword As String, SpecificDrive As StringAs String()
992         Dim Drives As ReadOnlyCollection(Of DriveInfo) =
993             If(String.IsNullOrEmpty(SpecificDrive),
994                FileIO.FileSystem.Drives,
995                New ReadOnlyCollection(Of IO.DriveInfo)(
996                    {FileIO.FileSystem.GetDriveInfo(SpecificDrive)}))
997         Dim DIRs As New List(Of String)
998
999         For Each Drive As DriveInfo In Drives
1000             DIRs += SearchDrive(Drive, Keyword)
1001         Next
1002
1003         Return DIRs.ToArray
1004     End Function
1005
1006     <Extension>
1007     Private Function SearchDrive(Drive As DriveInfo, keyword As StringAs String()
1008         If Not Drive.IsReady Then
1009             Return New String() {}
1010         End If
1011
1012         Dim DriveRoot = FileIO.FileSystem.GetDirectories(Drive.RootDirectory.FullName, FileIO.SearchOption.SearchTopLevelOnly, keyword)
1013         Dim files As New List(Of String)
1014
1015         Dim ProgramFiles As String = String.Format("{0}/Program Files", Drive.RootDirectory.FullName)
1016         If FileIO.FileSystem.DirectoryExists(ProgramFiles) Then
1017             Call files.AddRange(BranchRule(ProgramFiles, keyword))
1018         End If
1019
1020         Dim ProgramFilesX86 = String.Format("{0}/Program Files(x86)", Drive.RootDirectory.FullName)
1021         If FileIO.FileSystem.DirectoryExists(ProgramFilesX86) Then
1022             Call files.AddRange(BranchRule(ProgramFilesX86, keyword))
1023         End If
1024         Call files.AddRange(DriveRoot)
1025         Call files.AddRange(DriveRoot.Select(Function(rootDir) BranchRule(rootDir, keyword)).Unlist)
1026
1027         Return files.ToArray
1028     End Function
1029
1030     ''' <summary>
1031     ''' 商标搜索规则
1032     ''' </summary>
1033     ''' <param name="ProgramFiles"></param>
1034     ''' <param name="Keyword"></param>
1035     ''' <returns></returns>
1036     ''' <remarks></remarks>
1037     Private Function BranchRule(ProgramFiles As String, Keyword As StringAs String()
1038         Dim ProgramFiles_Directories = FileIO.FileSystem.GetDirectories(
1039             ProgramFiles,
1040             FileIO.SearchOption.SearchTopLevelOnly,
1041             Keyword)
1042         Dim fsObjs As New List(Of String)
1043
1044         For Each Dir As String In ProgramFiles_Directories
1045             fsObjs += FileIO.FileSystem.GetDirectories(
1046                 Dir, FileIO.SearchOption.SearchTopLevelOnly)
1047         Next
1048         Call fsObjs.Add(ProgramFiles_Directories.ToArray)
1049
1050         If fsObjs.Count = 0 Then
1051             ' 这个应用程序的安装文件夹可能是带有版本号标记的
1052             Dim Dirs = FileIO.FileSystem.GetDirectories(ProgramFiles, FileIO.SearchOption.SearchTopLevelOnly)
1053             Dim version As String = Keyword & ProgramPathSearchTool.VERSION
1054             Dim Patterns As String() =
1055                 LinqAPI.Exec(Of String) <= From DIR As String
1056                                            In Dirs
1057                                            Let name As String = FileIO.FileSystem.GetDirectoryInfo(DIR).Name
1058                                            Where Regex.Match(name, version, RegexOptions.IgnoreCase).Success
1059                                            Select DIR
1060             Call fsObjs.Add(Patterns)
1061         End If
1062
1063         Return fsObjs.ToArray
1064     End Function
1065
1066     Const VERSION As String = "[-_`~.]\d+(\.\d+)*"
1067
1068     ''' <summary>
1069     ''' 获取相对于本应用程序的目标文件的相对路径(请注意,所生成的相对路径之中的字符串最后是没有文件夹的分隔符\或者/的)
1070     ''' </summary>
1071     ''' <param name="path"></param>
1072     ''' <returns></returns>
1073     <MethodImpl(MethodImplOptions.AggressiveInlining)>
1074     <ExportAPI(NameOf(RelativePath),
1075                Info:="Get the specific file system object its relative path to the application base directory.")>
1076     Public Function RelativePath(path As StringAs String
1077         Return RelativePath(App.HOME, path)
1078     End Function
1079
1080     ''' <summary>
1081     ''' Gets the relative path of file system object <paramref name="pcTo"/> reference to the directory path <paramref name="pcFrom"/>.
1082     ''' (请注意,所生成的相对路径之中的字符串最后是没有文件夹的分隔符\或者/的)
1083     ''' </summary>
1084     ''' <param name="pcFrom">生成相对路径的参考文件夹</param>
1085     ''' <param name="pcTo">所需要生成相对路径的目标文件系统对象的绝对路径或者相对路径</param>
1086     ''' <param name="appendParent">是否将父目录的路径也添加进入相对路径之中?默认是</param>
1087     ''' <returns></returns>
1088     <ExportAPI(NameOf(RelativePath),
1089                Info:="Gets the relative path value of pcTo file system object relative to a reference directory pcFrom")>
1090     Public Function RelativePath(pcFrom$, pcTo$, Optional appendParent As Boolean = TrueAs <FunctionReturns("The relative path string of pcTo file object reference to directory pcFrom")> String
1091         Dim lcRelativePath As String = Nothing
1092         Dim lcFrom As String = (If(pcFrom Is Nothing"", pcFrom.Trim()))
1093         Dim lcTo As String = (If(pcTo Is Nothing"", pcTo.Trim()))
1094
1095         If lcFrom.Length = 0 OrElse lcTo.Length = 0 Then
1096             Throw New InvalidDataException("One of the path string value is null!")
1097         End If
1098         If Not IO.Path.GetPathRoot(lcFrom.ToUpper()) _
1099             .Equals(IO.Path.GetPathRoot(lcTo.ToUpper())) Then
1100             Return pcTo
1101         End If
1102
1103         ' 两个路径都有值并且都在相同的驱动器下才会进行计算
1104
1105         Dim laDirSep As Char() = {"\"c}
1106         Dim lcPathFrom As String = (If(IO.Path.GetDirectoryName(lcFrom) Is Nothing, IO.Path.GetPathRoot(lcFrom.ToUpper()), IO.Path.GetDirectoryName(lcFrom)))
1107         Dim lcPathTo As String = (If(IO.Path.GetDirectoryName(lcTo) Is Nothing, IO.Path.GetPathRoot(lcTo.ToUpper()), IO.Path.GetDirectoryName(lcTo)))
1108         Dim lcFileTo As String = (If(IO.Path.GetFileName(lcTo) Is Nothing"", IO.Path.GetFileName(lcTo)))
1109         Dim laFrom As String() = lcPathFrom.Split(laDirSep)
1110         Dim laTo As String() = lcPathTo.Split(laDirSep)
1111         Dim lnFromCnt As Integer = laFrom.Length
1112         Dim lnToCnt As Integer = laTo.Length
1113         Dim lnSame As Integer = 0
1114         Dim lnCount As Integer = 0
1115
1116         While lnToCnt > 0 AndAlso lnSame < lnToCnt
1117             If lnCount < lnFromCnt Then
1118                 If laFrom(lnCount).ToUpper().Equals(laTo(lnCount).ToUpper()) Then
1119                     lnSame += 1
1120                 Else
1121                     Exit While
1122                 End If
1123             Else
1124                 Exit While
1125             End If
1126             lnCount += 1
1127         End While
1128
1129         Dim lcEndPart As String = ""
1130         For lnEnd As Integer = lnSame To lnToCnt - 1
1131             If laTo(lnEnd).Length > 0 Then
1132                 lcEndPart += laTo(lnEnd) & "\"
1133             Else
1134                 Exit For
1135             End If
1136         Next
1137
1138         Dim lnDiff As Integer = Abs(lnFromCnt - lnSame)
1139         If lnDiff > 0 AndAlso laFrom(lnFromCnt - 1).Length > 0 Then
1140             While lnDiff > 0
1141                 lnDiff -= 1
1142                 lcEndPart = "..\" & lcEndPart
1143             End While
1144         End If
1145
1146         lcRelativePath = lcEndPart & lcFileTo
1147
1148         If appendParent Then
1149             Return "..\" & lcRelativePath
1150         Else
1151             ' 2017-8-26
1152             ' 为Xlsx打包模块进行的修复
1153             Return lcRelativePath.Split("\"c).Skip(1).JoinBy("\")
1154         End If
1155     End Function
1156
1157     ''' <summary>
1158     ''' Gets the full path of the specific file.(为了兼容Linux,这个函数会自动替换路径之中的\为/符号)
1159     ''' </summary>
1160     ''' <param name="file"></param>
1161     ''' <returns></returns>
1162     <MethodImpl(MethodImplOptions.AggressiveInlining)>
1163     <ExportAPI("File.FullPath"Info:="Gets the full path of the file.")>
1164     <Extension> Public Function GetFullPath(file As StringAs String
1165         Return FileIO.FileSystem.GetFileInfo(file).FullName.Replace("\""/")
1166     End Function
1167
1168     ''' <summary>
1169     ''' Gets the full path of the specific directory. 
1170     ''' (这个函数为了兼容linux的文件系统,也会自动的将所有的``\``替换为``/``)
1171     ''' </summary>
1172     ''' <param name="dir"></param>
1173     ''' <param name="stack">当程序出错误的时候记录进入日志的一个追踪目标参数,调试用</param>
1174     ''' <returns></returns>
1175     <ExportAPI("Dir.FullPath"Info:="Gets the full path of the directory.")>
1176     <Extension> Public Function GetDirectoryFullPath(dir$, <CallerMemberName> Optional stack$ = NothingAs String
1177         Try
1178             Return FileIO.FileSystem _
1179                 .GetDirectoryInfo(dir) _
1180                 .FullName _
1181                 .Replace("\""/")
1182         Catch ex As Exception
1183             stack = stack & " --> " & NameOf(GetDirectoryFullPath)
1184
1185             If dir = "/" AndAlso Not App.IsMicrosoftPlatform Then
1186                 Return "/"  ' Linux上面已经是全路径了,root
1187             Else
1188                 ex = New Exception(stack & ": " & dir, ex)
1189                 Call App.LogException(ex)
1190                 Call ex.PrintException
1191                 Return dir
1192             End If
1193         End Try
1194     End Function
1195
1196     ''' <summary>
1197     ''' Removes the file extension name from the file path.(去除掉文件的拓展名)
1198     ''' </summary>
1199     ''' <param name="file"></param>
1200     ''' <returns></returns>
1201     <ExportAPI("File.Ext.Trim")>
1202     <Extension> Public Function TrimSuffix(file As StringAs String
1203         Try
1204             Dim path$ = file.FixPath.TrimEnd("/"c, "\"c)
1205             Dim fileInfo = FileIO.FileSystem.GetFileInfo(path$)
1206             Dim Name As String = BaseName(fileInfo.FullName)
1207             Return $"{fileInfo.Directory.FullName}/{Name}"
1208         Catch ex As Exception
1209             ex = New Exception($"{NameOf(file)} --> {file}", ex)
1210             Throw ex
1211         End Try
1212     End Function
1213
1214     ''' <summary>
1215     ''' Removes the last \ and / character in a directory path string.
1216     ''' (使用这个函数修剪文件夹路径之中的最后一个分隔符,以方便生成文件名)
1217     ''' </summary>
1218     ''' <param name="DIR"></param>
1219     ''' <returns></returns>
1220     <MethodImpl(MethodImplOptions.AggressiveInlining)>
1221     <Extension>
1222     Public Function TrimDIR(DIR As StringAs String
1223         Return DIR.TrimEnd("/"c, "\"c)
1224     End Function
1225
1226     ''' <summary>
1227     ''' 返回``文件名称.拓展名``
1228     ''' </summary>
1229     ''' <param name="path"></param>
1230     ''' <returns></returns>
1231     ''' 
1232     <MethodImpl(MethodImplOptions.AggressiveInlining)>
1233     <ExportAPI("File.Name")>
1234     <Extension>
1235     Public Function FileName(path As StringAs String
1236         Return FileIO.FileSystem.GetFileInfo(path).Name
1237     End Function
1238
1239     ''' <summary>
1240     ''' 进行安全的复制,出现错误不会导致应用程序崩溃,大文件不推荐使用这个函数进行复制
1241     ''' </summary>
1242     ''' <param name="source"></param>
1243     ''' <param name="copyTo"></param>
1244     ''' <returns></returns>
1245     <ExportAPI("SafeCopyTo")>
1246     Public Function SafeCopyTo(source As String, copyTo As StringAs Boolean
1247         Try
1248             Dim buf As Byte() = IO.File.ReadAllBytes(source)
1249             Call buf.FlushStream(copyTo)
1250         Catch ex As Exception
1251             Dim pt As String = $"{source.ToFileURL} ===> {copyTo.ToFileURL}"
1252             Call App.LogException(New Exception(pt, ex))
1253             Return False
1254         End Try
1255
1256         Return True
1257     End Function
1258 End Module