1 #Region "Microsoft.VisualBasic::ff10e3ac74447c0278984c8ba77dbbe0, Microsoft.VisualBasic.Core\Extensions\Doc\Text.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 TextDoc
35     
36     '     FunctionForEachChar, IsTextFile, IterateAllLines, LineIterators, LoadTextDoc
37     '               OpenWriter, ReadAllLines, ReadAllText, ReadFirstLine, (+4 Overloads) SaveTo
38     '               SaveTSV, SaveWithHTMLEncoding, SolveStream, TsvHeaders
39     
40     ' /********************************************************************************/
41
42 #End Region
43
44 Imports System.IO
45 Imports System.Runtime.CompilerServices
46 Imports System.Text
47 Imports Microsoft.VisualBasic.CommandLine.Reflection
48 Imports Microsoft.VisualBasic.ComponentModel
49 Imports Microsoft.VisualBasic.ComponentModel.Collection
50 Imports Microsoft.VisualBasic.Linq
51 Imports Microsoft.VisualBasic.Scripting.MetaData
52 Imports Microsoft.VisualBasic.Text
53 Imports fs = Microsoft.VisualBasic.FileIO.FileSystem
54
55 <Package("Doc.TextFile", Category:=APICategories.UtilityTools, Publisher:="xie.guigang@gmail.com")>
56 Public Module TextDoc
57
58     ''' <summary>
59     ''' 默认是加载Xml文件的
60     ''' </summary>
61     ''' <typeparam name="T"></typeparam>
62     ''' <param name="file"></param>
63     ''' <param name="encoding"></param>
64     ''' <param name="parser">default is Xml parser</param>
65     ''' <param name="ThrowEx"></param>
66     ''' <returns></returns>
67     <Extension>
68     Public Function LoadTextDoc(Of T As ITextFile)(file$,
69                                                    Optional encoding As Encoding = Nothing,
70                                                    Optional parser As Func(Of String, Encoding, T) = Nothing,
71                                                    Optional ThrowEx As Boolean = TrueAs T
72         If parser Is Nothing Then
73             parser = AddressOf LoadXml
74         End If
75
76         Dim FileObj As T
77
78         Try
79             FileObj = parser(file, encoding)
80             FileObj.FilePath = file
81         Catch ex As Exception
82             Call App.LogException(New Exception(file.ToFileURL, ex))
83
84             If ThrowEx Then
85                 Throw ex
86             Else
87 #If DEBUG Then
88                 Call ex.PrintException
89 #End If
90                 Return Nothing
91             End If
92         End Try
93
94         Return FileObj
95     End Function
96
97     ''' <summary>
98     ''' 
99     ''' </summary>
100     ''' <param name="handle$">
101     ''' + 当这个参数为文件路径的时候会返回<see cref="Linq.IteratesALL(Of T)(IEnumerable(Of IEnumerable(Of T)))"/>函数的结果
102     ''' + 当这个参数只是为文本字符串的时候,则会返回<see cref="LineTokens"/>函数的结果
103     ''' </param>
104     ''' <returns></returns>
105     <Extension>
106     Public Function LineIterators(handle$) As IEnumerable(Of String)
107         If handle.FileExists Then
108             Return handle.IterateAllLines
109         Else
110             Return handle.LineTokens
111         End If
112     End Function
113
114     ''' <summary>
115     ''' 解析出TSV文件的头部并且生成index数据
116     ''' </summary>
117     ''' <param name="path$">``*.tsv``文件路径</param>
118     ''' <returns></returns>
119     <Extension>
120     Public Function TsvHeaders(path$) As Index(Of String)
121         Dim header$() = path.ReadFirstLine.Split(ASCII.TAB)
122         Dim index As New Index(Of String)(header)
123         Return index
124     End Function
125
126     ''' <summary>
127     ''' 将IDmapping数据保存为tsv文件
128     ''' </summary>
129     ''' <param name="tsv"></param>
130     ''' <param name="path$"></param>
131     ''' <param name="encoding"></param>
132     ''' <returns></returns>
133     <Extension>
134     Public Function SaveTSV(tsv As IEnumerable(Of IDMap), path$, Optional encoding As Encodings = Encodings.ASCII) As Boolean
135         Dim lines = tsv.Select(Function(x) x.TSV)
136         Return lines.SaveTo(path, encoding.CodePage)
137     End Function
138
139     ''' <summary>
140     ''' Enumerate all of the chars in the target text file.
141     ''' </summary>
142     ''' <param name="path"></param>
143     ''' <param name="encoding"></param>
144     ''' <returns></returns>
145     <Extension>
146     Public Iterator Function ForEachChar(path$, Optional encoding As Encodings = Encodings.Default) As IEnumerable(Of Char)
147         Using file As New FileStream(path, FileMode.Open)
148             Using reader As New BinaryReader(file, encoding.CodePage)
149                 Dim bs As Stream = reader.BaseStream
150                 Dim l As Long = bs.Length
151
152                 Do While bs.Position < l
153                     Yield reader.ReadChar
154                 Loop
155             End Using
156         End Using
157     End Function
158
159     ''' <summary>
160     ''' Open text file writer, this function will auto handle all things.
161     ''' </summary>
162     ''' <param name="path"></param>
163     ''' <param name="encoding"></param>
164     ''' <returns></returns>
165     <MethodImpl(MethodImplOptions.AggressiveInlining)>
166     <Extension>
167     Public Function OpenWriter(path$,
168                                Optional encoding As Encodings = Encodings.UTF8,
169                                Optional newLine$ = ASCII.LF,
170                                Optional append As Boolean = FalseAs StreamWriter
171         Return FileIO.OpenWriter(path, encoding.CodePage, newLine, append)
172     End Function
173
174     ''' <summary>
175     ''' Reading a super large size text file through stream method.
176     ''' (通过具有缓存的流对象读取文本数据,使用迭代器来读取文件之中的所有的行,大文件推荐使用这个方法进行读取操作)
177     ''' </summary>
178     ''' <param name="path"></param>
179     ''' <returns>不存在的文件会返回空集合</returns>
180     <Extension>
181     Public Iterator Function IterateAllLines(path$, Optional encoding As Encodings = Encodings.Default) As IEnumerable(Of String)
182         If Not path.FileExists Then
183             Return
184         End If
185
186         Using fs As New FileStream(path, FileMode.Open, access:=FileAccess.Read, share:=FileShare.Read)
187             Using reader As New StreamReader(fs, encoding.CodePage)
188
189                 Do While Not reader.EndOfStream
190                     Yield reader.ReadLine
191                 Loop
192             End Using
193         End Using
194     End Function
195
196     ''' <summary>
197     ''' Read the first line of the text in the file.
198     ''' </summary>
199     ''' <param name="path"></param>
200     ''' <param name="encoding">
201     ''' Parameter value is set to <see cref="DefaultEncoding"/> if this parameter is not specific.
202     ''' </param>
203     ''' <returns></returns>
204     <Extension> Public Function ReadFirstLine(path$, Optional encoding As Encoding = NothingAs String
205         Using file As New FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)
206             Using reader As New StreamReader(file, encoding Or DefaultEncoding)
207                 Dim first$ = reader.ReadLine
208                 Return first
209             End Using
210         End Using
211     End Function
212
213     ''' <summary>
214     ''' 自动进行判断解决所读取的数据源,当<paramref name="handle"/>为文件路径的时候,会读取文件内容,反之则会直接返回<paramref name="handle"/>的内容
215     ''' </summary>
216     ''' <param name="handle$">文本内容或者文件路径</param>
217     ''' <returns></returns>
218     <Extension> Public Function SolveStream(handle$, Optional encoding As Encodings = Encodings.UTF8) As String
219         If handle.FileExists(True) Then
220             Return handle.ReadAllText(encoding.CodePage)
221         Else
222             Return handle
223         End If
224     End Function
225
226     ''' <summary>
227     ''' This function just suite for read a small text file.(这个函数只建议读取小文本文件的时候使用)
228     ''' </summary>
229     ''' <param name="path"></param>
230     ''' <param name="encoding">Default value is <see cref="Encoding.UTF8"/></param>
231     ''' <param name="suppress">Suppress error message??</param>
232     ''' <returns></returns>
233     ''' <remarks></remarks>
234     '''
235     <ExportAPI("Read.TXT")>
236     <Extension>
237     Public Function ReadAllText(path$,
238                                 Optional encoding As Encoding = Nothing,
239                                 Optional throwEx As Boolean = True,
240                                 Optional suppress As Boolean = FalseAs String
241         Try
242             Return fs.ReadAllText(path, encoding:=encoding Or UTF8)
243         Catch ex As Exception
244             ex = New Exception(path.ToFileURL, ex)
245
246             If throwEx Then
247                 Throw ex
248             Else
249                 Call App.LogException(ex)
250
251                 If Not suppress Then
252                     Call ex.PrintException
253                 End If
254             End If
255         End Try
256
257         Return Nothing
258     End Function
259
260     ''' <summary>
261     ''' This function is recommend using for the small(probably smaller than 300MB) text file reading.
262     ''' (这个函数只建议读取小文本文件的时候使用)
263     ''' </summary>
264     ''' <param name="path"></param>
265     ''' <param name="Encoding">Default value is UTF8</param>
266     ''' <returns></returns>
267     ''' <remarks></remarks>
268     <MethodImpl(MethodImplOptions.AggressiveInlining)>
269     <ExportAPI("Read.Lines")>
270     <Extension>
271     Public Function ReadAllLines(path$, Optional Encoding As Encoding = NothingAs String()
272         If path.FileExists Then
273             Return File.ReadAllLines(path, encoding:=Encoding Or DefaultEncoding)
274         Else
275             Return New String() {}
276         End If
277     End Function
278
279     ''' <summary>
280     ''' 使用html文本的默认编码格式<see cref="Encodings.UTF8"/>来保存这个文本文件
281     ''' </summary>
282     ''' <param name="html$"></param>
283     ''' <param name="path$"></param>
284     ''' <returns></returns>
285     <Extension>
286     <MethodImpl(MethodImplOptions.AggressiveInlining)>
287     Public Function SaveWithHTMLEncoding(html$, path$) As Boolean
288         Return html.SaveTo(path, Encoding.UTF8)
289     End Function
290
291     ''' <summary>
292     ''' Write the text file data into a file which was specific by the <paramref name="path"></paramref> value,
293     ''' this function not append the new data onto the target file.
294     ''' (将目标文本字符串写入到一个指定路径的文件之中,但是不会在文件末尾追加新的数据)
295     ''' </summary>
296     ''' <param name="path"></param>
297     ''' <param name="text"></param>
298     ''' <param name="encoding">这个函数会自动处理文本的编码的</param>
299     ''' <returns></returns>
300     ''' <remarks></remarks>
301     '''
302     <ExportAPI("Write.Text")>
303     <Extension> Public Function SaveTo(<Parameter("Text")> text As String,
304                                        <Parameter("Path")> path As String,
305                                        <Parameter("Text.Encoding")>
306                                        Optional encoding As Encoding = Nothing,
307                                        Optional append As Boolean = False,
308                                        Optional throwEx As Boolean = TrueAs Boolean
309
310         If String.IsNullOrEmpty(path) Then
311             Return False
312         End If
313
314         Dim DIR As String
315
316 #If UNIX Then
317         DIR = System.IO.Directory.GetParent(path).FullName
318 #Else
319         Try
320             path = ProgramPathSearchTool.Long2Short(path)
321             DIR = fs.GetParentPath(path)
322         Catch ex As Exception
323             Dim msg As String = $" **** Directory string is illegal or string is too long:  [{NameOf(path)}:={path}] > 260"
324             Throw New Exception(msg, ex)
325         End Try
326 #End If
327
328         If String.IsNullOrEmpty(DIR) Then
329             DIR = App.CurrentDirectory
330         Else
331             DIR.MkDIR(throwEx:=False)
332         End If
333
334         Try
335             Call fs.WriteAllText(path, text Or EmptyString, append, encoding Or UTF8)
336         Catch ex As Exception
337             ex = New Exception("[DIR]  " & DIR, ex)
338             ex = New Exception("[Path]  " & path, ex)
339
340             If throwEx Then
341                 Throw ex
342             Else
343                 Call App.LogException(ex)
344                 Return False
345             End If
346         End Try
347
348         Return True
349     End Function
350
351     ''' <summary>
352     ''' Save the inner text value of a xml element
353     ''' </summary>
354     ''' <param name="value"></param>
355     ''' <param name="path"></param>
356     ''' <param name="encoding"></param>
357     ''' <returns></returns>
358     <ExportAPI("Write.Text")>
359     <MethodImpl(MethodImplOptions.AggressiveInlining)>
360     <Extension> Public Function SaveTo(value As XElement, path$, Optional encoding As Encoding = NothingAs Boolean
361         Return value.Value.SaveTo(path, encoding)
362     End Function
363
364     ''' <summary>
365     ''' Determined that the target file is a text file or binary file?
366     ''' (判断是否是文本文件)
367     ''' </summary>
368     ''' <param name="path">文件全路径名称</param>
369     ''' <returns>是返回True,不是返回False</returns>
370     ''' <param name="chunkSize">文件检查的长度,假若在这个长度内都没有超过null的阈值数,则认为该文件为文本文件,默认区域长度为4KB</param>
371     ''' <remarks>2012年12月5日</remarks>
372     '''
373     <ExportAPI("IsTextFile")>
374     <Extension> Public Function IsTextFile(path$, Optional chunkSize% = 4 * 1024) As Boolean
375         Using file As New FileStream(path, FileMode.Open, FileAccess.Read)
376             Dim byteData(1) As Byte
377             Dim i%
378             Dim p%
379
380             While file.Read(byteData, 0, byteData.Length) > 0
381                 If byteData(0) = 0 Then i += 1
382
383                 If p <= chunkSize Then
384                     p += 1
385                 Else
386                     Exit While
387                 End If
388             End While
389
390             Return i <= 0.1 * p
391         End Using
392     End Function
393
394     ''' <summary>
395     ''' 将目标字符串集合数据全部写入到文件之中,当所写入的文件位置之上没有父文件夹存在的时候,会自动创建文件夹
396     ''' </summary>
397     ''' <param name="array"></param>
398     ''' <param name="path"></param>
399     ''' <param name="encoding"></param>
400     ''' <returns></returns>
401     ''' <remarks></remarks>
402     '''
403     <ExportAPI("Write.Text")>
404     <Extension> Public Function SaveTo(array As IEnumerable(Of String), path$, Optional encoding As Encoding = NothingAs Boolean
405         If String.IsNullOrEmpty(path) Then
406             Return False
407         End If
408
409         Call "".SaveTo(path)
410
411         Using fs As New FileStream(path, FileMode.OpenOrCreate),
412             file As New StreamWriter(fs, encoding Or DefaultEncoding)
413
414             For Each line$ In array.SafeQuery
415                 Call file.WriteLine(line)
416             Next
417         End Using
418
419         Return True
420     End Function
421
422     ''' <summary>
423     ''' Save the text content in the <see cref="StringBuilder"/> object into a text file.
424     ''' </summary>
425     ''' <param name="sb"></param>
426     ''' <param name="path"></param>
427     ''' <param name="encoding"></param>
428     ''' <returns></returns>
429     <ExportAPI("Write.Text")>
430     <MethodImpl(MethodImplOptions.AggressiveInlining)>
431     <Extension> Public Function SaveTo(sb As StringBuilder, path$, Optional encoding As Encoding = NothingAs Boolean
432         Return sb.ToString.SaveTo(path, encoding)
433     End Function
434 End Module