Acquista i nostri libri consigliati su Amazon.it
+ Rispondi al messaggio
Visualizzazione dei risultati da 1 a 10 su 10

[VBA]Copiare da un file di testo solo alcune righe in un nuovo file di testo

  1. #1
    Una premessa: ho scelto la sezione Access solo perché faccio tutto da Access in una maschera ma sotto non uso il db. Qui si tratta solo di file di testo.
    Il file è testo puro, senza caratteri strani o altro. Si tratta dei classici file contenenti informazioni in sequenza. Il contenuto delle varie righe dipende ai primi due caratteri, che identificano il tipo record. (Da qui in avanti userò righe e record come sinonimi)
    Ci sono sempre: A1 (record di testa) e Z1 (record di coda, con informazioni riepilogative) e tra questi varie righe (G1, G2, G3, G4, G5 e G9). I file non sono di dimensioni elevate, solo nei periodi di massimo lavoro si superano i 100 KB.
    Partendo da un file che mi viene fornito da un sistema automatizzato devo crearne uno che contenga solo alcune righe, identificate mediante una lettera precisa che si trova in una posizione diversa in base al tipo record G. A1 e Z1 devono sempre essere presenti una volta che il file esiste, cioè se ci sono righe che soddisfano il requisito. Può accadere che non ce ne siano e in quel caso... niente, non si crea alcun file (non si deve creare un file per dire che non c'è niente, per capirci).
    La cosa non è difficile, per niente. Quello che ho fatto funziona e sotto molti aspetti "mi soddisfa". Chiedo aiuto al forum per trovare migliorie e/o confermare la mia impostazione del lavoro. Queste cose non si trovano scritte sui manuali!
    Const cFileDialogViewDetails = 2
    Const cFileDialogFilePicker = 3

    Dim astrFile() As String
    Dim intFileNr As Integer, intFileNuovo As Integer
    Dim lngG1 As Long, lngG2 As Long, lngG3 As Long, lngG4 As Long, lngG5 As Long, lngG9 As Long

    Private Sub LeggiFile()
    Dim strSelectedFile As String, strNuovoFile As String
    Dim i As Integer
    Dim blnTrovato As Boolean

    intFileNr = FreeFile()

    strSelectedFile = Me!txtFileToImport.Value

    Open strSelectedFile For Input As #intFileNr

    astrFile = Split(Input(LOF(intFileNr), #intFileNr), vbCrLf)

    blnTrovato = False
    i = 0
    'quando va oltre l'ultimo l'errore è il 9, lo "intrappolo" e sono a posto
    On Error GoTo GestioneErrori
    Do Until blnTrovato
    i = i + 1
    blnTrovato = TrovaCarattere(astrFile(i))
    Loop

    On Error GoTo 0

    ' significa che ho trovato qualcosa di buono, creo il file
    strNuovoFile = Replace(Mid$(GetFilenameFromPath(strSelectedFile), 5), ".", "_") & ".txt"
    strNuovoFile = GetPathOnly(strSelectedFile) & "\" & strNuovoFile
    'Debug.Print strNuovoFile
    intFileNuovo = FreeFile()
    Open strNuovoFile For Output As #intFileNuovo
    Print #intFileNuovo, astrFile(0) 'scrivo il tiporecord A1
    Print #intFileNuovo, astrFile(i) 'significa che quella riga mi interessa
    i = i + 1
    For i = i To (UBound(astrFile) - 2) 'l'ultimo è il fine file, il penultimo è il tipo record Z1 che devo costruire
    If TrovaCarattere(astrFile(i)) Then
    Print #intFileNuovo, astrFile(i)
    End If
    Next

    'adesso c'è tutta la trafila del tiporecord Z1
    Print #intFileNuovo, CreaZ1(astrFile(i))

    Me.txtRigaCorrente.Value = "Fatto"

    Close #intFileNuovo

    ExitSub:
    lngG1 = 0: lngG2 = 0: lngG3 = 0: lngG4 = 0: lngG5 = 0: lngG9 = 0
    Close #intFileNr
    Exit Sub

    GestioneErrori:
    Select Case Err.Number
    Case 9
    'è andato oltre l'ultimo elemento dell'array cioè ha finito il file senza trovare niente da scrivere
    Me!txtRigaCorrente.Value = "Nessun record trovato"
    Case Else
    MsgBox Err.Number & "= " & Err.Description
    End Select
    GoTo ExitSub

    End Sub


    Private Function TrovaCarattere(ByVal strRiga As String) As Boolean
    Dim blnRisultato As Boolean
    Dim intStart As Integer

    Select Case Left$(strRiga, 2)
    Case "G1"
    lngG1 = lngG1 + Abs(LetteraGiusta(strRiga, 260, blnRisultato))
    Case "G2"
    lngG2 = lngG2 + Abs(LetteraGiusta(strRiga, 61, blnRisultato))
    Case "G3"
    lngG3 = lngG3 + Abs(LetteraGiusta(strRiga, 79, blnRisultato))
    Case "G4"
    lngG4 = lngG4 + Abs(LetteraGiusta(strRiga, 61, blnRisultato))
    Case "G5"
    lngG5 = lngG5 + Abs(LetteraGiusta(strRiga, 98, blnRisultato))
    Case "G9"
    lngG9 = lngG9 + Abs(LetteraGiusta(strRiga, 145, blnRisultato))
    Case "Z1", "" 'il tipo record Z1 o il carattere di fine file
    blnRisultato = False
    Case Else
    MsgBox "Che razza di tiporecord c'è in questo file?"
    End Select

    TrovaCarattere = blnRisultato

    End Function


    Private Function LetteraGiusta(ByVal strRiga, ByVal intPosiz, ByRef blnDaScrivere) As Boolean
    'occhio al ByRef di blnDaScrivere, così ho due valori restituiti, in pratica
    LetteraGiusta = False
    blnDaScrivere = False

    Select Case Mid$(strRiga, intPosiz, 1)
    Case "I", "T", "S", "R", "A", "U", "M"
    'non c'è niente da fare, è già tutto False
    Case "O", "C"
    LetteraGiusta = True
    blnDaScrivere = True
    Case Else
    MsgBox "Hanno cambiato il tracciato record: modifica il codice, subito!"
    End Select

    End Function


    Private Function CreaZ1(ByVal strZ1originale As String) As String
    Dim strZ1 As String

    'Record Z1, prende un pezzo dell'originale, fino al carattere 79, poi bisogna aggiungere
    'G1 da 80, 7 caratteri
    'G2 da 87, 7 caratteri
    'G3 da 94, 7 caratteri
    'G4 da 101, 7 caratteri
    'G5 da 108, 7 caratteri
    'G9 da 115, 7 caratteri
    'totale record compresi A1 e Z1 da 122, 7 caratteri
    '172 spazi dalla posizione 129

    strZ1 = strZ1 & Format(lngG1, "0000000")
    strZ1 = strZ1 & Format(lngG2, "0000000")
    strZ1 = strZ1 & Format(lngG3, "0000000")
    strZ1 = strZ1 & Format(lngG4, "0000000")
    strZ1 = strZ1 & Format(lngG5, "0000000")
    strZ1 = strZ1 & Format(lngG9, "0000000")
    strZ1 = strZ1 & Format(lngG1 + lngG2 + lngG3 + lngG4 + lngG5 + lngG9 + 2, "0000000") 'aggiungo 2 perché ci sono i tipi record A1 e Z1 che non sono nel calcolo
    strZ1 = Left$(strZ1originale, 79) & strZ1 & Space(172)
    'Debug.Print strZ1
    CreaZ1 = strZ1

    End Function


    Private Sub cmdReadFile_Click()
    Me!txtRigaCorrente.Value = vbNullString
    Call LeggiFile
    End Sub


    Private Sub cmdFileSelect_Click()
    Dim fso As Object
    ...
    Me!cmdReadFile.Enabled = (Len(Me!txtFileToImport.Value & vbNullString) <> 0)
    Me!cmdReadFile.SetFocus
    End Sub

    Private Sub txtFileToImport_AfterUpdate()
    Me!cmdReadFile.Enabled = (Len(Me!txtFileToImport.Value & vbNullString) <> 0)
    End Sub

    Ho usato i tag code che mettono anche il numero della riga così da avere un riferimento più facile alle varie porzioni del codice.
    Ho caricato il file in un array. Ogni elemento dell'array contiene un record (ad eccezione dell'ultimo). L'elemento con indice 0 è sempre il tiporecord A1, l'ultimo elemento è il carattere di fine file, il penultimo il tiporecord Z1. Se il file deve essere creato, il tiporecord A1 lo posso prendere così com'è, mentre il tiporecord Z1 lo devo costruire in quanto contiene informazioni riepilogative: quanti record di ogni tipo ci sono e il numero totale di record compresi i record A1 e Z1 (più il classico filler)

    Spiegazione delle righe da 21 a 30: ho voluto scrivere il tutto in modo che non si creasse il file se non dopo aver trovato una riga utile, per non dover poi, alla fine, dover cancellare un file che non deve esistere. Allo stesso tempo non ho voluto perdere la posizione fino a quel momento raggiunta nell'array, in modo da proseguire solo con gli elementi (righe) successive.

    Per evitare che il post diventi lunghissimo e difficile da leggere non aggiungo altro. Per il dettaglio che sta alla base di ogni "ragionamento", chiedere (per il forum) è lecito, rispondere è compito mio.
    (ogni tanto sfoltisco il post da elementi inutili o correggo errori anche di digitazione, niente di sostanziale, altrimenti lo evidenzio)
    Ultima modifica di Phil_cattivocarattere; 12-02-2018 15:08 

  2. #2
    L'avatar di @Alex
    @Alex non è in linea Moderatore Globale
    Parliamo di cose che possono essere viste in modo diverso.... in quanto sulla logica solo tu hai la sequenza in mano.
    Righe 25÷28
    Do Until TrovaCarattere(astrFile(i))
        i = i + 1
        DoEvents
    Loop
    
    Per il resto la funzione TrovaCarattere() viene chiamata 2 volte e forse si può ottimizzare....? Mi pare poco sensato i linea di principio.
    La prima intanto che lo cicli riscrivo un array già controllato così poi lo passi senza ricontrollare alla fine...
    Se poi ci sono molte righe e viene richiamata molt e volte può essere ottimizzata con una semplice sistema in stringa o una collection...
    ℹ️ Leggi di più su @Alex ...

  3. #3
    L'avatar di gibra
    gibra non è in linea Very Important Person
    Senza 'vedere' alcun file non è semplice valutare se e dove ottimizzare.

    1)
    Quello che posso dirti è che per aprire il file usando Input devi essere certo al 100% che il file non contenga caratteri strani, in caso contrario può generare un errore di runtime 62 - Input past end ( Input dopo la fine del file).
    Io preferisco usare sempre Binary, con cui evito questo inconveniente.

    Quello che invece trovo strano è voler intercettare l'errore 9 perché non ha molto senso.
    Dal momento che usi Split() per creare l'array non c'è logica nel pensare che la funzione split generi un array che possa presentare quest'errore. Se accade allora qualcosa non quadra.

    Caso mai, potrebbero esserci delle righe vuote, ma in questo caso basta un controllo sull'elemendo corrente dell'array:
    If astrFile(i) > vbNullString Then
        ' eseguo
    Else
        ' non faccio nulla, o passo al prossimo elemento
    End If
    
    2)
    Vedo che usi la routine TrovaCarattere 2 volte, come mai?


    3)
    la Split genera un'array il cui indice inizia da 0, mentre tu inizi 1.
    Immagino tu lo sappia già, quindi do per scontato che la scelta sia intenzionale.
    Ma, come ti dicevo prima, non potendo vedere i file di testo da elaborare, non posso conoscerne il motivo.

    ℹ️ Leggi di più su gibra ...

  4. #4
    Quote Originariamente inviato da @Alex Visualizza il messaggio
    Parliamo di cose che possono essere viste in modo diverso.... in quanto sulla logica solo tu hai la sequenza in mano.
    Righe 25÷28
    Do Until TrovaCarattere(astrFile(i))
        i = i + 1
        DoEvents
    Loop
    
    Vediamo se ho capito questa parte: inutile assegnare ad una variabile il risultato di una funzione (che restituisce true o false) ed usarla per verificare se ripetere il ciclo o meno, quando posso usare la funzione stessa. Il DoEvents serve per evitare che il codice mi "scappi" al Loop prima che i sia stato aumentato di 1. Dico bene?
    Quote Originariamente inviato da @Alex Visualizza il messaggio
    Per il resto la funzione TrovaCarattere() viene chiamata 2 volte e forse si può ottimizzare....? Mi pare poco sensato i linea di principio.
    La prima intanto che lo cicli riscrivo un array già controllato così poi lo passi senza ricontrollare alla fine...
    Se poi ci sono molte righe e viene richiamata molt e volte può essere ottimizzata con una semplice sistema in stringa o una collection...
    La funzione legge una riga alla volta, quindi un elemento solo dell'array per volta e viene chiamata due volte ma non per gli stessi elementi dell'array, anzi! All'interno del ciclo di cui alle righe 25÷28 serve per trovare la prima occorrenza di un record utile. Se nessun record soddisfa la richerca salta direttamente alla fine della Sub, o meglio, grazie al fatto che va oltre all'upperbound dell'array gestisco l'errore e chiudo la procedura senza creare niente.
    Se invece trova un record "buono" significa che il file è da creare, esce dal Do Loop e prosegue: apro il file, scrivo il tiporecord A1, scrivo il record appena trovato (con indice i) e proseguo ad analizzare gli elementi che seguono dell'array.
    L'array viene "passato" una volta sola, in ogni caso.
    Quote Originariamente inviato da gibra Visualizza il messaggio
    Senza 'vedere' alcun file non è semplice valutare se e dove ottimizzare.
    Lo so, ma è pieno di codici fiscali, ABI e CAB, crearne uno di pulito è un'impresa.
    Quote Originariamente inviato da gibra Visualizza il messaggio
    1) Quello che posso dirti è che per aprire il file usando Input devi essere certo al 100% che il file non contenga caratteri strani, in caso contrario può generare un errore di runtime 62 - Input past end ( Input dopo la fine del file).
    Io preferisco usare sempre Binary, con cui evito questo inconveniente.
    Ecco risolto, senza nemmeno chiedere, il problema che mi si è presentato andando a pescare un vecchio file che non sarebbe mai stato oggetto di questa operazione ma solo perché era "massiccio" volevo testarlo. Alla presenza di null avevo proprio quell'errore ma era troppo presto per chiedere aiuto. Su questo provvedo quanto prima.
    Quote Originariamente inviato da gibra Visualizza il messaggio
    Quello che invece trovo strano è voler intercettare l'errore 9 perché non ha molto senso.
    Dal momento che usi Split() per creare l'array non c'è logica nel pensare che la funzione split generi un array che possa presentare quest'errore. Se accade allora qualcosa non quadra.
    La spiegazione è dovuta al "meccanismo" da cui sono partito, cioè quello di lasciare al ciclo Do Loop la ricerca del primo elemento che rispondesse ai requisiti, con la funzione TrovaCarattere che dice se questa ricerca è stata positiva o meno. In caso negativo arrivo ad un punto in cui cercherà oltre il limite superiore dell'array. Questo significa che non ha trovato alcun record utile. Adesso che ci penso però può essere scritta meglio e in modo più "lineare", con un semplice Exit For. Da confrontare poi con la proposta di @Alex.
    Quote Originariamente inviato da gibra Visualizza il messaggio
    Caso mai, potrebbero esserci delle righe vuote, ma in questo caso basta un controllo sull'elemendo corrente dell'array:
    No, non possono esistere righe vuote (se non per un errore del sistema che ha generato il file). In ogni caso è presente una cosa "del genere" nel Case Select, quando prevedo che se il tipo record è Z1 o un "record vuoto" (ma che nel mio caso può essere solo l'ultimo elemento dell'array che contiene il carattere di fine file) non deve fare nulla, cioè non scrivere quella riga.
    Quote Originariamente inviato da gibra Visualizza il messaggio
    2)Vedo che usi la routine TrovaCarattere 2 volte, come mai?
    Vedi se la risposta data in questo stesso post ad @Alex è sufficiente.
    Quote Originariamente inviato da gibra Visualizza il messaggio
    3)la Split genera un'array il cui indice inizia da 0, mentre tu inizi 1.
    Immagino tu lo sappia già, quindi do per scontato che la scelta sia intenzionale.
    Sì, la scelta è intenzionale: l'elemento 0 è sempre il record di testa A1 che non deve essere analizzato dalla funzione TrovaCarattere.
    Ultima modifica di Phil_cattivocarattere; 12-02-2018 15:45 

  5. #5
    L'avatar di @Alex
    @Alex non è in linea Moderatore Globale
    Sicché la cicli 2 volte... ora se i dati sono i medesimi puoi trovare tutte le giustificazioni che vuoi.... ma 2 volte soni troppe
    Se trovi l'occorrenza utile che fai riparto da zero...?

    Fai uso buono della lettura in modalità Binary suggerita da Gibra che è l'unico modo per evitare rogne oltre ad essere piu veloce.
    ℹ️ Leggi di più su @Alex ...

  6. #6
    L'avatar di gibra
    gibra non è in linea Very Important Person
    Quote Originariamente inviato da Phil_cattivocarattere Visualizza il messaggio
    Lo so, ma è pieno di codici fiscali, ABI e CAB, crearne uno di pulito è un'impresa.
    Ma scherzi?
    1) Gli ABI ed i CAB non sono un segreto per nessuno.
    2) per gli altri dati come nomi, cognomi, Codici Fiscali, CC, IBAN, ecc. ti basta fare qualche Replace() con lettere astruse come XYZJWK...

    ℹ️ Leggi di più su gibra ...

  7. #7
    Quote Originariamente inviato da @Alex Visualizza il messaggio
    Sicché la cicli 2 volte... ora se i dati sono i medesimi puoi trovare tutte le giustificazioni che vuoi.... ma 2 volte soni troppe
    Se trovi l'occorrenza utile che fai riparto da zero...?
    Noooo, non ciclo l'array due volte. L'ho scritto dappertutto. Anzi ho proprio studiato quel meccanismo un po' contorto proprio perché volevo evitarlo. Codice vecchia versione (ma non cambierebbe nulla sotto questo aspetto con la modifica che mi hai suggerito)
    Do Until blnTrovato
        i = i + 1
        blnTrovato = TrovaCarattere(astrFile(i))
    Loop
    
    Parto dall'elemento 1, perché lo 0 so che non sarà mai true. Ipotizziamo che il primo "record buono" abbia indice 10. In quel momento esce dal Loop e fa
    Print #intFileNuovo, astrFile(0) 'scrivo il tiporecord A1
    Print #intFileNuovo, astrFile(i) 'che è il record con indice 10
    
    poi prosegue nell'analisi degli altri elementi dell'array, quindi riprende dall'elemento con indice 11 ed ogni riga deve essere valutata con la stessa funzione di prima per sapere se deve essere scritta o meno.
    i = i + 1 'nell caso dell'esempio parte da 11
    For i = i To (UBound(astrFile) - 2) 'l'ultimo è il fine file, il penultimo è il tipo record Z1 che devo costruire
        If TrovaCarattere(astrFile(i)) Then
            Print #intFileNuovo, astrFile(i)
        End If
    Next
    
    Quote Originariamente inviato da gibra Visualizza il messaggio
    2) per gli altri dati come nomi, cognomi, Codici Fiscali, CC, IBAN, ecc. ti basta fare qualche Replace() con lettere astruse come XYZJWK...
    Sì, mi sono fasciato la testa prima di farmi male e senza che poi ci sia da farsi male. Sonno permettendo preparo il file questa sera.

  8. #8
    Quote Originariamente inviato da Phil_cattivocarattere Visualizza il messaggio
    Sonno permettendo preparo il file questa sera.
    Il sonno ha prevalso ma sono stato mattiniero ed c'è qualcosa di pronto su cui lavorare
    Questi file sono tipo quelli ufficiali che ricevo. Ho sostituito la serie di informazioni non significative per il test contenute nelle varie righe con diciture a mia scelta, trattini e/o spazi (filler). Ogni riga è di 300 caratteri, ad eccezione dell'ultima che tra l'altro non è mai considerata (lo chiamo carattere di fine file ma viene apposto automaticamente in fase di creazione del file ed esiste anche in quello originale). Ho lasciato inalterati i primi due caratteri, quelli che identificano il tipo record, e la lettera (situata in posizioni diverse a seconda del tipo record) che discrimina se quella riga deve poi essere riportata nel nuovo file da costruire. L'ordine con cui si presentano le righe è lo stesso dei file originali, quindi è normale che ci siano molti G1, seguiti da G2 e che poi riprendano ad esserci G1. Questo per dire che i file dimostrativi sono quanto più simili a quelli reali possibili.
    File nr. 1 Test_M999_D2017361_P01_T00_RUN.txt = non contiene alcuna riga che mi interessa. Non verrà creato il file.
    File nr. 2 Test_M999_D2018027_P01_T00_RUN.txt = contiene alcune righe che mi interessano. Dovrà creare il file.
    File nr. 3 Test_M999_D2018034_P01_T00_RUN.txt = per puro caso contiene solo righe che mi interessano. Dovrà creare un file identico a quello originale (il nome può anche cambiare). Dal momento che so solo dopo aver passato tutte le righe che mi interessano tutte, non vale la pena fare una copia del file, visto che l'ho già creato (per la cronaca, con apposite funzioni di confronto mi serve per verificare la bontà del mio sistema di creazione di un file da zero).
    File nr. 4 Test_M999_D2018041_P01_T00_RUN.txt = contiene alcune righe che mi interessano. Dovrà creare il file. E' la stessa situazione del file nr. 2, la più frequente, l'ho messo solo per avere più di una fornitura da analizzare.
    Allegato 2451
    Li ho inseriti in un file compresso perché 2 superavano la soglia massima prevista per i file di testo e non valeva la pena comprimere solo quei 2.
    Ultima modifica di AntonioG; 13-02-2018 13:07 

  9. #9
    L'avatar di gibra
    gibra non è in linea Very Important Person
    Leggendo tutti i file mi risponde sempre:

    Hanno cambiato il tracciato record: modifica il codice, subito!

    ℹ️ Leggi di più su gibra ...

  10. #10
    Quote Originariamente inviato da gibra Visualizza il messaggio
    Leggendo tutti i file mi risponde sempre:
    Hanno cambiato il tracciato record: modifica il codice, subito!
    Il vero messaggio d'errore doveva essere: "Cambiare quello che ha costruito i file di test".
    Errore mio nella creazione del file e nella fretta ho dimenticato di provarli! Che figura! Rimedio subito.
    Ecco fatto Tutti_i_file.zip.
    Ultima modifica di Phil_cattivocarattere; 13-02-2018 12:39 

+ Rispondi al messaggio

Potrebbero interessarti anche ...

  1. Copiare righe non presenti da file di testo a tabella Excel
    Da simomonti nel forum Visual Basic .Net
    Risposte: 12
    Ultimo Post: 31-03-2016, 21:08
  2. Articolo: Estrarre e copiare righe in nuovo file excel
    Da kingstone74 nel forum Microsoft Excel
    Risposte: 19
    Ultimo Post: 30-10-2014, 09:26
  3. copiare su nuovo foglio solo alcune righe
    Da pietroexcel nel forum Microsoft Excel
    Risposte: 10
    Ultimo Post: 20-06-2013, 14:44
  4. Risposte: 3
    Ultimo Post: 07-11-2011, 13:28
  5. Risposte: 3
    Ultimo Post: 29-07-2008, 10:38