Operazioni su cartelle di lavoro in sequenza: aprirle, salvarle, copiare dati, incollarli in una terza cartella. - dal 04/09/04 pagina vista: volte

Ovvero: aprire tutti i file xls di una cartella, uno alla volta, crearne una copia salvandola in un'altra cartella, copiare dei dati di un foglio della cartella aperta, in un foglio di un terzo file xls che funga da archivio.

Una precisazione per evitare malintesi: purtroppo si chiamano "Cartelle" sia le Directory del nostro hard-disk (che sono dei "contenitori", non sono files, ed in inglese si chiamano "Folder"), sia i file di Excel che in italiano sono "Cartelle di lavoro", e che in inglese si chiamano "Cartel". Nell'articolo quindi chiamerò "cartelle" le directory, e "file" le cartelle di lavoro di Excel.

Un esercizio interessante nato da una specifica esigenza, ma che potrà interessare anche perchè non è molto difficile. Vediamo di esaminare la situazione, che sembrerebbe complessa, ma non lo è:

  1. Supponiamo di avere una cartella, che per comodità chiameremo "Sorgente", sul nostro hard-disk. La cartella contiene più files .xls che contengono dati, ognuno con un nome diverso. Vogliamo creare un ciclo che ci consenta di aprire tutti i files, uno alla volta e richiuderlo, in sequenza.

  2. Di ogni file che apriremo, vogliamo crearne una copia, salvandola in un'altra cartella, che chiameremo "Destinazione". Inoltre del file aperto, vogliamo prendere dei dati che sono contenuti in una zona di un foglio di lavoro, e copiare questi dati in un foglio di un terzo file.xs, che chiameremo Archivio.xls, inserendo i dati, ed accodando i dati del foglio successivo che apriremo, proprio per creare un archivio totale dei dati provenienti da tutti i files che saranno aperti.

Il file Archivio.xls sarà il file dal quale lanciare la routine, e questo file potrà risiedere dove vorremo; noi supponiamo che si trovi in una terza cartella del nostro hard-disk.

Come è impostata la procedura: Apriremo per primo il file Archivio.xls e sullo stesso foglio di lavoro dove incolleremo i dati, useremo un pulsante al quale associare la macro, per lanciarla. Vediamo subito le istruzioni, in verde le spiegazioni: Presento tre procedure, la prima, questa sotto, cercherà tutti i file con estensione .xls presenti nella cartella "Sorgente", la seconda cercherà tutti i file .xls che corrispondino ad un nome file progressivo, quindi basto sulla progressione che avrà il nome, la terza cercherà i file basandosi sulla loro data di creazione o di modifica.

Sub CreaArchivio()
Application.ScreenUpdating = False
'questo sopra evita il saltellamento a schermo durante l'apertura, il salvataggio e la copia


'sotto: impostiamo la variabile "fs" usando l'oggetto FileSearch (cerca file), che con la sua 'proprietà LookIn  imposta la cartella in cui deve essere eseguita la ricerca di file specificata con 'Filename
Set fs = Application.FileSearch
With fs
.LookIn = "C:\Sorgente"
 'indichiamo la cartella in cui cercare
.Filename = "*.xls" 
'indichiamo l'estensione dei file da cercare all'interno della cartella

'sotto: impieghiamo il metodo Execute (già spiegato nell'articolo "Cercare l'ultimo file" in questa 'stessa sezione) senza argomenti, in pratica dice: se l'esecuzione della ricerca file risulta maggiore 'di zero, quindi esiste un certo numero di file, allora:
If .Execute() > 0 Then
'sotto: si inizia il ciclo che parte dal primo file ordinato per SortByName e terminerà all'ultimo 'valore rappresentato dal totale dei file presenti contati con FoundFiles.Count.
For i = 1 To .FoundFiles.Count
X = .FoundFiles(i)  
'X sarà uguale al percorso completo del file in quel momento letto

'sotto: poichè vorremo salvare detto file in un'altra cartella, ed X rappresenta il percorso completo 'del file, quindi con unità e cartella, abbiamo bisogno di estrarre il solo nome del file da usare poi 'nel SaveAs, sfruttiamo il metodo GetFile che restituisce il nome del File corrispondente al file di 'un percorso specificato (X).
Dim f
Set fn = CreateObject("Scripting.FileSystemObject")
Set nome = fn.Getfile(X)
f = nome.Name
    'f sarà quindi solo il nome del file senza il path


'sotto: ora possiamo aprire il file usando Open Filename indicando il percorso completo (X)
Workbooks.Open Filename:=X
'sotto: si crea una copia salvando il file in quel momento aperto e quindi attivo, indicando il 'percorso completo formato dall'unità hard-disk, dalla cartella di destinazione, e dal nome 'rappresentato dalla variabile "f" che è completo di estensione
ActiveWorkbook.SaveAs Filename:="C:\Destinazione\" & f & "", _
FileFormat:=xlNormal, Password:="", WriteResPassword:="", _
ReadOnlyRecommended:=False, CreateBackup:=False


'sotto: attenzione: il file aperto è ancora il file attivo, si seleziona quindi il foglio dal quale vogliamo 'copiare dei dati (io ho usato come esempio il Foglio1)
Worksheets("Foglio1").Select
Range("A2:D20").Select
'si seleziona l'area dati (ognuno deciderà la propria area, magari usando 'altri sistemi tipo UsedRange o CurrentRegion, o ancora definire un'area con Cells + End, ecc. 'ecc.. Questo consentirebbe la libertà di prelevare dati in zone non necessariamente uguali tra un 'file di origine ed un altro, anche se il nome del foglio dovrà essere necessariamente uguale su tutti i 'files)

Selection.Copy
 'si copia la selezione così impostata


ActiveWorkbook.Close
'si chiude il file attivo che è quello che è stato appena aperto
'si attiva il foglio Archivio che era già aperto (visto che abbiamo lanciato da qui la macro)
Workbooks("Archivio.xls").Activate

'si seleziona in foglio nel quale vorremo incollare i dati che sono stati appena copiati e che sono 'ancora in memoria anche se si è chiuso il file sorgente
Worksheets("Foglio1").Select

'iniziamo un ciclo While..Wend per trovare la prima cella libera a partire dalla riga 2 e dalla 'colonna 1 (la A)
Dim iRow As Integer
iRow = 2
While Cells(iRow, 1).Value <> ""
iRow = iRow + 1
Wend

'trovata la riga libera incolliamo i dati che sono in memoria
Cells(iRow, 1).Select
ActiveSheet.Paste Destination:=Worksheets("Foglio1").Cells(iRow, 1)


Next
'si passa ad aprire il file successivo che si trova nella cartella Sorgente, fino all'ultimo
End If
End With
ActiveWorkbook.Save
'si salva la cartella Archivio che è il file ora attivo
'ActiveWorkbook.Close
'questo solo se vorremo chiudere ANCHE la cartella Archivio
End Sub

Sicuramente il vba ci consente di seguire altre strade nella compilazione di istruzioni per ottenere gli stessi risultati; questi comunque sono un sistema pratico e sufficientemente veloce, dipenderà comunque dal numero di file da aprire e dalla quantità di dati da copiare.

La routine sopra richiede che una volta eseguito il salvataggio e la copia dei dati che interessano, archiviandoli, si cancellino i file che sono stati già trattati in quanto un successivo lancio della macro, riaprirebbe li stessi file già aperti provocando due effetti indesiderati: una duplicazione dei dati archiviati, ed un errore di run-time perchè il metodo SaveAs si rifiuta di salvare un file con lo stesso nome di uno già esistente. Oppure i file già trattati possono essere spostati in un'altra cartella. (ma non esiste già una cartella con la copia degli originali ??). Sarà poi possibile usare delle variabili per reperire i nomi delle cartelle sia dei file da aprire, sia dove salvare.

Variante2: La routine sotto invece, simile alla precedente, ma con pochissime modifiche, ci consente di memorizzare il nome dell'ultimo file aperto(copiato, salvato), in modo che ad un successivo lancio della macro, vengano aperti, copiati, salvati, solo i file più recenti, anzi, meglio dire: i file successivi al nome dell'ultimo file trattato in precedenza. In questo caso non sarà necessario eliminare i file già trattati, dalla cartella "Sorgente", in quanto verranno aperti solo i successivi.

Unico accorgimento da tenere sarà quello di mantenere una progressione numerica nel nome dei file che terremo nella cartella "Sorgente", : esempio fatt001.xls, fatt002.xls, fatt003.xls ecc..

Per le modifiche useremo una cella del foglio di lavoro del file Archivio.xls (io ho usato la H1 come esempio), dove verrà memorizzato il nome dell'ultimo file aperto, inseriamo un controllo condizionale If : se il nome del file letto nella cartella sorgente sarà minore o uguale al nome registrato in H1, si passerà al successivo tramite Next, altrimenti verrà eseguita l'apertura del file, il suo salvataggio, la copia dei dati e l'incollaggio sul file Archivio. Vediamo la routine, in grassetto evidenzio le modifiche rispetto alla precedente:

Sub CreaArchivioSuNome()
Application.ScreenUpdating = False
'questo sopra evita il saltellamento a schermo durante l'apertura, il salvataggio e la copia

ind = [H1].Value 'con "ind" prendiamo il nome del file scritto nella cella H1 (non 'dimentichiamo che ci troviamo già sul foglio1 del file Archivio).
'sotto: impostiamo la variabile "fs" usando l'oggetto FileSearch (cerca file), che con la sua 'proprietà LookIn  imposta la cartella in cui deve essere eseguita la ricerca di file specificata con 'Filename
Set fs = Application.FileSearch
With fs
.LookIn = "C:\Sorgente"
 'indichiamo la cartella in cui cercare
.Filename = "*.xls" 
'indichiamo l'estensione dei file da cercare all'interno della cartella

'sotto: impieghiamo il metodo Execute (già spiegato nell'articolo "Cercare l'ultimo file" in questa 'stessa sezione) senza argomenti, in pratica dice: se l'esecuzione della ricerca file risulta maggiore 'di zero, quindi esiste un certo numero di file, allora:
If .Execute() > 0 Then
'sotto: si inizia il ciclo che parte dal primo file ordinato per SortByName e terminerà all'ultimo 'valore rappresentato dal totale dei file presenti contati con FoundFiles.Count.
For i = 1 To .FoundFiles.Count
X = .FoundFiles(i)  
'X sarà uguale al percorso completo del file in quel momento letto

'sotto: poichè vorremo salvare detto file in un'altra cartella, ed X rappresenta il percorso completo 'del file, quindi con unità e cartella, abbiamo bisogno di estrarre il solo nome del file da usare poi 'nel SaveAs, sfruttiamo il metodo GetFile che restituisce il nome del File corrispondente al file di 'un percorso specificato (X).
Dim f
Set fn = CreateObject("Scripting.FileSystemObject")
Set nome = fn.Getfile(X)
f = nome.Name
    'f sarà quindi solo il nome del file senza il path

If f <= ind Then GoTo 10 'se "f", cioè il nome letto nel momento dal ciclo, è minore o 'uguale al nome rappresentato da "ind", si passa alla riga numero 10, cioè Next, 'saltando le istruzioni sottostanti
'sotto: ora possiamo aprire il file usando Open Filename indicando il percorso completo (X)
Workbooks.Open Filename:=X
'sotto: si crea una copia salvando il file in quel momento aperto e quindi attivo, indicando il 'percorso completo formato dall'unità hard-disk, dalla cartella di destinazione, e dal nome 'rappresentato dalla variabile "f" che è completo di estensione
ActiveWorkbook.SaveAs Filename:="C:\Destinazione\" & f & "", _
FileFormat:=xlNormal, Password:="", WriteResPassword:="", _
ReadOnlyRecommended:=False, CreateBackup:=False


'sotto: attenzione: il file aperto è ancora il file attivo, si seleziona quindi il foglio dal quale vogliamo 'copiare dei dati (io ho usato come esempio il Foglio1)
Worksheets("Foglio1").Select
Range("A2:D20").Select
'si seleziona l'area dati (ognuno deciderà la propria area, magari usando 'altri sistemi tipo UsedRange o CurrentRegion, o ancora definire un'area con Cells + End, ecc. 'ecc.. Questo consentirebbe la libertà di prelevare dati in zone non necessariamente uguali tra un 'file di origine ed un altro, anche se il nome del foglio dovrà essere necessariamente uguale su tutti i 'files)

Selection.Copy
 'si copia la selezione così impostata


ActiveWorkbook.Close
'si chiude il file attivo che è quello che è stato appena aperto
'si attiva il foglio Archivio che era già aperto (visto che abbiamo lanciato da qui la macro)
Workbooks("Archivio.xls").Activate

'si seleziona in foglio nel quale vorremo incollare i dati che sono stati appena copiati e che sono 'ancora in memoria anche se si è chiuso il file sorgente
Worksheets("Foglio1").Select

[H1] = f  'si segna in H1 il nome dell'ultimo file aperto rappresentato da "f"

'iniziamo un ciclo While..Wend per trovare la prima cella libera a partire dalla riga 2 e dalla 'colonna 1 (la A)
Dim iRow As Integer
iRow = 2
While Cells(iRow, 1).Value <> ""
iRow = iRow + 1
Wend

'trovata la riga libera incolliamo i dati che sono in memoria
Cells(iRow, 1).Select
ActiveSheet.Paste Destination:=Worksheets("Foglio1").Cells(iRow, 1)

10:   'indice riga
Next
'si passa ad aprire il file successivo che si trova nella cartella Sorgente, fino all'ultimo
End If
End With
ActiveWorkbook.Save
'si salva la cartella Archivio che è il file ora attivo
'ActiveWorkbook.Close
'questo solo se vorremo chiudere ANCHE la cartella Archivio
End Sub

 

Variante 3: questa ultima procedura, che si basa sulla ricerca dei file in base alla loro data di creazione o di modifica, richiede delle istruzioni leggermente diverse dalla precedente: useremo ancora una cella sul Foglio1 del file Archivio (la H1) per memorizzare la data relativa all'ultimo file aperto, ma avremo bisogno di reperire la data di creazione o modifica del file, oltre al suo nome. Per reperire questa data useremo la Funzione FileDateTime , che restituisce un valore Variant (Date) che indica la data e l'ora in cui un file è stato creato o modificato.

Poichè esistono possibilità di errore nella gestione di data e ora (pensiamo ad esempio all'apertura indipendente di un file già precedentemente salvato e copiato, ma che si apri per consultazione e senza averci apportato modifiche lo si salvi comunque in uscita. Windows registra il nuovo orario come orario di modifica, e se lanciassimo la nostra macro verrebbe inevitabilmente caricato perchè la data sarebbe quella del giorno, ma l'orario lo renderebbe più recente rispetto alla data e ora registrate in H1, con i disagi già detti).

Per limitare queste possibilità di errori, useremo quindi la Funzione Mid che reperirà solo la data dal valore restituito dalla Funzione FileDateTime, e su quella eseguiremo il controllo con la sola data che avremo registrato in H1. Vediamo la procedura, in grassetto le varianti:

Sub ArchivioSuDataModificaDelFile()
Application.ScreenUpdating = False
'questo sopra evita il saltellamento a schermo durante l'apertura, il salvataggio e la copia

indu = Mid([H1], 1, 8) 'con "indu" tramite Mid prendiamo la sola data (dell'ultimo file 'aperto) scritta nella cella H1 (non dimentichiamo che ci troviamo già sul foglio1 del file 'Archivio). Ho considerato la data in formato breve es. 10/11/03 e non 10/11/2003. Questa impostazione del formato data dipende dalle impostazioni del formato data/ora del pannello di controllo, e NON dal formato cella.


'sotto: impostiamo la variabile "fs" usando l'oggetto FileSearch (cerca file), che con la sua 'proprietà LookIn  imposta la cartella in cui deve essere eseguita la ricerca di file specificata con 'Filename
Set fs = Application.FileSearch
With fs
.LookIn = "C:\Sorgente"
 'indichiamo la cartella in cui cercare
.Filename = "*.xls" 
'indichiamo l'estensione dei file da cercare all'interno della cartella

'sotto: impieghiamo il metodo Execute (già spiegato nell'articolo "Cercare l'ultimo file" in questa 'stessa sezione) senza argomenti, in pratica dice: se l'esecuzione della ricerca file risulta maggiore 'di zero, quindi esiste un certo numero di file, allora:
If .Execute() > 0 Then
'sotto: si inizia il ciclo che parte dal primo file ordinato per SortByName e terminerà all'ultimo 'valore rappresentato dal totale dei file presenti contati con FoundFiles.Count.
For i = 1 To .FoundFiles.Count
X = .FoundFiles(i)  
'X sarà uguale al percorso completo del file in quel momento letto

