sabato 10 marzo 2007

Leggere Files Excel con ADO.NET

Una richiesta frequente sui forum di programmazione riguarda come poter leggere o scrivere su file excel. A mio avviso il modo più semplice e comodo è quello di pensare i fogli excel esattamente come tabelle di un database e trattarli di conseguenza.

Un file Excel contiene al suo interno n fogli ed ogni singolo foglio può essere visto come una tabella a se stante. Con ADO.NET e OLEDB (ma lo stesso risultato si poteva ottenere anche con ADO classico) è possibile collegarsi ad un file, leggerne il contenuto e memorizzarlo in un DataSet esattamente allo stesso modo con cui si interroga un db SQL o di qualsiasi altro tipo.

Lo script qui sotto apre un qualsiasi file Excel, legge tutto il contenuto del Foglio1 e lo visualizza in una gridview [myGridView].


Private Sub ReadExcelFile(ByVal FilePath As String)
Dim oledbConn As OleDb.OleDbConnection
Dim oleCmd As OleDb.OleDbCommand
Dim oleDR As OleDb.OleDbDataAdapter
Dim myData As Data.DataSet
myData = New Data.DataSet

oledbConn = New OleDb.OleDbConnection
oledbConn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" &
FilePath & ";Extended Properties = ""Excel 8.0;HDR=YES;"""
oledbConn.Open()

oleCmd = New OleDb.OleDbCommand
oleCmd.Connection = oledbConn

oleCmd.CommandText = "SELECT * FROM [Foglio1$]"

oleDR = New OleDb.OleDbDataAdapter(oleCmd)
oleDR.Fill(myData, "Tab")

myGridView.DataSource = myData.Tables("Tab")

myData.Dispose()
myData = Nothing
oleDR.Dispose()
oleDR = Nothing
oleCmd.Dispose()
oleCmd = Nothing
oledbConn.Dispose()
oledbConn = Nothing
End Sub



Il Foglio1 deve esistere nel file e deve chiamarsi esattamente così altrimenti la procedura va in errore.

Il fatto che nella connectionstring sia specificata la proprietà HDR=YES indica ad ADO di utilizzare la prima riga del foglio per le intestazioni di colonna. Di conseguenza le colonne della GridView prenderanno come etichetta i valori di colonna della prima riga, mentre i dati visualizzati cominceranno dalla seconda riga del foglio. La cosa interessante è che questa proprietà (influenzando direttamente il dataset) consente di accedere programmaticamente alle colonne del foglio, specificandone il nome. (Per esempio: myData.Tables("Tab").Rows(0).Item("Codice")).

Il fatto di poter leggere i file Excel con ADO permette di stabilire un criterio unico di accesso ai dati rendendo la vita del programmatore decisamente più semplice.

Nel prossimo post vedremo come scrivere dati su Excel sempre attraverso ADO

sabato 3 marzo 2007

Una Funzione per Elencare Cartelle e Sottocartelle

A volte capita di dover scrivere delle routines che ricerchino un determinato file sul computer, oppure che leggano la struttura di una cartella, oppure ancora che creino una copia identica di una cartella con le relative sottocartelle in una nuova posizione.

In tutti questi casi, è necessario trovare un sistema per elencare le sottocartelle partendo da una cartella madre, e quindi navigare poi i risultati per effettuare le operazioni che ci interessano.
Può sembrare banale ma non lo è affatto per chi, come me, si è avvicinato tardi alla programmazione e lo ha fatto fondamentalmente da autodidatta. Chiunque volesse cimentarsi in un giochino "intellettuale" è invitato a ragionare su come scriverebbe una routine che navighi una cartella e ne sfogli tutte le sottocartelle. Vi assicuro che non è esattamente banale e per ottenere questo risultato, qualche anno fa, ho dovuto scrivere 3 o 4 funzioni per un totale di un centinaio di righe.

Ultimamente, con un pò di esperienza in più sulle spalle, mi sono trovato nuovamente ad affrontare il problema e ho cercato di riscrivere quelle funzioni in una maniera un pò più "scientifica" e fondamentalmente di ottimizzarle un pò.

Per riuscirci ho dovuto apprendere il concetto, piuttosto semplice tuttavia, delle funzioni ricorsive e cioè della possibilità di richiamare una funzione o routine dalla funzione stessa.

Il risultato è quello che trovate qua sotto, con la speranza come al solito che sia utile a qualcuno.


Public Function EnumerateSubFolders(ByVal RootFolder As String) As String()
Dim FolderTree(10000) As String

DoEnumerateSubFolders(RootFolder, FolderTree)

If Array.IndexOf(FolderTree, RootFolder) = -1 Then
FolderTree(Array.IndexOf(FolderTree, Nothing)) = RootFolder
End If

Array.Resize(FolderTree, Array.IndexOf(FolderTree, Nothing))

Return FolderTree
End Function


Private Sub DoEnumerateSubFolders(ByVal RootFolder As String, _
ByRef FolderArray As String())
Dim DirList As String()
DirList = System.IO.Directory.GetDirectories(RootFolder)

For i As Integer = 0 To DirList.GetUpperBound(0)
FolderArray(Array.IndexOf(FolderArray, Nothing)) = DirList(i)
DoEnumerateSubFolders(DirList(i), FolderArray)
Next
End Sub