VBA - Artigo 016 - Trabalhando com arquivos sequenciais

Progredindo em VBA no Microsoft Excel

Trabalhando com arquivos sequenciais

Há vezes que precisamos trabalhar com arquivos com formatos diferentes daqueles com que o Excel trabalha e nem sempre é um formato fácil de importar. Normalmente são usados arquivos sequenciais, ou seja, arquivos em que cada linha representa um registro e que são processados do início até o fim, em sequência.

Se o arquivo for recebido apenas uma vez, não há muito problema em fazer a importação de maneira manual. Porém, quando é um arquivo recebido rotineiramente, é melhor automatizar a importação do arquivo. E o que é melhor, pode-se automatizar também toda a formatação da tabela feita após a importação.

Por outro lado, pode haver a necessidade de gerar um arquivo em um formato específico. Neste caso, a criação manual do arquivo pode ser demasiadamente trabalhosa, por isso é melhor fazer um processo automático para não dar margem para erros na criação do arquivo, principalmente se houver muitas linhas e colunas para serem exportadas.
O código para ler ou gravar um arquivo não é difícil. O esqueleto básico para arquivos sequenciais é o seguinte: abrir arquivo, laço para processar, fechar arquivo. A complexidade está no processamento do registro, seja para ler ou para gravar o registro. Vejamos o código para ler um arquivo:

Sub ProcessarArquivo()

    Dim Arquivo As String
    Dim Registro As String

    Arquivo = "C:\Temp\Teste.txt"
   
    Open Arquivo For Input As #1
   
    Do Until EOF(1)
        ' Lê o próximo registro
        Line Input #1, Registro
        ' Chama a sub-rotina para processar o registro lido
        ProcessarRegistro (Registro)
    Loop

    Close #1
End Sub

Vamos entender esse código. No início ele cria uma variável de formato texto chamada Arquivo, à qual é associado um arquivo. Note bem que o caminho do arquivo está completo, com disco e pasta especificados, caso contrário o Excel pode não encontrar o arquivo e então gerará uma mensagem de erro.

É possível obter o arquivo por meio de uma caixa de diálogo, onde o usuário pode selecionar o arquivo. Neste caso deve-se alterar a linha para a seguinte forma:

Arquivo = Application.GetOpenFilename(FileFilter:="Arquivo de texto (*.txt), *.txt", _
    title:="Selecione um arquivo de texto para importar")

Use a forma que for mais adequada à necessidade. Se o arquivo vem sempre com o mesmo nome e caminho, a primeira forma é a melhor. Caso o nome e o caminho possam variar, a segunda forma ajuda a selecionar o arquivo.

Continuando o código, logo a seguir aparece um comando Open, que irá abrir o arquivo. Ele contém o parâmetro For Input, que indica que o arquivo será usado para leitura (outros parâmetros serão vistos mais adiante). A seguir, há outro parâmetro, As #1, que indica que o número de ordem do arquivo que está sendo aberto. Se houver outros arquivos a serem abertos ao mesmo tempo, deve-se incrementar o número: #2, #3 etc, sendo que o limite é 511. Como pode-se imaginar, trabalhar com mais de um arquivo ao mesmo tempo requer cuidado. Aqui vou limitar a um arquivo para facilitar o aprendizado.

Após o arquivo ser aberto, há uma estrutura de repetição Do Until... Loop, que testa uma condição EOF(1). EOF significa end of file, ou seja, fim do arquivo, e 1 é o número de ordem do arquivo. Traduzindo, o laço será processado até que não haja mais registros a serem lidos.

Dentro do laço, temos uma o comando Line Input #1, Registro, que irá preencher a variável Registro com o conteúdo do registro lido no arquivo. Há também uma chamada para uma sub-rotina chamada ProcessarRegistro, que irá processar o registro, onde estará toda a lógica e complexidade do que fazer com o registro lido.

Por fim, temos o comando Close #1, que irá fechar o arquivo. Lembre-se sempre de fechar os arquivos após processar, caso contrário eles podem ficar presos ao Excel até que este seja fechado, impedindo outros programas ou outras pessoas de usarem o arquivo.
Agora vejamos o código para fechar o arquivo:

