VB .NET - Formatando arquivos textos


No artigo de hoje eu vou tratar de um assunto recorrente: a formatação de arquivos textos.

Não é raro o desenvolvedor ter que realizar tarefas de formatação de arquivos textos recebidos de legados quer gerando ou recebendo tais arquivos e tendo que realizar sua formatação.

Neste artigo veremos como é simples realizar o tratamento de strings usando os recursos do VB .NET. Para quem é o do tempo do Clipper, nada comparado ao trabalho que o programador Clipper tinha que fazer para realizar as mesmas tarefas. (Fiz um curso de Clipper na Nantucket)

Clipper (ou CA-Clipper) é um compilador 16 bits da linguagem xBase para o ambiente DOS. Foi criada em 1984 com o propósito de ser um compilador para o Ashton-Tate dBase, um gerenciador de banco de dados muito popular em sua época.

Para ilustrar vamos supor que você trabalha em uma empresa e como programador VB .NET recebeu a seguinte tarefa:

1- Você irá receber um arquivo texto no seguinte formato:

9800009038,1
9857007038,1
9907010027,1
9567012027,1
9568009038,1
.....

Este arquivo é recebido de um legado e representa a relação de produtos recebidos por uma filial da empresa.

2- A informação contida no arquivo é a seguinte:

Cada linha de texto possui duas informações separadas por uma vírgula sendo que a primeira parte representa o código e a cor do produto e a segunda parte a quantidade do produto.

Na primeira parte da linha temos um número que se apresenta composto de duas informações :

Dessa forma, para clarear o entendimento, temos:

linha 1 - 9800009038,1

código do produto = 9800
Cor do produto (sempre 6 dígitos) = 009038
quantidade = 1

Pois bem, o seu trabalho será receber o tal arquivo e tratá-lo realizando a seguinte formatação:

1- O código do produto deve ser extraído e alinhado a esquerda;
2- A cor do produto deve ser alinhada 20 posições a partir do código do produto;
3- A quantidade do produto deve ser alinhada 15 posições a partir da cor do produto;

A seguir temos uma ilustração de como o serviço deverá ser feito:

                                                                                                                        cod.Produto                cor Produto       Qtde

Arquivo texto recebido Arquivo texto formatado

É uma tarefa simples para que conhece o VB .NET que oferece diversas classes para tratar arquivos textos.

Vamos então rever algumas dessas classes e como usá-las para resolver o problema acima.

Criando o projeto no Visual Basic Express Edition

Abra o Visual Basic 2010 Express Edition e no menu File-> New Project, crie um novo projeto do tipo Windows Forms Application com o nome Gerar_Arquivo_Texto;

No formulário padrão form1.vb vamos incluir a partir da ToolBox os seguinte controles:

Conforme o leiaute da figura abaixo:

Este singelo programa irá atuar da seguinte forma:

  1. Você informa na primeira caixa de texto o nome e o local do arquivo texto recebido;
  2. Na segunda caixa de texto você informa o nome e o local do arquivo formatado que você irá gerar;
  3. No evento Click do botão - Gerar Arquivo Texto Formatado - você inicia a geração do arquivo texto e exibe o resultado da seguinte forma: Na primeira listbox você exibe o arquivo texto original e na segunda o arquivo texto formatado;
  4. No evento Click do botão - Exibe Ultimo arquivo gerado - irá exibir o último arquivo texto formatado gerado;
  5. No evento Click do botão - Apagar arquivo gerado - irá apagar o último arquivo formatado gerado;
  6. No evento Click do botão Sair você encerra o programa;

A seguir vemos uma figura exibindo o programa em execução realizando a tarefa proposta:

Vamos agora definir o código do nosso programa.

A primeira coisa a fazer é identificar as classes que iremos usar para realizar as tarefas de acesso e formatação do arquivo texto. Essas classes se encontram no namespace System.IO por isso vamos definir no início do formulário a declaração :

Imports System.IO

