Confuso com a
profusão de classes e métodos disponíveis na plataforma .NET |
Afinal quando vou
acessar para ler ou escrever em um arquivo qual classe devo usar ?
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.
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)
Mas onde estão as classes TextReader/TextWriter , StreamReader/StreamWriter e StringReader/StringWriter ?
Se você esta curioso para saber basta olhar a figura abaixo:
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/StringReader herdam de TextReader e StreamWriter/StringWriter herdam de TextWriter.
Concluímos assim que tanto StreamReader como StringReader são tipos de TextReader e que StreamWriter/StringWriter são tipos de TextWriter.
Muita calma nesta hora !!!
Mas qual a diferença então ???
A diferença básica esta na fonte de caracteres.
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.
Um StringReader lê os dados de outro string e um TextReader lê os dados de um arquivo texto.
Um TextReader possui toda lógica para leitura de caracteres e declara um 'contrato' em como fazer a leitura de caracteres.
Um StreamReader possui uma implementação específica para leitura de caracteres de um stream como um arquivo.
Um StringReader possui uma implementação específica para leitura de caracteres de uma string.
StreamReader
Implementa um TextReader que lê caracteres de
um fluxo de bytes em uma codificação específica. StringReader Implementa um TextReader que lê a partir de uma string. TextReader Representa um leitor que pode ler uma série seqüencial de caracteres.(classe abstrata) |
Se eu preciso de uma rotina para ler Texto eu vou usar um TextReader que depois eu posso passar para um StreamReader ou StringReader.
Chegou a hora da prática.
No código abaixo, ingênuo por sinal, estamos o construtor da classe StreamReader() para criar uma instância de um TextReader(). Isso em programação orientada a objetos tem um nome : polimorfismo.
A classe StreamReader inclui métodos sobrecarregados adicionais que permitem que você especifique o arquivo de formas diferentes.
Neste exemplo abrimos o arquivo 'macoratti.txt' que deverá esta na mesma pasta que o arquivo executável da aplicação. (Na pasta Debug/bin):
Textreader tr = new StreamReader("macoratti.txt");
A seguir o programa lê uma linha do arquivo texto usando o método ReadLine() da instância TextReader(). Esta classe também inclui métodos que permitem invocar o método Read() ou usar o método Peek() para verificar o próximo caractere.
A linha lida é exibida no console usando a instrução Console.WriteLine():
Console.WriteLine(tr.ReadLine());
A seguir o código completo.
1- Lendo arquivo texto com StreamReader()
using System;
using System.IO;
namespace Macoratti
{
class TextFileReader
{
static void Main(string[] args)
{
// cria um leitor e abre o arquivo
Textreader tr = new StreamReader("macoratti.txt");
// lê uma linha de texto
Console.WriteLine(tr.ReadLine());
// fecha o stream
tr.Close();
}
}
}
|
2- Escrevendo em um arquivo texto com StreamWriter()
using System;
using System.IO;
namespace Macoratti
{
class TextFileWriter
{
static void Main(string[] args)
{
// cria um escrito e abre o arquivo
TextWriter tw = new StreamWriter("macoratti.txt");
// escreve um a linha de texto no arquivo
tw.WriteLine(DateTime.Now);
// fecha o stream
tw.Close();
}
}
}
|
Neste código o programa cria um arquivo texto chamado 'macoratti.txt' na mesma pasta onde esta o executável da aplicação. O arquivo vai conter a informação da data e hora da última vez que foi aberto para escrita.
Estamos instanciando a classe StreamWriter() que retorna um objeto do
tipo TextWriter() (ai temos o polimorfismo novamente). A classe
StreamWriter() é chamada com um único parâmetro que indica o nome do
arquivo a ser aberto.
Se o arquivo não existir o StreamWriter() vai criar o arquivo:
Uma alternativa mais robusta ao código mostrado acima pode ser usada conforme o código a seguir:
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.
Examinais as Escrituras, porque julgais ter nelas a vida eterna;
e
são elas que dão testemunho de mim;
mas não quereis vir a mim para terdes vida!
Eu não recebo glória da parte dos homens;
mas bem vos conheço, que não tendes em vós o amor de Deus.
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#