Sub ProcessarArquivo()

    Dim Arquivo As String
    Dim Registro As String
    Dim Linha As Integer
    Dim UltimaLinha As Integer

    Arquivo = "C:\Temp\Teste.txt"
   
    Open Arquivo For Output As #1
   
    UltimaLinha = Cells(1, 1).End(xlDown).Row

    For Linha = 2 To UltimaLinha
        ' Chama a sub-rotina para gerar registro para gravação
        Registro = GerarRegistro(Linha)
        ' Grava o registro no arquivo
        Print #1, Registro
    Next

    Close #1

End Sub

Aqui temos três diferenças em relação ao código de abrir arquivo. A primeira é que o comando Open agora está com o parâmetro For Output, o que significa que o arquivo será usado para gravação. A segunda diferença é o laço de repetição, que agora se refere à planilha, que é de onde sairão os dados para serem gravados no arquivo. Aqui usei um laço For... Next, mas poderia ser qualquer outra estrutura de repetição. E a terceira é o comando Print #1, Registro, que irá gravar o conteúdo da variável Registro no arquivo 1. Há o comando Write, que é usado da mesma forma, porém este grava strings entre aspas.

Observe nos códigos uma diferença importante nos laços: na leitura, primeiro lê o registro e depois processa; na gravação, primeiro gera o registro e depois grava. É algo bem lógico, mas às vezes uma desatenção nesse detalhe pode causar a perda de registros.

O conhecimento até agora permite ler e gravar arquivos para muitos casos. Daqui em diante, vamos aprofundar o conhecimento no assunto, estudando alguns detalhes mais a fundo.

O comando Open tem a seguinte sintaxe (os parâmetros opcionais estão entre colchetes):

Open NomeDoArquivo For Modo [Access Acesso] [Trava] As Número [Len=Comprimento]

Vimos os modos Input e Output acima. Tem ainda os seguintes modos:
- Append: Adiciona registros a um arquivo existente;
- Binary: Usado para ler e gravar arquivos em formato binário;
- Random: Para arquivos com acesso aleatório (não sequencial). É a opção padrão.
Os modos Random e Binary não serão abordados neste artigo devido à complexidade. Eles terão um artigo no futuro.

O parâmetro opcional Access pode ter os seguintes valores:
- Read: Permite somente leitura;
- Write: Permite somente escrita;
- Read Write: Permite tanto leitura quanto escrita.

Perceba que não há necessidade deste parâmetro quando se usa os modos Input, Output e Append, somente nos casos de modos Binary e Random.

Trava define se o arquivo pode ser aberto por outro processo enquanto estiver aberto no Excel. Os parâmetros possíveis são:
- Shared: Compartilhado, permite leitura e escrita;
- Lock Read: Não permite leitura, só escrita;
- Lock Write: Não permite escrita, só leitura;
- Lock Read Write: Não permite leitura nem escrita.

Por fim, Len define o comprimento do registro. Não há necessidade de especificar e é mais vantajoso controlar o comprimento do registro na própria criação, definindo com campos de tamanhos fixos.

Agora vejamos a diferença entre os comandos Print e Write com um código simples:

Sub GravarTexto()

    Dim Arquivo As String

    Arquivo = "C:\Temp\Texto Gravado.txt"
       
    Open Arquivo For Output As #1

    Print #1, "Olá, mundo!"
    Write #1, "Olá, mundo!"

    Close #1

End Sub

O resultado do arquivo gravado é o seguinte:

Olá, mundo!
"Olá, mundo!"

Desta forma é mais fácil visualizar a diferença entre os dois comandos. Enquanto Print apenas gravou o conteúdo do texto, Write deixou-o entre aspas.

É possível adicionar mais argumentos em ambos comandos, separando por vírgulas. Desta forma podemos gravar vários campos ou variáveis em uma linha sem precisar compor um campo com o registro inteiro. Porém, há diferenças na forma de gravar as linhas. Altere as linhas dos comandos Print e Write da seguinte forma:

    Print #1, "Olá, mundo!", 123
    Write #1, "Olá, mundo!", 123

Acrescentamos um valor numérico, que será gravado ao lado da string. Observe como fica a gravação:

Olá, mundo!    123
"Olá, mundo!",123

O comando Print separa campos distintos com uma tabulação, enquanto o Write separa com vírgulas. Perceba que o valor numérico não recebeu aspas. Se fosse outro campo do tipo string, o texto seria envolvido por aspas.

Repare também que a string gravada contém uma vírgula. Ela não vai interferir no registro, pois no registro gravado com Print não há separação por vírgulas, enquanto no registro gravado com Write ela está entre aspas. É importante conhecer esses detalhes para saber decidir como os dados serão gravados, porque a forma de ler depende diretamente dessa escolha.