A seguir , logo a após a declaração da classe do formulário form1, vamos definir algumas variáveis visíveis no formulário

 Dim nomeArquivoDestino As String = "c:\dados\ArquivoTextoDestino.txt"
 Dim nomeArquivoDestinoPadrao As String = "c:\dados\ArquivoTextoDestino.txt"
 Dim nomearquivoOrigem As String = "c:\dados\ArquivoTexto.txt"
 Dim nomearquivoOrigemPadrao As String = "c:\dados\ArquivoTexto.txt"
 Dim linha As String = ""
 Dim linhaFormatada As String = ""

Essas variáveis definem o nome do arquivo de origem e destino, a linha e a linhaFormatada que iremos usar para formatar o arquivo texto.

No evento Click do botão - btnOrigem - que fica ao lado da primeira caixa de texto, vamos definir o código que usa o controle OpenFileDialog e que irá abrir a caixa de diálogo para procurar o arquivo origem , selecioná-lo e exibir o seu nome na caixa de texto.

Private Sub btnOrigem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOrigem.Click
        'define o titulo, pasta inicial e o multiselect como false
        Me.ofdOrigem.Multiselect = False
        Me.ofdOrigem.Title = "Selecionar Arquivos"
        ofdOrigem.InitialDirectory = "C:\dados"

        'filtra para exibir somente arquivos textos
        ofdOrigem.Filter = "Texto (*.txt)|*.txt|" & "Texto (*.txt)|*.txt*"
        ofdOrigem.CheckFileExists = True
        ofdOrigem.CheckPathExists = True
        ofdOrigem.FilterIndex = 2
        ofdOrigem.RestoreDirectory = True
        ofdOrigem.ReadOnlyChecked = True
        ofdOrigem.ShowReadOnly = True

        'atribui nome do arquivo de origema a caixa de texto origem
        If ofdOrigem.ShowDialog() = DialogResult.OK Then
            nomearquivoOrigem = ofdOrigem.FileName
            txtArquivoOrigem.Text = nomearquivoOrigem
        Else
            nomearquivoOrigem = nomearquivoOrigemPadrao
            txtArquivoOrigem.Text = nomearquivoOrigem
        End If
    End Sub

No evento Click do botão - Gerar Arquivo Texto Formatado - temos a rotina que inicia a formatação do arquivo texto origem. O código é o seguinte:

Private Sub btnExibeTexto_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExibeTexto.Click
        lstDados.Items.Clear()
        lstFormatado.Items.Clear()
        gerarArquivoTextFormatado()
    End Sub

O código da rotina gerarArquivoTextoFormatado() é quem inicia o trabalho de formatação e seu código é dado a seguir:

Private Sub gerarArquivoTextFormatado()
        ' limpa os  listbox
        lstDados.Items.Clear()
        lstFormatado.Items.Clear()
        Try
            'verifica se existe o arquivo de origem
            If File.Exists(txtArquivoOrigem.Text) Then
                'verifica se ja existe o arquivo formatado e avisa o usuário
                If File.Exists(nomeArquivoDestino) = True Then
                    MessageBox.Show("Atenção !!! Já existe arquivo de destino formatado, se você 
não apagar o arquivo o arquivo gerado terá o conteúdo em duplicidade.", "Gerar arquivo Formatado", 
MessageBoxButtons.OK, MessageBoxIcon.Information)
                End If
                'abre arquivo texto usando a declaração using
                Using r As StreamReader = New StreamReader(nomearquivoOrigem)
                    ' define a linha como  String.
                    Dim linha As String

                    'le primeira linha
                    linha = r.ReadLine

                    ' percorre cada linha no arquivo enquanto a linha contiver dados
                    Do While (Not linha Is Nothing)
                        'exibe a linha
                        lstDados.Items.Add(linha)
                        ' le a proxima linha e formata linha
                        lstFormatado.Items.Add(formataLinha(linha))
                        'gera arquivo formatado
                        geraArquivoFormatado(formataLinha(linha))
                        'le proxima linha
                        linha = r.ReadLine
                    Loop
                End Using
            Else
                MsgBox("Arquivo " + nomearquivoOrigem + " não localizado ")
            End If
        Catch ex As Exception
            MsgBox("Ocorreu um erro durante a geração do arquivo : " & ex.Message)
        End Try
    End Sub


