Condividi tramite


VB.NET: Ordinare e Filtrare Files CSV tramite DataTable e DataView (it-IT)


Introduzione

Lavorando su dati gestionali, una necessità piuttosto comune è quella di rendere intercomunicabili software diversi, che dovranno trovarsi a condividere determinate informazioni. Sebbene l'introduzione di linguaggi d markup come XML stia soppiantando la vecchia prassi, mote volte la fase di scambio dati è demandata a semplici file di testo (CSV, o Comma Separated Values). Tale metodologia è decisamente adatta allo scopo, soprattutto quando i dati che ci vengono forniti non hanno bisogno di essere rimaneggiati prima di un'eventuale importazione nelle nostre tabelle, ma se ci troviamo invece nella necessità di effettuare pre-elaborazioni sui dati forniti, un file di testo non rappresenta sicuramente l'opzione più comoda su cui lavorare. Ecco che però le classi DataTable e DataView possono venire in nostro soccorso, aiutandoci a gestire i dati in arrivo in forma tabellare. In questo articolo, vedremo alcuni brevi esempi in proposito, facendo uso di Visual Basic .NET.

Un semplice scenario

Consideriamo un semplice file CSV, in cui i campi sono separati dal carattere pipe:

1.Name|Surname|Age|Occupation|City
2.John|Doe|30|Developer|New York
3.Jack|NoName|25|DBA|Los Angeles
4.Mario|Mario|42|Plumber|Unknown
5.Laura|Green|25|Developer|Unknown

Abbiamo una prima riga di intestazione campi, e quattro righe dati. Filtrare o ordinare tali dati, mantenendo la loto forma testuale originaria, può essere un compito decisamente fastidioso. Potremmo importare il file in uno sheet Excel, in una tabella di SQL Server, e così via, ma potremmo anche trovarci in situazioni in cui non disponiamo di tali strumenti, e vi è comunque bisogno di un certo automatismo. Potremmo trovarci nella necessità di dover sviluppare un interprete di questi dati, che a partire dal file di origine ne generi un secondo con diversa struttura. Gli scenari possibili sono molteplici.

In casi come questi, la classe DataTable è senz'altro ciò di cui necessittiamo. Così come riportato nella documentazione MSDN, la DataTable rappresenta una tabella di dati in memoria. È una sorta di tabella virtuale, nella quale possiamo rappresentare dati in forma tabellare, ossia organizzati in righe e colonne, potendo altresì contare sulle particolarità che tale struttura offre (accesso ai dati, relazionalità, e così via). Una DataTable può inoltre essere bindata ad un qualsiasi controllo, WPF o WinForm, che disponga delle proprietà DataSource o ItemSource.

Creare una DataTable da un file CSV

Creiamo allora una DataTable attingendo dai dati visti sopra. Per questioni di immediatezza, ho redatto uno scorcio di codice che prepari un file contenente le informazioni precedentemente analizzate. Consideriamo quanto segue:

01.Const sampleFile As String  = "c:\temp\sample.txt"
02. 
03.'-- Create sample data, writing them to c:\temp\sample.txt
04.Dim _sampleData As String  = "Name|Surname|Age|Occupation|City"  & Environment.NewLine & _
05.                "John|Doe|30|Developer|New York" & Environment.NewLine & _
06.                "Jack|NoName|25|DBA|Los Angeles" & Environment.NewLine & _
07.                "Mario|Mario|42|Plumber|Unknown" & Environment.NewLine & _
08.                "Laura|Green|25|Developer|Unknown"
09. 
10.IO.File.WriteAllText(sampleFile, _sampleData)

Lo snippet crea un file di nome "sample.txt" sotto la directory "c:\temp", scrivendo in esso il contenuto della variabile _sampleData. Partendo da un tale file, inizializzeremo la DataTable, creando le colonne rappresentanti i nostri campi. Dal momento che abbiamo i nomi di campo sulla prima riga del file, possiamo scrivere uno snippet come il seguente:

1.'-- Create a datatable from our text file 
2.Dim dt As New DataTable("Sample")
3. 
4.'-- Opens sample file, read first line, assign 
5.For Each  l As  String In  IO.File.ReadLines(sampleFile)(0).Split("|")
6.     dt.Columns.Add(l)
7.Next

Abbiamo chiamato la nostra DataTable "Sample". Successivamente, ciclando sull'array di stringhe ottenuto dalla separazione per pipe della prima riga del file, abbiamo estratto ciascun nome campo (Name, Surname, Age, Occupation, City). Per ciascuna stringa, si è quindi effettuata una chiamata alla funzione Add della DataColumnCollection, in modo da creare all'interno della DataTable una colonna avente nome pari al parametro passato alla funzione (nel nostro caso, il nome di campo letto dal file).

Quanto sopra ha creato la struttura della tabella: ora dobbiamo però popolarne i dati. Ciò significa che dovremo applicare una logica simile a quella utilizzata per le colonne, questa volta sulle righe, a partire dalla seconda fino alla fine del file..