Para a leitura dos dados, pode-se ler o registro inteiro e depois usar um comando como o Instr para localizar os separadores, seja a tabulação ou a vírgula, e então ir compondo as células com os valores. Há uma alternativa mais prática que usa o comando Split, que gera um array com os dados do registro. Observe o código abaixo:

Sub LerTexto()

    Dim Arquivo As String
    Dim Registro As String
    Dim Linha As Integer
    Dim Conteudo As Variant
    Dim Contador As Integer

    Arquivo = "C:\Temp\Texto Gravado.txt"

    Linha = 1

    Open Arquivo For Input As #1

    Do Until EOF(1)
        Line Input #1, Registro
        Conteudo = Split(Registro, vbTab)
        For Contador = LBound(Conteudo) To UBound(Conteudo)
            Cells(Linha, Contador + 1).Value = Conteudo(Contador)
        Next
        Linha = Linha + 1
    Loop
   
    Close #1

End Sub

Analisando o código: o comando Line Input irá ler o registro e gravá-lo na variável Registro, como visto anteriormente. A linha seguinte compõe a variável Conteudo como um array, separando os itens pelo separador. O caractere da tabulação é identificado pela constante do VBA vbTab. Por fim, há um laço For... Next que irá preencher as células da planilha ativa com os valores do array Conteudo. Atente para o fato de que um array inicializa sempre em 0, por isso é preciso acrescentar 1 para gravar na primeira coluna.

Experimente criar um arquivo com várias linhas e vários valores em cada linha, separados por tabulações e então teste o código. Depois experimente fazer o mesmo, separando por vírgulas, colocando strings entre aspas, alterando o vbTab no código para "," (a fim de indicar que é para separar pelas vírgulas) e teste novamente. Se você manteve a string "Olá, mundo!" do exemplo anterior, verá que na separação por vírgulas uma célula ficou com "Olá e a outra ficou com mundo!". Outro problema é que as strings permaneceram com as aspas nas células. São problemas que demandarão um código adicional para verificar essas eventualidades e corrigi-las todas.

Particularmente prefiro criar uma variável para compor o registro, mantendo todos os campos de tamanho fixo, para então gravar o registro. Em uma das vídeo-aulas do Alessandro Trovato foi demonstrado uma forma de como fazer isso, reveja se for necessário.

Por fim, algumas dicas para quando estiver exportando dados para arquivos:
- Números com casas decimais devem ser exportados de preferência sem a vírgula. Se for valor de moeda (duas casas decimais), multiplique por 100 antes de exportar e divida por 100 quando for importar. Caso sejam valores com mais casas decimais, defina o multiplicador de acordo com  o valor da maior casa decimal para então exportar todos com o mesmo valor. Isso é para ajudar a manter os números com a mesma quantidade de dígitos e assim facilitar a exportação e posterior importação dos valores;

- Transforme as datas para o formato AAAA/MM/DD antes de exportar, porque o Excel tem um problema na importação de datas. Ele considera como MM/DD/AAAA a não ser que o MM seja maior que 12, neste caso ele importa como DD/MM/AAAA. Imagine uma situação com muitas datas e o Excel bagunçando tudo. Veja o resultado dos testes nas imagens abaixo:

Datas exportadas como DD/MM/AAAA: As datas de 01/02/2001 a 12/02/2001 foram importadas de maneira errada pelo Excel, enquanto o restante foi importado corretamente.

Datas exportadas como AAAA/MM/DD: Todas as datas foram importadas corretamente.

Espero que este artigo lhe ajude a trabalhar com arquivos de forma satisfatória e sem problemas. Quaisquer dúvidas, use a seção de comentários abaixo.

Pedro Martins



Formado em Tecnologia em Eletrônica Digital, já trabalhou como artefinalista, eletrotécnico, programador de CLP (para máquinas industriais) e analista de sistemas em sistema bancário, programando em COBOL.
Mexe com computadores e programação desde a segunda metade dos anos 1980, quando teve um MSX e aprendeu a programar em BASIC. É a favor da disseminação do conhecimento.





Catálogo de aulas (NOVIDADE)

Criei um catálogo de aulas para ajudar você em seus estudos. Acesse clicando na imagem abaixo ou clique aqui.


Postagem Anterior Próxima Postagem