C# - Usando streams : FileStream
Hoje vamos recordar o conceito de streams e mostrar o uso da classe FileStream. |
Não tem ideia do que é um stream e nunca ouviu falar na classe FileStream. Então acompanhe...
Um stream é um objeto que representa uma sequência de bytes, permitindo que o programador leia ou escreva dados em uma fonte de dados, como um arquivo, um socket de rede ou uma memória. Ele fornece uma interface padronizada para operações de entrada e saída de dados, tornando mais fácil trabalhar com diferentes tipos de fontes de dados.
Resumindo : um stream é um fluxo de dados (bytes) que se move de um ponto a outro, assim como um fluxo de água fluindo em um rio e são usados para ler e gravar dados de e para : arquivos, redes, memória e outros streams.
As classes usadas para trabalhar com streams são parte do namespace System.IO e incluem:
Essas classes fornecem uma interface padronizada para trabalhar com streams de diferentes fontes de dados e permitem que os programadores realizem operações de E/S de maneira mais eficiente e flexível.
Vamos focar neste artigo na classe FileStream que implementa a classe Stream e nas classes auxiliares StreamReader e StreamWriter:
Stream é uma classe abstrata para transferência de bytes de diferentes origens. É a classe base para todas as outras classes que leem/escrevem bytes em fontes diferentes;
A classe FileStream fornece a funcionalidade de leitura e gravação de bytes no arquivo físico;
StreamReader fornece métodos para ler strings de um FileStream convertendo bytes em strings;
StreamWriter fornece métodos para gravar strings em um FileStream convertendo strings em bytes;
Como ler um arquivo usando o FileStream
A seguir temos um exemplo que vai ler o conteúdo do arquivo informado pelo
usuário usando o método ReadFile:
using System.Text;
Console.WriteLine("Informe o caminho e nome do arquivo texto : ");
string? caminhoArquivo = Console.ReadLine();
try
{
ReadFile(caminhoArquivo);
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
static void ReadFile(string path) //ex: @"d:\dados\poesia.txt"
{
//abre o arquivo para leitura
var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
//define array de bytes com o tamanho do arquivo
var buffer = new byte[fileStream.Length];
//le o arquivo
fileStream.Read(buffer);
//decodifica o array de bytes em uma string
var resultado = Encoding.UTF8.GetString(buffer);
Console.WriteLine($"\n{resultado}");
// certifique-se de que todos os dados ainda no buffer
// (bytesArray) sejam gravados no arquivo
fileStream.Flush();
//liberar quaisquer recursos do sistema associados ao objeto
fileStream.Close();
}
|
O array de bytes é usado para armazenar temporariamente o conteúdo do arquivo, que será lido ou gravado usando as classes de fluxo (stream) do C#. O tamanho do array é definido com base no tamanho do arquivo para garantir que haja espaço suficiente para armazenar todo o conteúdo do arquivo.
Uma forma mais simplificada de escrever o método acima é usar o bloco using.
O bloco using atua da seguinte forma : quando a variável que você declarou dentro do using sai do escopo, ela chama o método .Dispose() que chama internamente .Flush() e .Close() automaticamente para nós. Assim não corremos o risco de esquecer de liberar os recursos usados.
static void ReadFileUsing(string path)
{
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
{
var bytesArray = new byte[fileStream.Length];
fileStream.Read(bytesArray);
var resultado = Encoding.UTF8.GetString(bytesArray);
Console.WriteLine($"\n{resultado}");
}
}
|
Podemos simplificar o uso do bloco using usando a declaração using (C# 8) :
static void ReadFileUsing(string path)
{
using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read);
var bytesArray = new byte[fileStream.Length];
fileStream.Read(bytesArray);
var resultado = Encoding.UTF8.GetString(bytesArray);
Console.WriteLine($"\n{resultado}");
}
|
Nos exemplos acima, nem precisamos usar o FileStream. O StreamReader lida com o FileStream sob o capô para tornar nossas vidas mais simples.
E se precisarmos ler um arquivo maior do que a nossa memória RAM disponível ?
Uma coisa é certa, não podemos ler todo o arquivo na memória de uma só vez! Temos que usar a abordagem de partes por partes do arquivo.
static string ReadLargeFile(string path)
{
// le o arquivo em pedaços de 4KB
const int bufferSize = 4096;
var builder = new StringBuilder();
using (var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read))
using (var streamReader = new StreamReader(fileStream))
{
char[] buffer = new char[bufferSize];
int bytesRead;
while ((bytesRead = streamReader.ReadBlock(buffer, 0, bufferSize)) > 0)
{
builder.Append(buffer, 0, bytesRead);
}
}
return builder.ToString();
}
|
Aqui está o que cada linha do código faz:
Como gravar em um arquivo usando o FileStream
A seguir temos um método que recebe dois argumentos : o caminho do arquivo e o
texto a ser escrito no arquivo:
static void WriteText(string path, string texto) //E.x. @"d:\dados\teste.txt"
{
try
{
byte[] bytesArray = Encoding.UTF8.GetBytes(texto);
using (var fileStream = new FileStream(path, FileMode.Create))
{
fileStream.Write(bytesArray);
}
}
catch (Exception)
{
throw;
}
}
|
Podemos simplificar o código usando apenas a classe StreamWriter :
static void WriteText(string path, string texto)
{
try
{
using (var s = new StreamWriter(path))
{
s.WriteLine(texto);
}
}
catch (Exception)
{
throw;
}
}
|
Nota: A
classe File também fornece alguns métodos úteis. O exemplo acima poderia ter
sido escrito como: File.WriteAllText(path, texto);
Para
gravar no arquivo podemos usar o seguinte código na classe Program:
using System.Text;
Console.WriteLine("Informe o caminho e nome do arquivo texto : ");
string? caminhoArquivo = Console.ReadLine();
Console.WriteLine("Informe o texto a ser gravado ");
string? texto = Console.ReadLine();
try
{
WriteText(caminhoArquivo, texto);
Console.WriteLine("Texto gravado com sucesso !!!");
}
catch (IOException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
|
Vamos levantar o
mesmo problema de tamanho novamente. E se você quiser escrever um arquivo grande
demais para caber na sua RAM de uma só vez ?
Podemos usar a mesma abordagem que usamos para ler o arquivo grande:
static void WriteFileLarge(string path, string content)
{
// Cria um novo arquivo
using (FileStream stream = new FileStream(path, FileMode.Create, FileAccess.Write))
{
// Escreve em pedaços de 1024 bytes
byte[] buffer = Encoding.UTF8.GetBytes(content);
int chunkSize = 1024;
for (int i = 0; i < buffer.Length; i += chunkSize)
{
int remainingBytes = buffer.Length - i;
int bytesToWrite = remainingBytes < chunkSize ? remainingBytes : chunkSize;
stream.Write(buffer, i, bytesToWrite);
}
}
}
|
Com isso temos alguns exemplos de como ler e gravar em arquivos textos usando FileStream e StreamReader e StreamWriter.
Pegue o exemplo aqui : AppStreams.zip
"Louvai ao SENHOR. Louvai ao SENHOR desde os céus, louvai-o nas alturas.
Louvai-o, todos os seus anjos; louvai-o, todos os seus exércitos.
Louvai-o,
sol e lua; louvai-o, todas as estrelas luzentes."
Salmos 148:1-3
Referências:
NET - Unit of Work - Padrão Unidade de ...