Confuso com a profusão de classes e métodos disponíveis na plataforma .NET para ler e escrever em arquivos ? |
Afinal quando vou
acessar para ler ou escrever em um arquivo qual classe devo usar ?
Como faço para
arquivos textos ?
Antes de irmos direto para a parte prática vamos tentar ter uma visão geral da hierarquia de classes que a plataforma .NET oferece para esta finalidade.
Um pouco de teoria
As operações de input e output na plataforma .NET são gerenciadas pela classe abstrata Stream que esta no namespace System.IO.
Se você conhece os fundamentos da programação orientada a objetos deve saber que uma classe abstrata não pode ser instanciada diretamente.
Portanto você não pode usar a classe Stream diretamente para acessar arquivos.
Ora bolas, então para que serve esta classe ?
Serve para padronizar e fornecer um contrato com métodos e propriedades que devem ser implementadas por outras classes que herdam da classe Stream.
Notou a palavra mágica herança ?
Pois bem a plataforma .NET oferece uma hierarquia de classes que podemos usar para realizar operações de input/output.(I/O)
A seguir vemos uma figura que mostra resumidamente a hierarquia de classes que herdam da classe Stream:
Podemos dizer que a classe Stream é a raiz das classes que tratam um fluxo de dados ou stream.
Como vemos existem
muitas classes que derivam a classe Stream e podemos dividi-las em duas
categorias:
A primeira categoria contém todas as classes que implementam a classe Stream
para uma operação específica de I/O. Essas classes são:
FileStream, para trabalhar com arquivos;
NetworkStream, para transferência de dados em rede;
MemoryStream, para a troca de dados em memória;
A segunda categoria
contém classes derivadas de Stream que reforçam o que a classe Stream
tem a oferecer. Elas podem ser considerados como uma camada sobre a
funcionalidade básica oferecida pela classe Stream.
Esta categoria inclui os seguintes classes:
BufferedStream, que permitem realizar a operação tamponada sobre um stream. Devido ao fato que os dados não são gravados diretamente para o stream, o número de operações I/O necessários para executar a operação é menor, aumentando assim, a execução da operação;
CryptoStream, que permitem que os dados escritos ou lidos a partir do fluxo de dados possa ser criptografado ou descriptografado.
Como todas as classes que tratam streams ou fluxo de dados herdam da classe Stream vejamos a seguir os métodos e propriedades desta classe que as demais classes tem que implementar:
1- Propriedades
CanRead | obtém um valor indicando se o fluxo atual oferece suporte à leitura. |
CanSeek | obtém um valor indicando se o fluxo atual oferece suporte a busca. |
CanTimeout | Obtém um valor que determina se o fluxo atual da suporte ao time out. |
CanWrite | Obtém um valor indicando se o fluxo atual oferece suporte a escrita. |
Length | Obtém o tamanho em bytes do fluxo. |
Position | Obtém ou define a posição no fluxo atual. |
ReadTimeout | Obtém ou define um valor, em milisegundos, que determina quanto tempo o fluxo tentará ler antes do time out. |
WriteTimeout | Obtém ou define um valor, em milisegundos, que determina quanto tempo o fluxo tentará escrever antes do time out. |
2- Métodos (Os principais)
BeginRead | Inicia uma operação de leitura assíncrona. (Considere usar ReadAsync) |
BeginWrite | Inicia uma operação de gravação assíncrona. (Considere usar WriteAsync) |
Close | Fecha o fluxo atual e libera os recursos (como os soquetes e identificadores de arquivo) associados com o fluxo atual. Em vez de chamar esse método, certifique-se de que o fluxo seja descartado corretamente. |
CopyTo(Stream) | Le os bytes do fluxo atual e escreve-os em outro fluxo. |
Dispose() | Libera os recursos usados pelo Stream. |
EndRead | Aguarda a leitura assíncrona pendente terminar. (Considere usar ReadAsync) |
EndWrite | Termina uma operação de gravação assíncrono. (Considere usar WriteAsync) |
Equals(Object) | Verifica se o objeto especificado é igual ao objeto atual. (Herdado de Object.) |
Finalize | Permite que um objeto tente liberar recursos e executar outras operações de limpeza antes que ele seja recuperado pela coleta de lixo. (Herdado de Object.) |
Flush | Limpa todos os buffers para esse fluxo e faz com que alguns dados armazenados em buffer sejam gravados no dispositivo subjacente. |
Read | Lê uma sequência de bytes de fluxo atual e avança a posição no fluxo pelo número de bytes lidos. |
Seek | Define a posição no fluxo atual. |
SetLength | Define o comprimento de fluxo atual. |
ToString | Retorna uma string que representa o objeto atual. (Herdado de Object.) |
Write | Grava uma sequência de bytes no fluxo atual e avança a posição atual dentro desse fluxo pelo número de bytes escritos. |
(fonte: http://msdn.microsoft.com/pt-br/library/system.io.stream%28v=vs.110%29.aspx - acessado em fevereiro de 2014)
Então para ler e escrever em arquivos textos podemos use as classes StreamReader/StreamWriter. Essas classes derivam das classes TextReader/TextWriter.
Abaixo temos uma figura onde temos as classes do namespace System.IO mostrando a hierarquia de herança das principais classes para Input/OutPut.
As classes TextReader/TextWriter representam um leitor/escritor que podem ler/escrever uma série sequencial de caracteres. Elas são classes abstratas e não podem ser instanciadas diretamente.
Observe que as classes StreamReader/StreamWriter herdam de TextReader e de TextWriter.
Concluímos assim que StreamReader é um tipo de TextReader e que StreamWriter é um tipo de TextWriter.
Assim um StreamReader obtém os seus dados de um fluxo ou stream que pode se representado por dados que estão na memória, em um arquivo ou vindo de uma porta serial, vídeo ou capturado em um dispositivo.
Dessa forma um StreamReader possui uma implementação específica para leitura de caracteres de um stream como um arquivo.
StreamReader
Implementa um TextReader que lê caracteres de
um fluxo de bytes em uma codificação específica. TextReader Representa um leitor que pode ler uma série seqüencial de caracteres.(classe abstrata) |
Conclusão: Se eu preciso de uma rotina para ler e/ou escrever em arquivos texto eu vou usar um StreamReader/StreamWriter.
Escrevendo e Lendo Arquivos Textos
Vamos começar mostrando como criar e escrever em arquivos texto usando StreamWriter.
A seguir as principais propriedades e métodos da classe StreamWriter:
AutoFlush() | Obtém ou define um valor indicando se o StreamWriter irá liberar o buffer no fluxo subjacente após cada chamada a Write. |
Close() | Fecha o objeto atual de StreamWriter e o fluxo subjacente |
Dispose() | Libera os recursos usados pelo objeto de TextWriter |
Flush() | Limpa todos os buffers para o gravador atual e faz com que todos os dados armazenados em buffer sejam gravados no fluxo subjacente. |
NewLine() | |
Write() | Grava no fluxo. |
WriteLine() | Grava no fluxo seguido de um terminador de linha |
Para começar temos que referenciar o namespace System.IO e declarar e inicializar uma nova instância de StreamWriter usando um de seus construtores.
StreamWriter writer = new StreamWriter("macoratti.txt")
Nesta declaração um novo StreamWriter é inicializado com o nome do arquivo igual 'macoratti.txt'
(você pode usar qualquer extensão como .dat por exemplo)
Se o arquivo não existir ela será criado. No exemplo como não definimos um local para o arquivo ele será criado no mesmo diretório da aplicação.
Para escrever no arquivo podemos usar o método Write() ou WriteLine() . Estes métodos possuem diversas sobrecargas.
using (StreamWriter writer = new StreamWriter("macoratti.txt"))
{
writer.Write("Macoratti .net ");
writer.WriteLine("Quase tudo para Visual Basic");
writer.WriteLine("http://www.macoratti.net");
}
No código o método Write() escreve uma linha e não anexa uma linha. Já o método WriteLine() anexa uma nova linha ("\r\n") ao arquivo a cada chamada.
Observe que estamos usando a cláusula using de forma a liberar os recursos usados na operação após sua utilização. Nunca esqueça de liberar os recursos usados e também verifique sempre se o arquivo existe quando necessário.
Se desejarmos escrever em um arquivo já existente anexando linhas ao arquivo podemos usar o construtor : StreamWriter(string caminho, bool anexa)
Inicializa uma nova instância do StreamWriter para o arquivo no caminho especificado, usando a codificação padrão e buffer. Se o arquivo existir, pode ser tanto sobrescrito ou anexado. Se o arquivo não existir, este construtor cria um novo arquivo.
using System.IO;
class Program
{
static void Main()
{
// 1: Escreve um linha para o novo arquivo
using (StreamWriter writer = new StreamWriter("C:\\dados\\macoratti.txt", true))
{
writer.WriteLine("Macoratti .net");
}
// 2: Anexa uma linha ao arquivo
using (StreamWriter writer = new StreamWriter(@"C:\dados\macoratti.txt", true))
{
writer.WriteLine("Quase tudo para Visual Basic");
}
}
}
|
No código acima inicia inicializando um novo StreamWriter e abre o arquivo c:\dados\macoratti.txt no modo append. O argumento true do construtor especifica a operação para anexar dados ao arquivo.
No primeiro WriteLine() a string será anexada ao arquivo e no segundo
WriteLine() estamos reabrindo o arquivo
c:\dados\macoratti.txt e anexando
a nova string ao arquivo.
É muito comum quando você estiver escrevendo em um arquivo usar um array ou uma lista de strings. A melhor forma de fazer isso é usar um laço após declarar uma instância de um StreamWriter conforme mostra o exemplo a seguir:
using System.IO; |
No exemplo a instrução using abre e prepara o arquivo e após concluir fecha e libera os recursos usados.
Para ler um arquivo texto utilize a classe StreamReader() criando uma
instância da classe e usando o construtor onde informa o nome do arquivo a ser
aberto.
A seguir temos os principais membros da classe StreamReader:
Close() | Fecha o StreamReader atual |
Dispose() | Libera os recursos usado pelo StreamReader |
Peek() | Retorna o próximo caractere disponível mas não o utiliza |
Read() | Lê o próximo caractere ou conjunto de caracteres de um stream |
ReadBlock() | Lê um número específico de caracteres de um stream atual e escreve os dados em um buffer |
ReadLine() | Lê uma linha de caractere do stream atual e retorna os dados em uma string |
ReadToEnd() | Lê o stream da posição atual até o fim do stream |
No exemplo abaixo estamos acessando o arquivo 'macoratti.txt' e lendo uma linha do arquivo texto usando o método ReadLine(). É uma boa prática sempre verificar se o arquivo a ser lido existe ou se encontra no local indicado.
using System; |
A linha lida é exibida no console usando a instrução Console.WriteLine():
Para ler mais de uma linha do arquivo podemos usar um laço While e percorrer o arquivo enquanto não for encontrado um caractere null que indica o fim de arquivo.
using System;
namespace
Operacoes_IO
if (File.Exists(arquivo))
|
Este código utiliza a classe StreamReader() para ler o arquivo macoratti.txt na pasta c:\dados.
Estamos usando o método ReadLine() em um bloco While lendo linha a linha até encontrar o valor null que corresponde ao final do arquivo : while ((linha = sr.ReadLine()) != null)
Estamos verificando se o arquivo existe usando o método estático Exists() da classe File: if (File.Exists(arquivo))
Estamos usando
também um bloco try/catch para tratar qualquer exceção que ocorre
quando do acesso e da utilização do arquivo.
A cláusula using esta sendo usada para liberar de forma automática os recursos usados para acessar e ler o arquivo.
Dessa forma sempre que formos abrir um recurso como um arquivo primeiro devemos verificar e o mesmo existe e sempre devemos realizar o tratamento de exceção usando o bloco try/catch quando formos tratar arquivos com as classes do namespace System.IO.
Finalmente não devemos esquecer de liberar os recursos usados.
João 5:39 Examinais as Escrituras, porque julgais ter nelas a vida eterna; e são elas que dão testemunho de mim;
João 5:40 mas não quereis vir a mim para terdes vida!
João 5:41 Eu não recebo glória da parte dos homens;
João 5:42 mas bem vos conheço, que não tendes em vós o amor de Deus.
Referências: