microsoft-visualbasic-runtime/Extensions/Doc/Text.vb

482 lines
17 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::54f6d5b57fb02072461335cc17660e6b, Microsoft.VisualBasic.Core\Extensions\Doc\Text.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 TextDoc
'
' Function: ForEachChar, IsTextFile, IterateAllLines, LineIterators, LoadTextDoc
' OpenWriter, ReadAllLines, ReadAllText, ReadFirstLine, SaveHTML
' SaveJson, (+4 Overloads) SaveTo, SaveTSV, SaveWithHTMLEncoding, SolveStream
' TsvHeaders
'
' /********************************************************************************/
#End Region
Imports System.IO
Imports System.Runtime.CompilerServices
Imports System.Text
Imports Microsoft.VisualBasic.CommandLine.Reflection
Imports Microsoft.VisualBasic.ComponentModel
Imports Microsoft.VisualBasic.ComponentModel.Collection
Imports Microsoft.VisualBasic.Linq
Imports Microsoft.VisualBasic.Scripting.MetaData
Imports Microsoft.VisualBasic.Serialization.JSON
Imports Microsoft.VisualBasic.Text
Imports fs = Microsoft.VisualBasic.FileIO.FileSystem
<Package("Doc.TextFile", Category:=APICategories.UtilityTools, Publisher:="xie.guigang@gmail.com")>
Public Module TextDoc
''' <summary>
''' 默认是加载Xml文件的
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="file"></param>
''' <param name="encoding"></param>
''' <param name="parser">default is Xml parser</param>
''' <param name="ThrowEx"></param>
''' <returns></returns>
<Extension>
Public Function LoadTextDoc(Of T As IFileReference)(file$,
Optional encoding As Encoding = Nothing,
Optional parser As Func(Of String, Encoding, T) = Nothing,
Optional ThrowEx As Boolean = True) As T
If parser Is Nothing Then
parser = AddressOf LoadXml
End If
Dim FileObj As T
Try
FileObj = parser(file, encoding)
FileObj.FilePath = file
Catch ex As Exception
Call App.LogException(New Exception(file.ToFileURL, ex))
If ThrowEx Then
Throw ex
Else
#If DEBUG Then
Call ex.PrintException
#End If
Return Nothing
End If
End Try
Return FileObj
End Function
''' <summary>
'''
''' </summary>
''' <param name="handle$">
''' + 当这个参数为文件路径的时候会返回<see cref="Linq.IteratesALL(Of T)(IEnumerable(Of IEnumerable(Of T)))"/>函数的结果
''' + 当这个参数只是为文本字符串的时候,则会返回<see cref="LineTokens"/>函数的结果
''' </param>
''' <returns></returns>
<Extension>
Public Function LineIterators(handle$) As IEnumerable(Of String)
If handle.FileExists Then
Return handle.IterateAllLines
Else
Return handle.LineTokens
End If
End Function
''' <summary>
''' 解析出TSV文件的头部并且生成index数据
''' </summary>
''' <param name="path$">``*.tsv``文件路径</param>
''' <returns></returns>
<Extension>
Public Function TsvHeaders(path$) As Index(Of String)
Dim header$() = path.ReadFirstLine.Split(ASCII.TAB)
Dim index As New Index(Of String)(header)
Return index
End Function
''' <summary>
''' 将IDmapping数据保存为tsv文件
''' </summary>
''' <param name="tsv"></param>
''' <param name="path$"></param>
''' <param name="encoding"></param>
''' <returns></returns>
<Extension>
Public Function SaveTSV(tsv As IEnumerable(Of IDMap), path$, Optional encoding As Encodings = Encodings.ASCII) As Boolean
Dim lines = tsv.Select(Function(x) x.TSV)
Return lines.SaveTo(path, encoding.CodePage)
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function SaveJson(Of T)(obj As T, path$,
Optional encoding As Encoding = Nothing,
Optional indent As Boolean = False) As Boolean
Return obj.GetJson(indent:=indent).SaveTo(path, encoding)
End Function
''' <summary>
''' Enumerate all of the chars in the target text file.
''' </summary>
''' <param name="path"></param>
''' <param name="encoding"></param>
''' <returns></returns>
<Extension>
Public Iterator Function ForEachChar(path$, Optional encoding As Encodings = Encodings.Default) As IEnumerable(Of Char)
Using file As New FileStream(path, FileMode.Open)
Using reader As New BinaryReader(file, encoding.CodePage)
Dim bs As Stream = reader.BaseStream
Dim l As Long = bs.Length
Do While bs.Position < l
Yield reader.ReadChar
Loop
End Using
End Using
End Function
''' <summary>
''' Open text file writer, this function will auto handle all things.
''' </summary>
''' <param name="path"></param>
''' <param name="encoding"></param>
''' <returns></returns>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function OpenWriter(path$,
Optional encoding As Encodings = Encodings.UTF8,
Optional newLine$ = ASCII.LF,
Optional append As Boolean = False) As StreamWriter
Return FileIO.OpenWriter(path, encoding.CodePage, newLine, append)
End Function
''' <summary>
''' Reading a super large size text file through stream method.
''' (通过具有缓存的流对象读取文本数据,使用迭代器来读取文件之中的所有的行,大文件推荐使用这个方法进行读取操作)
''' </summary>
''' <param name="path"></param>
''' <returns>不存在的文件会返回空集合</returns>
<Extension>
Public Iterator Function IterateAllLines(path$, Optional encoding As Encodings = Encodings.Default) As IEnumerable(Of String)
If Not path.FileExists Then
Return
End If
Using fs As New FileStream(path, FileMode.Open, access:=FileAccess.Read, share:=FileShare.Read)
Using reader As New StreamReader(fs, encoding.CodePage)
Do While Not reader.EndOfStream
Yield reader.ReadLine
Loop
End Using
End Using
End Function
''' <summary>
''' Read the first line of the text in the file.
''' </summary>
''' <param name="path"></param>
''' <param name="encoding">
''' Parameter value is set to <see cref="DefaultEncoding"/> if this parameter is not specific.
''' </param>
''' <returns></returns>
<Extension> Public Function ReadFirstLine(path$, Optional encoding As Encoding = Nothing) As String
If path.FileLength <= 0 Then
Return ""
End If
Using file As New FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)
Using reader As New StreamReader(file, encoding Or DefaultEncoding)
Dim first$ = reader.ReadLine
Return first
End Using
End Using
End Function
''' <summary>
''' 自动进行判断解决所读取的数据源,当<paramref name="handle"/>为文件路径的时候,
''' 会读取文件内容,反之则会直接返回<paramref name="handle"/>的内容
''' </summary>
''' <param name="handle$">The text content or file path string.(文本内容或者文件路径)</param>
''' <returns>Always returns a text content.</returns>
''' <remarks>
''' 不适用于大文本数据
''' </remarks>
<Extension>
Public Function SolveStream(handle$, Optional encoding As Encodings = Encodings.UTF8, Optional null$ = "null") As String
If handle Is Nothing Then
Return null
ElseIf handle.IndexOf(ASCII.CR) > -1 OrElse handle.IndexOf(ASCII.LF) > -1 Then
' is text content, not path
Return handle
ElseIf ILLEGAL_PATH_CHARACTERS _
.Any(Function(i)
' handle可能是绝对路径在windows之中绝对路径会含有盘符
' 例如E:\冒号会导致这里的判断出现BUG
' 所以需要添加一个额外的判断条件
Return i <> ":"c AndAlso handle.IndexOf(i) > -1
End Function) Then
' is text content, not path
Return handle
ElseIf handle.Count(":"c) > 1 Then
' json?
Return handle
End If
If handle.FileExists(True) Then
Return handle.ReadAllText(encoding.CodePage)
Else
Return handle
End If
End Function
''' <summary>
''' This function just suite for read a small text file.(这个函数只建议读取小文本文件的时候使用)
''' </summary>
''' <param name="path"></param>
''' <param name="encoding">Default value is <see cref="Encoding.UTF8"/></param>
''' <param name="suppress">Suppress error message??</param>
''' <returns></returns>
''' <remarks></remarks>
'''
<ExportAPI("Read.TXT")>
<Extension>
Public Function ReadAllText(path$,
Optional encoding As Encoding = Nothing,
Optional throwEx As Boolean = True,
Optional suppress As Boolean = False) As String
Try
Return fs.ReadAllText(path, encoding:=encoding Or UTF8)
Catch ex As Exception
ex = New Exception(path.ToFileURL, ex)
If throwEx Then
Throw ex
Else
Call App.LogException(ex)
If Not suppress Then
Call ex.PrintException
End If
End If
End Try
Return Nothing
End Function
''' <summary>
''' This function is recommend using for the small(probably smaller than 300MB) text file reading.
''' (这个函数只建议读取小文本文件的时候使用)
''' </summary>
''' <param name="path"></param>
''' <param name="Encoding">Default value is UTF8</param>
''' <returns></returns>
''' <remarks></remarks>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<ExportAPI("Read.Lines")>
<Extension>
Public Function ReadAllLines(path$, Optional Encoding As Encoding = Nothing) As String()
If path.FileExists Then
Return File.ReadAllLines(path, encoding:=Encoding Or DefaultEncoding)
Else
Return New String() {}
End If
End Function
''' <summary>
''' 使用html文本的默认编码格式<see cref="Encodings.UTF8"/>来保存这个文本文件
''' </summary>
''' <param name="html$"></param>
''' <param name="path$"></param>
''' <returns></returns>
<Extension>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
Public Function SaveWithHTMLEncoding(html$, path$) As Boolean
Return html.SaveTo(path, Encoding.UTF8)
End Function
''' <summary>
''' Write the text file data into a file which was specific by the <paramref name="path"></paramref> value,
''' this function not append the new data onto the target file.
''' (将目标文本字符串写入到一个指定路径的文件之中,但是不会在文件末尾追加新的数据)
''' </summary>
''' <param name="path"></param>
''' <param name="text"></param>
''' <param name="encoding">这个函数会自动处理文本的编码的</param>
''' <returns></returns>
''' <remarks></remarks>
'''
<ExportAPI("Write.Text")>
<Extension> Public Function SaveTo(<Parameter("Text")> text As String,
<Parameter("Path")> path As String,
<Parameter("Text.Encoding")>
Optional encoding As Encoding = Nothing,
Optional append As Boolean = False,
Optional throwEx As Boolean = True) As Boolean
If String.IsNullOrEmpty(path) Then
Return False
End If
Dim DIR As String
#If UNIX Then
DIR = System.IO.Directory.GetParent(path).FullName
#Else
Try
path = PathExtensions.Long2Short(path)
DIR = fs.GetParentPath(path)
Catch ex As Exception
Dim msg As String = $" **** Directory string is illegal or string is too long: [{NameOf(path)}:={path}] > 260"
Throw New Exception(msg, ex)
End Try
#End If
If String.IsNullOrEmpty(DIR) Then
DIR = App.CurrentDirectory
Else
DIR.MkDIR(throwEx:=False)
End If
Try
Call fs.WriteAllText(path, text Or EmptyString, append, encoding Or UTF8)
Catch ex As Exception
ex = New Exception("[DIR] " & DIR, ex)
ex = New Exception("[Path] " & path, ex)
If throwEx Then
Throw ex
Else
Call App.LogException(ex)
Return False
End If
End Try
Return True
End Function
''' <summary>
''' Save the inner text value of a xml element
''' </summary>
''' <param name="value"></param>
''' <param name="path"></param>
''' <param name="encoding"></param>
''' <returns></returns>
<ExportAPI("Write.Text")>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension> Public Function SaveTo(value As XElement, path$, Optional encoding As Encoding = Nothing) As Boolean
Return value.Value.SaveTo(path, encoding)
End Function
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension>
Public Function SaveHTML(html As XElement, path$, Optional encoding As Encodings = Encodings.UTF8WithoutBOM) As Boolean
Return html.ToString.SaveTo(path, encoding.CodePage)
End Function
''' <summary>
''' Determined that the target file is a text file or binary file?
''' (判断是否是文本文件)
''' </summary>
''' <param name="path">文件全路径名称</param>
''' <returns>是返回True不是返回False</returns>
''' <param name="chunkSize">文件检查的长度假若在这个长度内都没有超过null的阈值数则认为该文件为文本文件默认区域长度为4KB</param>
''' <remarks>2012年12月5日</remarks>
'''
<ExportAPI("IsTextFile")>
<Extension> Public Function IsTextFile(path$, Optional chunkSize% = 4 * 1024) As Boolean
Using file As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim byteData(1) As Byte
Dim i%
Dim p%
While file.Read(byteData, 0, byteData.Length) > 0
If byteData(0) = 0 Then i += 1
If p <= chunkSize Then
p += 1
Else
Exit While
End If
End While
Return i <= 0.1 * p
End Using
End Function
''' <summary>
''' 将目标字符串集合数据全部写入到文件之中,当所写入的文件位置之上没有父文件夹存在的时候,会自动创建文件夹
''' </summary>
''' <param name="array"></param>
''' <param name="path"></param>
''' <param name="encoding"></param>
''' <returns></returns>
''' <remarks></remarks>
'''
<ExportAPI("Write.Text")>
<Extension> Public Function SaveTo(array As IEnumerable(Of String), path$, Optional encoding As Encoding = Nothing) As Boolean
If String.IsNullOrEmpty(path) Then
Return False
End If
Call "".SaveTo(path)
Using fs As New FileStream(path, FileMode.OpenOrCreate),
file As New StreamWriter(fs, encoding Or DefaultEncoding)
For Each line$ In array.SafeQuery
Call file.WriteLine(line)
Next
End Using
Return True
End Function
''' <summary>
''' Save the text content in the <see cref="StringBuilder"/> object into a text file.
''' </summary>
''' <param name="sb"></param>
''' <param name="path"></param>
''' <param name="encoding"></param>
''' <returns></returns>
<ExportAPI("Write.Text")>
<MethodImpl(MethodImplOptions.AggressiveInlining)>
<Extension> Public Function SaveTo(sb As StringBuilder, path$, Optional encoding As Encoding = Nothing) As Boolean
Return sb.ToString.SaveTo(path, encoding)
End Function
End Module