'sotto: con FileDateTime reperiamo la data e l'ora del file ora letto e reso con X, e 'l'assegniamo alla variabile "fd"

fd = FileDateTime(X)

'ora estraiamo la sola data da fd, tramite Mid, e l'assegniamo alla variabile "ff"
ff = Mid(fd, 1, 8)

'sotto: ora  confrontiamo "ff" con la data scritta in h1: se è inferiore o uguale passiamo 'al file successivo con GoTo che ci porta all'indice riga 10 e quindi a Next
If ff < indu Or ff = indu Then GoTo 10

'sotto: poichè vorremo salvare detto file in un'altra cartella, ed X rappresenta il percorso completo 'del file, quindi con unità e cartella, abbiamo bisogno di estrarre il solo nome del file da usare poi 'nel SaveAs, sfruttiamo il metodo GetFile che restituisce il nome del File corrispondente al file di 'un percorso specificato (X).
Dim f
Set fn = CreateObject("Scripting.FileSystemObject")
Set nome = fn.Getfile(X)
f = nome.Name
    'f sarà quindi solo il nome del file senza il path


'sotto: ora possiamo aprire il file usando Open Filename indicando il percorso completo (X)
Workbooks.Open Filename:=X
'sotto: si crea una copia salvando il file in quel momento aperto e quindi attivo, indicando il 'percorso completo formato dall'unità hard-disk, dalla cartella di destinazione, e dal nome 'rappresentato dalla variabile "f" che è completo di estensione
ActiveWorkbook.SaveAs Filename:="C:\Destinazione\" & f & "", _
FileFormat:=xlNormal, Password:="", WriteResPassword:="", _
ReadOnlyRecommended:=False, CreateBackup:=False


'sotto: attenzione: il file aperto è ancora il file attivo, si seleziona quindi il foglio dal quale vogliamo 'copiare dei dati (io ho usato come esempio il Foglio1)
Worksheets("Foglio1").Select
Range("A2:D20").Select
'si seleziona l'area dati (ognuno deciderà la propria area, magari usando 'altri sistemi tipo UsedRange o CurrentRegion, o ancora definire un'area con Cells + End, ecc. 'ecc.. Questo consentirebbe la libertà di prelevare dati in zone non necessariamente uguali tra un 'file di origine ed un altro, anche se il nome del foglio dovrà essere necessariamente uguale su tutti i 'files)

Selection.Copy
 'si copia la selezione così impostata


ActiveWorkbook.Close
'si chiude il file attivo che è quello che è stato appena aperto
'si attiva il foglio Archivio che era già aperto (visto che abbiamo lanciato da qui la macro)
Workbooks("Archivio.xls").Activate

'si seleziona in foglio nel quale vorremo incollare i dati che sono stati appena copiati e che sono 'ancora in memoria anche se si è chiuso il file sorgente
Worksheets("Foglio1").Select

[H1] = ff  'si registra in H1 la data dell'ultimo file aperto rappresentato da "ff"

'iniziamo un ciclo While..Wend per trovare la prima cella libera a partire dalla riga 2 e dalla 'colonna 1 (la A)
Dim iRow As Integer
iRow = 2
While Cells(iRow, 1).Value <> ""
iRow = iRow + 1
Wend

'trovata la riga libera incolliamo i dati che sono in memoria
Cells(iRow, 1).Select
ActiveSheet.Paste Destination:=Worksheets("Foglio1").Cells(iRow, 1)

10:   'indice riga
Next
'si passa ad aprire il file successivo che si trova nella cartella Sorgente, fino all'ultimo
End If
End With
ActiveWorkbook.Save
'si salva la cartella Archivio che è il file ora attivo
'ActiveWorkbook.Close
'questo solo se vorremo chiudere ANCHE la cartella Archivio
End Sub

Se togliete i commenti, vedrete che le istruzioni non sono poi molte, come preannunciato.

 

Buon lavoro.

prelevato sul sito www.ennius.altervista.org