1.'-- Read sample data as rows
2.Dim nRow As Boolean  = False
3.For Each  l As  String In  IO.File.ReadLines(sampleFile)
4.    If Not (nRow) Then  nRow = True  : Continue For
5.    dt.Rows.Add(l.Split("|"))
6.Next

Semplicemente, cicliamo su tutte le righe che non siano la prima, ogni volta aggiungendo un oggetto Row alla nostra DataTable. Dal momento che uno degli overloads di Rows.Add accetta un array di oggetti, possiamo utilizzare l'array ottenuto tramite splitting di ciascuna riga per il proprio separatore (l.Split("|")). Al termine del ciclo, la nostra DataTable avrà quindi una struttura valida, così come validi saranno i dati di cui è costituita.

DataTable come DataSource

Possiamo testare la nostra DataTable in congiunzione ad una DataGridView, o altro controllo dotato di proprietà DataSource, per verificare che tutto sia andato per il vero giusto. Ho aggiunto al mio form una DataGridView, alla quale possiamo bindare la DataTable nel modo seguente:

1.'-- The DataTable could be used as a Data Source
2.DataGridView1.DataSource = dt

Eseguendo il programma, il risultato sarà:

Possiamo quindi notare che i dati letti sono correttamente visualizzati. Sono altresì presentati nel modo in cui sono stati letti dal file, non vi sono filtri né ordinamenti particolari. Ogni riga è nella posizione in cui si trovava all'interno del flusso originale (per esempio "John Doe" è la prima riga, "Laura Green" l'ultima).

Utilizzo di DataView per ordinare e filtrare i dati ​

Come affermato dalla documentazione MSDN, una DataView «rappresenta una vista bindabile e personalizzabile di una DataTable, utile ad ordinare, filtrare, ricercare, editare e navigare i dati. La DataView non contiene di per sé dati, ma piuttosto rappresenta una vista connessa alla DataTable corrispondente». Pertanto, le DataViews ci consentono di personalizzare il modo in cui i dati vengono presentati.

Diciamo di voler ordinare i nostri dati secondo la colonna Age, mostrando prima le anagrafiche più vecchie. Possiamo ottenere ciò semplicemente scrivendo:

1.Dim dv As New DataView(dt)
2.dv.Sort = "Age DESC"

E quindi effettuando il binding della vista al nostro controllo

1.DataGridView1.DataSource = dv

Eseguendo il nostro programma ora, vedremo le righe ordinate come richiesto. L'ordinamento può essere eseguito su più colonne: per esempio, se volessimo ordinare per Age discendente e per Name discendente, potremmo modificare la proprietà Sort vista sopra nel modo seguente:

1.dv.Sort = "Age DESC, Name DESC"

Ovviamente si possono aggiungere tutte le colonne desiderate, separando ciascun ordinamento con una virgola. I predicati DESC e ASC determinano la direzione dell'ordinamento, rispettivamente Discendente (dal più grande al più piccolo) o Ascendente (dal più piccolo al più grande).

Nello screenshot, è possibile notare l'ordinamento discendente per Age, e l'ordinamento secondario discendente per Name nei casi di uguale valore su Age. Anche i filtri sulle DataViews mantengono lo stesso livello di accessibilità. Usando la proprietà RowFilter, possiamo scrivere delle espressioni concise per determinare i dati da mostrare. Per esempio, supponiamo di voler estrarre solo quei records in cui la città ha valore di "Unknonw". Potremo quindi scrivere:

1.dv.RowFilter = "City = 'Unknown'"

Ed il risultato sarà:

Un multifiltro può essere impostato legando tra loro diverse espressioni, mediante i predicati logici AND e OR. L'espressione:

1.dv.RowFilter = "Age < 30 OR Age=42"

Risulterà in:

Dal momento che abbiamo solo due record nel set iniziale in cui Age < 30 e solo uno in cui Age = 42.

Da DataView a Testo

Supponiamo ora di voler creare un secondo file CSV, contenente solo i record in cui il campo City contiene uno spazio bianco, il tutto ordinato in modo ascendente per la colonna Age. Un modo semplice per ottenere tale esportazione può essere: 

01.Dim dw As New DataView(dt)
02.dw.Sort = "Age ASC"
03.dw.RowFilter = "City LIKE '% %'"
04. 
05.Dim lines As String  = ""
06.For Each  r As  DataRowView In  dw
07.    lines &= String.Join("|", r.Row.ItemArray) & Environment.NewLine
08.Next
09. 
10.IO.File.WriteAllText("c:\temp\output.txt", lines)

In altre parole, dichiariamo una DataView basata sulla DataTable precedentemente popolata, specificando le proprietà Sort e RowFilter. Dopodiché, eseguendo un ciclo sulle righe della vista, creiamo una stringa separata da pipes, ottenuta dal join dei campi della tabella. Infine, scriviamo i dati così estratti in un file di testo arbitrario.

Codice sorgente di esempio

Il codice sorgente di esempio può essere scaricato al seguente indirizzo: https://code.msdn.microsoft.com/Sort-and-Filter-CSV-files-ee8affda

Referenze

Altre lingue

Il presente articolo è localizzato nelle seguenti lingue