2019-02-19 18:25:51 +08:00
|
|
|
|
#Region "Microsoft.VisualBasic::487b931cb55478bfb5e4e2ae15685c54, Microsoft.VisualBasic.Core\ComponentModel\DataSource\Tsv.vb"
|
2018-08-02 20:14:48 +08:00
|
|
|
|
|
|
|
|
|
|
' 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 TsvFileIO
|
|
|
|
|
|
'
|
|
|
|
|
|
' Function: GetTsvHeader, Load, LoadByIndex, LoadFile
|
|
|
|
|
|
'
|
|
|
|
|
|
'
|
|
|
|
|
|
' /********************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
#End Region
|
|
|
|
|
|
|
|
|
|
|
|
Imports System.IO
|
|
|
|
|
|
Imports System.Reflection
|
|
|
|
|
|
Imports System.Runtime.CompilerServices
|
|
|
|
|
|
Imports System.Text
|
|
|
|
|
|
Imports Microsoft.VisualBasic.ComponentModel.Collection
|
|
|
|
|
|
Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel.SchemaMaps
|
|
|
|
|
|
Imports Microsoft.VisualBasic.Language
|
|
|
|
|
|
Imports Microsoft.VisualBasic.Language.Default
|
|
|
|
|
|
Imports Microsoft.VisualBasic.Linq
|
|
|
|
|
|
Imports Microsoft.VisualBasic.Text
|
|
|
|
|
|
Imports FieldTuple = System.Collections.Generic.KeyValuePair(Of String, System.Reflection.PropertyInfo)
|
2019-03-08 19:32:07 +08:00
|
|
|
|
Imports RowTokens = System.Collections.Generic.IEnumerable(Of String)
|
2018-08-02 20:14:48 +08:00
|
|
|
|
|
|
|
|
|
|
Namespace ComponentModel.DataSourceModel
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' 将文件读取出来然后对每一行数据进行分割,由于没有使用自定义属性来标记列的名称,所以这个很简单的tsv加载器要求属性的名称与列名称要完全一致。
|
|
|
|
|
|
''' 而且,还不能够为非初始数据类型,这个模块之中提供了简单的数据类型转换操作,这个只是一个简单的内置TSv文件读取模块
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <remarks></remarks>
|
|
|
|
|
|
Public Module TsvFileIO
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Columns indexing by title.(自动将tsv文件数据之中的行解析反序列化加载为一个Class对象)
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <typeparam name="T"></typeparam>
|
|
|
|
|
|
''' <param name="Path"></param>
|
|
|
|
|
|
''' <returns></returns>
|
|
|
|
|
|
Public Iterator Function Load(Of T As Class)(path$, Optional encoding As Encodings = Encodings.UTF8) As IEnumerable(Of T)
|
|
|
|
|
|
Dim data As IEnumerable(Of RowTokens) = TsvFileIO.LoadFile(path, encoding.CodePage, skipFirstLine:=True)
|
|
|
|
|
|
Dim tableSchema = DataFramework.Schema(Of T)(PropertyAccess.ReadWrite, True)
|
|
|
|
|
|
Dim type As Type = GetType(T)
|
|
|
|
|
|
Dim schemaOrdinals As Index(Of String) =
|
|
|
|
|
|
path _
|
|
|
|
|
|
.OpenReader(encoding.CodePage) _
|
|
|
|
|
|
.GetTsvHeader(False)
|
|
|
|
|
|
Dim typers = tableSchema.ToDictionary(
|
|
|
|
|
|
Function(m) m.Key,
|
|
|
|
|
|
Function(p) p.Value.PropertyType)
|
|
|
|
|
|
|
|
|
|
|
|
For Each line As String() In data.Select(Function(r) DirectCast(r, String()))
|
|
|
|
|
|
Dim o As Object = Activator.CreateInstance(type)
|
|
|
|
|
|
|
|
|
|
|
|
For Each field As FieldTuple In tableSchema
|
|
|
|
|
|
With field
|
|
|
|
|
|
Dim index As Integer = schemaOrdinals(.Key)
|
|
|
|
|
|
Dim s$ = line(index)
|
|
|
|
|
|
Dim value As Object = Scripting.CTypeDynamic(s, typers(.Key))
|
|
|
|
|
|
|
|
|
|
|
|
Call .Value.SetValue(o, value)
|
|
|
|
|
|
End With
|
|
|
|
|
|
Next
|
|
|
|
|
|
|
|
|
|
|
|
Yield DirectCast(o, T)
|
|
|
|
|
|
Next
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Columns indexing by position.
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <typeparam name="T"></typeparam>
|
|
|
|
|
|
''' <param name="path$"></param>
|
|
|
|
|
|
''' <param name="encoding"></param>
|
|
|
|
|
|
''' <param name="base">
|
|
|
|
|
|
''' By default the array index is ZERO based, or you can specific this array index base from 1, or any other integer values
|
|
|
|
|
|
''' </param>
|
|
|
|
|
|
''' <returns></returns>
|
|
|
|
|
|
Public Iterator Function LoadByIndex(Of T As Class)(path$, Optional encoding As Encodings = Encodings.UTF8, Optional base% = 0) As IEnumerable(Of T)
|
|
|
|
|
|
Dim data As IEnumerable(Of RowTokens) = TsvFileIO.LoadFile(path, encoding.CodePage, skipFirstLine:=False)
|
|
|
|
|
|
Dim type As Type = GetType(T)
|
|
|
|
|
|
Dim index = DataFrameColumnAttribute _
|
|
|
|
|
|
.LoadMapping(type, mapsAll:=False) _
|
|
|
|
|
|
.Values _
|
|
|
|
|
|
.OrderBy(Function(field)
|
|
|
|
|
|
Return field.field.Index
|
|
|
|
|
|
End Function) _
|
2019-03-08 19:32:07 +08:00
|
|
|
|
.ToDictionary(Function(i)
|
|
|
|
|
|
Return i.field.Index
|
|
|
|
|
|
End Function)
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Dim str$
|
2019-03-08 19:32:07 +08:00
|
|
|
|
Dim objVal As Object
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Dim fields As PropertyInfo() = data _
|
|
|
|
|
|
.First _
|
|
|
|
|
|
.Count _
|
|
|
|
|
|
.SeqIterator(offset:=base) _
|
|
|
|
|
|
.Select(Function(i)
|
|
|
|
|
|
' The tsv file have 10 columns, but only have 7 columns was indexed in target class schema type
|
|
|
|
|
|
' Set all of the no-indexed column in tsv file its reader property to nothing.
|
|
|
|
|
|
If index.ContainsKey(i) Then
|
|
|
|
|
|
Return DirectCast(index(i).member, PropertyInfo)
|
|
|
|
|
|
Else
|
|
|
|
|
|
Return Nothing
|
|
|
|
|
|
End If
|
|
|
|
|
|
End Function) _
|
|
|
|
|
|
.ToArray
|
|
|
|
|
|
|
|
|
|
|
|
For Each line As RowTokens In data
|
|
|
|
|
|
Dim o = Activator.CreateInstance(type)
|
|
|
|
|
|
|
|
|
|
|
|
For Each col As SeqValue(Of String) In line.SeqIterator
|
|
|
|
|
|
str = col.value
|
|
|
|
|
|
|
|
|
|
|
|
With fields(col)
|
|
|
|
|
|
If Not .IsNothing Then
|
2019-03-08 19:32:07 +08:00
|
|
|
|
objVal = Scripting.CTypeDynamic(str, .PropertyType)
|
|
|
|
|
|
|
|
|
|
|
|
' set property value
|
|
|
|
|
|
Call .SetValue(obj:=o, value:=objVal)
|
2018-08-02 20:14:48 +08:00
|
|
|
|
End If
|
|
|
|
|
|
End With
|
|
|
|
|
|
Next
|
|
|
|
|
|
|
|
|
|
|
|
Yield DirectCast(o, T)
|
|
|
|
|
|
Next
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' Returns the source string without any processing
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
ReadOnly withoutProcess As New DefaultValue(Of Func(Of String, String))(Function(str) str)
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
'''
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <param name="stream"></param>
|
|
|
|
|
|
''' <param name="lower"></param>
|
|
|
|
|
|
''' <param name="process"></param>
|
|
|
|
|
|
''' <returns></returns>
|
|
|
|
|
|
''' <remarks>
|
|
|
|
|
|
''' Linux平台上面的mono这里有bug,为什么<see cref="StreamReader.ReadLine()"/>一直都输出空值?
|
|
|
|
|
|
''' </remarks>
|
|
|
|
|
|
<Extension>
|
|
|
|
|
|
Public Function GetTsvHeader(stream As StreamReader,
|
|
|
|
|
|
Optional lower As Boolean = False,
|
|
|
|
|
|
Optional process As Func(Of String, String) = Nothing) As Index(Of String)
|
|
|
|
|
|
|
|
|
|
|
|
Dim line$ = stream.ReadLine
|
|
|
|
|
|
Dim headers$() = line _
|
|
|
|
|
|
.Split(ASCII.TAB) _
|
2019-03-08 19:32:07 +08:00
|
|
|
|
.Select(selector:=process Or withoutProcess) _
|
2018-08-02 20:14:48 +08:00
|
|
|
|
.ToArray
|
|
|
|
|
|
|
|
|
|
|
|
If lower Then
|
|
|
|
|
|
Return headers _
|
|
|
|
|
|
.Select(AddressOf Strings.LCase) _
|
|
|
|
|
|
.Indexing
|
|
|
|
|
|
Else
|
|
|
|
|
|
Return New Index(Of String)(headers)
|
|
|
|
|
|
End If
|
|
|
|
|
|
End Function
|
|
|
|
|
|
|
|
|
|
|
|
''' <summary>
|
|
|
|
|
|
''' 读取文件并且按照TAb进行分割
|
|
|
|
|
|
''' </summary>
|
|
|
|
|
|
''' <param name="path"></param>
|
|
|
|
|
|
''' <param name="skipFirstLine">The first line of the text document maybe is the title headers, skip this line?</param>
|
|
|
|
|
|
''' <returns></returns>
|
2019-03-08 19:32:07 +08:00
|
|
|
|
Public Function LoadFile(path$, Optional encoding As Encoding = Nothing, Optional skipFirstLine As Boolean = False) As IEnumerable(Of RowTokens)
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Dim lines As String() = TextDoc.ReadAllLines(path, encoding Or UTF8)
|
|
|
|
|
|
Dim LQuery = LinqAPI.Exec(Of RowTokens) _
|
|
|
|
|
|
_
|
|
|
|
|
|
() <= From strLine As String
|
|
|
|
|
|
In lines
|
2019-03-08 19:32:07 +08:00
|
|
|
|
Let t As String() = Strings.Split(strLine, vbTab)
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Select DirectCast(t, RowTokens)
|
|
|
|
|
|
|
|
|
|
|
|
If skipFirstLine Then
|
2019-03-08 19:32:07 +08:00
|
|
|
|
' 跳过标题行
|
2018-08-02 20:14:48 +08:00
|
|
|
|
Return LQuery.Skip(1)
|
|
|
|
|
|
Else
|
|
|
|
|
|
Return LQuery
|
|
|
|
|
|
End If
|
|
|
|
|
|
End Function
|
|
|
|
|
|
End Module
|
|
|
|
|
|
End Namespace
|