O VB.NET introduziu um novo forma orientada a objetos de trabalhar com arquivos . O namespace
System.IO fornece diversas classes para trabalhar com arquivos textos , arquivos binários e streams de bytes. As classes contém diversos métodos para as operações mais comuns com arquivos : copiar , excluir, manipular os atributos , etc...

Para ler arquivos textos vamos usar a classe StreamReader e os métodos Read e Readline. Para escrever em arquivos textos iremos usar a classe StreamWriter e os métodos Write e WriteLine.
 

Geralmente quando vamos tratar arquivos textos e realizar operações de leitura e escrita precisamos verificar se um determinado arquivo existe , fazemos isto usando a classe System.IO.File que possui os seguintes métodos :
 

AppendText Cria um StreamWriter anexa texto UTF-8 a um arquivo existente.
Copy Overloaded. Copia um arquivo existente para um novo arquivo.
Create Overloaded. Cria um arquivo no diretório especificado.
CreateText Cria ou abre um novo arquivo para escrita.
Delete Exclui um arquivo definido
Exists Determina se um arquivo existe.
GetAttributes Obtêm os atributos do arquivo especificado.
GetCreationTime Retorna a data e hora de criação para o arquivo ou pasta definido.
GetLastAccessTime Retorna a data e hora de criação para o arquivo ou pasta definido acessado pela última vez.
GetLastWriteTime Retorna a data e hora de criação para o arquivo ou pasta definido escrito pela última vez.
Move Move um arquivo definido para um novo local.
Open Overloaded. Abre um FileStream no caminho indicado.
OpenRead Abre um arquivo para leitura.
OpenText Abre um arquivo existente para leitura.(UTF-8)
OpenWrite Abre um arquivo existente para escrita.

A função formataLinha é quem realmente realiza a formatação de cada linha:

 Private Function formataLinha(ByVal linha As String) As String
    
   'define as variaveis locais usadas
        Dim codProd As String
        Dim corProd As String
        Dim qtdProd As String
        Dim tamanho As Integer
        Dim linhaFormatada As String = ""
        Dim strArr() As String
        Try
          
 'separa as linhas do arquivo texto em um array de strings
            strArr = linha.Split(",")
          
 'obtem o tamanho do array
            tamanho = strArr(0).Length
         
  'obtem o código do produto
            codProd = strArr(0).Substring(0, tamanho - 6)
         
  'alinha o codigo do produto
            codProd = codProd.PadRight(20)
         
  'obtem a cor do produto
            corProd = strArr(0).Substring(tamanho - 6, 6)
         
  'alinha a cor do produto
            corProd = corProd.PadRight(15)
            'obtem a quantidade do produto
            qtdProd = strArr(1)
     
      'monta a linha formatada
            linhaFormatada = codProd + corProd + qtdProd
            Return linhaFormatada
        Catch ex As Exception
            Throw ex
        End Try
    End Function

Vamos entender como o código acima funciona:

- Declaramos a variável strArr() como um array de strings para receber cada linha que é extraída do arquivo texto

- O código linha.split(",") separa a linha em duas partes a partir da vírgula. Dessa forma a linha "9800009038,1" será armazenada em duas partes no array: str(0)=9800009038 e str(1)=1;

Obs: Na verdade a função Split retorna um array de única dimensão contendo o número de substrings separadas no processo dependendo do critério e delimitador usado.

- Tratando a primeira parte obtemos tamanho da string armazenada em strArr(0) usando o método Length. Assim para str(0)=9800009038 temos que tamanho=10;

- Como sabemos que o código do produto tem um tamanho fixo de 4 obtemos o seu valor no código: strArr(0).Substring(0, tamanho - 6)
onde usamos o método Substring(0,tamanho-6) que extrai tamanho-6 (=4) posições a partir da posição zero a string referente ao código do produto. Para a string 9800009038 obtemos temos que trArr(0).Substring(0, 4) e obtemos o código do produto: 9800

- Alinhamos o código do produto a esquerda com preenchendo a direita com espaços até 20 posições usando a função PadRight(20)

PadLeft e PadRight são usados para preencher uma string (PadLeft- preenche com caracter(es) à esquerda e PadRight - preenche com caracter(es) à direita).

Sintaxe: PadRight(tamanho,caractere) , PadLeft(tamanho,caractere)

Onde:

