Procedura: eseguire trasformazioni di flusso di testo in XML
Uno degli approcci disponibili per l'elaborazione di un file di testo consiste nello scrivere un metodo di estensione che genera un flusso del file di testo, una riga alla volta, tramite il costrutto yield return.È quindi possibile scrivere una query LINQ che elabora il file di testo in modo posticipato lazy.Se poi si utilizza XStreamingElement per il flusso di output, è possibile creare una trasformazione del file di testo in XML che utilizza una quantità minima di memoria, indipendentemente dalle dimensioni del file di testo di origine.
È necessario tener conto di alcune considerazioni in relazione alle trasformazioni di flusso.Le trasformazioni di flusso sono ideali nelle situazioni in cui è possibile elaborare l'intero file una sola volta e se è possibile elaborare le righe nell'ordine in cui sono riportate nel documento di origine.Se è necessario elaborare il file più volte o ordinare le righe prima che sia possibile elaborarle, si perderanno molti dei vantaggi associati all'utilizzo di una tecnica di flusso.
Esempio
Il file di testo seguente, People.txt, è l'origine di questo esempio.
#This is a comment
1,Tai,Yee,Writer
2,Nikolay,Grachev,Programmer
3,David,Wright,Inventor
Nel codice seguente è contenuto un metodo di estensione che genera il flusso delle righe del file di testo in modo posticipato.
Nota
Nell'esempio seguente viene utilizzato il costrutto yield return di C#.Codice equivalente viene fornito in Visual Basic utilizzando una classe che implementa l'interfaccia IEnumerable(Of XElement).Per un esempio di implementazione di IEnumerable(Of T) in Visual Basic, vedere Procedura dettagliata: implementazione di IEnumerable(Of T) in Visual Basic.
public static class StreamReaderSequence
{
public static IEnumerable<string> Lines(this StreamReader source)
{
String line;
if (source == null)
throw new ArgumentNullException("source");
while ((line = source.ReadLine()) != null)
{
yield return line;
}
}
}
class Program
{
static void Main(string[] args)
{
StreamReader sr = new StreamReader("People.txt");
XStreamingElement xmlTree = new XStreamingElement("Root",
from line in sr.Lines()
let items = line.Split(',')
where !line.StartsWith("#")
select new XElement("Person",
new XAttribute("ID", items[0]),
new XElement("First", items[1]),
new XElement("Last", items[2]),
new XElement("Occupation", items[3])
)
);
Console.WriteLine(xmlTree);
sr.Close();
}
}
Module Module1
Sub Main()
Dim sr = New IO.StreamReader("..\..\People.txt")
Dim xmlTree = New XStreamingElement("Root",
From line In sr.Lines()
Let items = Split(line, ",")
Where Not line.StartsWith("#")
Select <Person ID=<%= items(0) %>>
<First><%= items(1) %></First>
<Last><%= items(2) %></Last>
<Occupation><%= items(3) %></Occupation>
</Person>
)
Console.WriteLine(xmlTree)
sr.Close()
End Sub
End Module
Module StreamReaderSequence
<System.Runtime.CompilerServices.Extension()>
Public Function Lines(ByRef source As IO.StreamReader) As IEnumerable(Of String)
If source Is Nothing Then Throw New ArgumentNullException("source")
Return New StreamReaderEnumerable(source)
End Function
End Module
Public Class StreamReaderEnumerable
Implements IEnumerable(Of String)
Private _source As IO.StreamReader
Public Sub New(ByVal source As IO.StreamReader)
_source = source
End Sub
Public Function GetEnumerator() As Generic.IEnumerator(Of String) Implements IEnumerable(Of String).GetEnumerator
Return New StreamReaderEnumerator(_source)
End Function
Public Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
Return Me.GetEnumerator()
End Function
End Class
Public Class StreamReaderEnumerator
Implements IEnumerator(Of String)
Private _current As String
Private _source As IO.StreamReader
Public Sub New(ByVal source As IO.StreamReader)
_source = source
End Sub
Public ReadOnly Property Current As String Implements Generic.IEnumerator(Of String).Current
Get
Return _current
End Get
End Property
Public ReadOnly Property Current1 As Object Implements IEnumerator.Current
Get
Return Me.Current
End Get
End Property
Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
_current = _source.ReadLine()
Return If(_current IsNot Nothing, True, False)
End Function
Public Sub Reset() Implements IEnumerator.Reset
_current = Nothing
_source.DiscardBufferedData()
_source.BaseStream.Seek(0, IO.SeekOrigin.Begin)
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
L'output ottenuto eseguendo l'esempio è il seguente:
<Root>
<Person ID="1">
<First>Tai</First>
<Last>Yee</Last>
<Occupation>Writer</Occupation>
</Person>
<Person ID="2">
<First>Nikolay</First>
<Last>Grachev</Last>
<Occupation>Programmer</Occupation>
</Person>
<Person ID="3">
<First>David</First>
<Last>Wright</Last>
<Occupation>Inventor</Occupation>
</Person>
</Root>