tamanho   = O tamanho a ser preenchido
caractere  = O caractere de preenchimento

Exemplo:

PadRight(10, "0") => alinha a sequência de caracteres a esquerda e preenche com zero até 10 posições a direita

Ex: Dim nome as String = "Macoratti"    =>    nome.PadRight(20,"0")   =>   resultado = "Macoratti00000000000"

Obs: Se o caractere não for especificado serão usados espaços em branco para o preenchimento

PadRight(n) - Alinha à esquerda os caracteres de uma seqüência de caracteres, preenchendo à direita com espaços ou um caractere Unicode especificado, para um tamanho total n especificado.

Para PadLeft() o funcionamento é idêntico mas o preenchimento ocorre à esquerda.

Ex: Dim nome as String = "Macoratti"    =>    nome.PadLeft(20,"0")   =>   resultado = "00000000000Macoratti"

- Extraímos a cor do produto usando o código : strArr(0).Substring(tamanho - 6, 6) onde a função Substring extrai 6 posições a string a partir da posição tamanho-6 (=4)

- Alinhamos a cor do produto a esquerda preenchendo com espaços até 15 posições à direita usando o código: corProd = corProd.PadRight(15)

A rotina geraArrquivoFormatado recebe a linha formatada e a escreve no arquivo de destino:

 Private Sub geraArquivoFormatado(ByVal linha As String)
        Try
            Using writer As New StreamWriter(nomeArquivoDestino, True)
                writer.WriteLine(linha)
            End Using
        Catch ex As Exception
            MsgBox(ex.Message)
        End Try
    End Sub

Para exibir o último arquivo de destino formatado temos no evento Click do botão btnExibeUltimoArquivoGerado o seguinte código:

Private Sub btnExibeUltimoArquivoGerado_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExibeUltimoArquivoGerado.Click
        'limpa os listbox
        lstDados.Items.Clear()
        lstFormatado.Items.Clear()
        Try
            'verifica se existe o arquivo de destino formatado
            If File.Exists(nomeArquivoDestino) = True Then
                Dim Tr As IO.TextReader = System.IO.File.OpenText(nomeArquivoDestino)
                Dim FileLines() As String = Split(Tr.ReadToEnd(), vbCrLf)
                Tr.Close()
                'exibe o arquivo formatado no listbox
                For i = 0 To FileLines.Length - 1
                    lstFormatado.Items.Add(FileLines(i))
                Next
            Else
                MsgBox("arquivo não existe")
            End If
        Catch ex As Exception
            MsgBox("Ocorreu um erro durante a exibição do arquivo : " & ex.Message)
        End Try
    End Sub

No evento Click do botão btnApagarArquivoGerado temos o código que exclui o último arquivo de destino gerado:

 Private Sub btnApagarArquivoGerado_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnApagarArquivoGerado.Click
        Try
            'verifica se o arquivo existe
            If File.Exists(nomeArquivoDestino) = True Then
                lstFormatado.Items.Clear()
                'apaga o arquivo
                File.Delete(nomeArquivoDestino)
                MsgBox("arquivo excluido")
            End If
        Catch ex As Exception
            MsgBox("Ocorreu um erro durante a exclusão do arquivo : " & ex.Message)
        End Try
    End Sub

O objetivo foi mostrar como usar as classes do namespace System.IO para realizar uma tarefa prática. Eu não usei um projeto Windows Forms onde coloquei o código no próprio formulário mas você deve atentar que essa prática não é aderente as boas práticas.

Deixo como exercício a separação da lógica de negócio em uma camada separada da camada de apresentação de forma a tornar o projeto mais fácil de manter e testar.

Pegue o projeto completo aqui : Gerar_Arquivo_Texto.zip

Obs: Como exemplo eu deixei uma rotina comentada no projeto que utiliza o StringBuilder no tratamento de strings.

Eu sei é apenas VB .NET mas eu gosto...

"Graças e Paz da parte de Deus Pai e da de nosso Senhor Jesus Cristo. O qual se deu a si mesmo por nossos pecados para nos livrar do presente século mau, segundo a vontade de Deus nosso Pai, ao qual glória para todo o sempre, Amém." 
Gálatas 3-5

Referências:


José Carlos Macoratti