Alura > Cursos de Programação > Cursos de .NET > Conteúdos de .NET > Primeiras aulas do curso C#: trabalhando com arquivos

C#: trabalhando com arquivos

Lendo arquivos de texto - Apresentação

Boas-vindas! Meu nome é Larissa Gabriela, faço parte do time de instrutores da Alura, na Escola de Programação. Acompanharei vocês ao longo deste curso, em que falaremos sobre como manipular arquivos com a linguagem C# e ferramentas do ecossistema .NET.

Larissa Gabriela é uma mulher de pele clara com olhos castanhos escuros. Tem cabelos castanhos, lisos, na altura dos ombros. Veste uma regata amarela e está num ambiente com iluminação azul. Ao fundo, há uma parede lisa. Na lateral esquerda do vídeo, vê-se um microfone com um abafador de ruídos.

Quais são os pré-requisitos?

Espera-se que você já tenha conhecimentos sobre sintaxe e tenha desenvolvido seus primeiros programas em C#, trabalhando os conceitos de variável, laços de repetição e condicionais — ferramentas que utilizaremos nesse curso.

Além disso, é importante que você já tenha estudado programação orientada a objetos em C#, pois usaremos algumas nomenclaturas relativas a esse assunto. Por fim, também é interessante que você saiba como lidar com erros em C#: o que são exceções e como tratá-las.

O que vamos desenvolver?

Nesse treinamento, vamos aprender a lidar com fluxo de arquivos, isto é, como selecionar um arquivo em nosso computador e trazê-lo para dentro do nosso código. Por exemplo, podemos mostrar seu conteúdo no console para o usuário, da forma mais otimizada possível. Ademais, aprenderemos a aproveitar as informações desses arquivos em nosso código, utilizando-as em nosso favor.

Também vamos focar na criação de arquivos, escrevendo comandos no código para criar e adicionar informações dentro dele. Além disso, trabalharemos com arquivos binários, estudando como trabalhar com arquivos grandes e otimizar o uso de memória do nosso computador.

Por fim, falaremos sobre o stream do console e exploremos como trazer informações que o usuário digita no console para dentro de um arquivo, por exemplo, para armazenar esses dados.

Espero que vocês estejam animados para se aventurar nesse enorme ecossistema do .NET. Vamos estudar!

Lendo arquivos de texto - Entendendo fluxo de dados

Vamos dar início ao nosso curso em que exploraremos como aplicar os conceitos de fluxo de dados e arquivos de texto, de modo a solucionar problemas e melhorar nossa aplicação, tornando-a mais dinâmica.

Para começar, vamos abrir o projeto inicial do curso no Visual Studio. O projeto ByteBank é um banco em que podemos simular algumas ações, como depósitos e saques. Nele, temos as classes ContaCorrente e Cliente. Trata-se de uma aplicação relativamente simples, pois nosso foco será trabalhar com arquivos de texto.

Até então, para criar uma conta-corrente na nossa aplicação, declaramos uma variável (por exemplo, conta) e instanciamos uma ContaCorrente, fornecendo o número da agência e o número da conta-corrente, em Program.cs:

using ByteBankIO;

class Program
{
    static void Main(string[] args)
    {
        var conta = ContaCorrente(524, 4518);
        Console.ReadLine();
    }

    private static object ContaCorrente(int v1, int v2)
    {
        throw new NotImplementedException();
    }
}

Se estivéssemos trabalhando com uma pequena quantidade de contas, isso seria o suficiente. No entanto, considerando o cenário de um banco grande em que há diversos funcionários criando inúmeras contas-correntes diariamente, esse sistema seria inconveniente e prejudicial. É preciso encontrar formas mais rápidas de acessar esses dados.

Uma opção é armazenar os dados dos clientes em um arquivo de texto, listando o número da agência, o número da conta, o saldo e o nome do cliente — como no arquivo contas.txt disponibilizado junto do projeto do curso. Veja um pequeno trecho dele, a seguir:

296 5459 3494.67 Davi

433 2341 3257.15 Elza

465 2291 1566.57 Denilson

234 6151 4081.61 Tania

...

A princípio, poderíamos pensar em armazenar esses dados em uma única string e, posteriormente, usar métodos para "quebrá-la" e separar cada item. Essa ideia é possível, porém muito trabalhosa. Além disso, por serem muitas informações, esse processo consumiria bastante memória do computador. Logo, vamos encontrar uma forma melhorar de lidar com esses dados, trazendo esse arquivo .txt para dentro do nosso programa.

Fluxo de dados

Primeiramente, é importante compreendermos como o computador interpreta e trabalha com dados, ou seja, o fluxo de dados.

O arquivo contas.txt é pequeno, tem cerca de 24 kB. Examinando a memória RAM do nosso computador, é comum encontrarmos a memória de 4 GB ou 8 GB, uma quantidade razoável para manter esse documento inteiro carregado na memória RAM.

Quando estamos trabalhando com vídeos (por exemplo, aulas na plataforma Alura, ou filmes em HD ou 4K), esses vídeos de alta qualidade ocupam bastante memória. Geralmente, o computador não os carrega e interpreta de uma só vez, ele realiza esse processo gradualmente.

Em outras palavras, não lidamos com um arquivo completo para exibir um dado para o usuário, mas com um fluxo de dados.

Como os vídeos são um código diferenciado para que o computador consiga interpretá-los, nossa máquina transforma esses dados em vídeo de trechos em trechos, conforme assistimos. E esta é exatamente a ideia de fluxo de dados: selecionar trechos e trabalhar com eles para minimizar o uso de memória e otimizar as ferramentas que estamos utilizando.

Em resumo, para desenvolver nosso projeto em C#, é importante saber que lidamos com fluxo de dados, não com arquivos completos. Para exibir informações aos usuários, ponderamos e lidamos com trechos para facilitar nosso trabalho.

Lendo arquivos de texto - Criando um FileStream

No vídeo anterior, entendemos como funciona o fluxo de dados. Aprendemos, por exemplo, como o computador exibe o conteúdo de um vídeo, lidando com trechos de arquivos em lugar do arquivo completo, para otimizar o uso de memória. Agora, vamos entender esse conceito na prática, construindo um fluxo de dados na nossa aplicação.

O primeiro passo é mostrar para a aplicação com qual arquivo trabalharemos, seja para abri-lo ou criá-lo. No caso, vamos abrir e manipular o arquivo contas.txt, disponibilizado junto do projeto inicial do curso.

Endereço do arquivo

Em Program.cs, vamos armazenar o endereço do arquivo em uma variável para a aplicação reconhecer onde ele está. Esse endereço varia de acordo com a pasta onde salvamos o contas.txt — por exemplo, se você fez o download pela plataforma da Alura, é possível que ele esteja na pasta "Downloads". Para evitar confusões ao longo do curso, vamos colocar o arquivo contas.txt dentro da mesma pasta do nosso executável.

Para encontrar a pasta do executável, vamos clicar em "Exibir > Gerenciador de Soluções" no menu superior do Visual Studio ou usar o atalho "Ctrl + Alt + L". O gerenciador de soluções será aberto na lateral direita da IDE. Nele, temos as classes e as dependências com as quais estamos trabalhando até agora na nossa aplicação. Com o botão direito, clicaremos na nossa solução "Solução 'ByteBank_IO' (1 de 1 projeto)" e selecionaremos "Abrir Pasta no Gerenciador de Arquivos".

Uma vez aberto o gerenciador de arquivos, vamos navegar até "ByteBank_IO > bin > Debug > net6.0". Entre outros arquivos, encontraremos nessa pasta o nosso executável (o aplicativo da nossa aplicação): o ByteBank_IO. Portanto, vamos arrastar o arquivo contas.txt para dentro dessa pasta.

Agora, voltando ao Program.cs, podemos armazenar o endereço do arquivo em uma variável chamada enderecoDoArquivo. Como ele está na mesma pasta do executável, basta colocar o nome do arquivo:

using ByteBankIO;

class Program
{
    static void Main(string[] args)
    {
        var enderecoDoArquivo = "contas.txt";

        Console.ReadLine();
    }
}

Assim, evitamos escrever um caminho longo, minimizando problemas que poderiam surgir por conta do endereço do arquivo — por exemplo, caso movêssemos o arquivo da pasta "Downloads" para a pasta "Documentos".

Fluxo de bytes

O próximo passo é criar um fluxo de bytes, que possibilitará nosso acesso ao arquivo contas.txt. Em outras palavras, vamos percorrer os bytes que contêm os dados desse documento.

De início, vamos criar a variável fluxoDoArquivo para guardar esse fluxo de dados. A partir de agora, trabalharemos com uma ferramenta importante para lidar com arquivos: o FileStream. Stream significa "fluxo" em inglês, logo, o FileStream trabalhará com o fluxo de dados de um arquivo:

using ByteBankIO;

class Program
{
    static void Main(string[] args)
    {
        var enderecoDoArquivo = "contas.txt";
        var fluxoDoArquivo = new FileStream();

        Console.ReadLine();
    }
}

Ao digitar new FileStream() na linha 8, o Visual Studio indicará alguns argumentos que precisamos fornecer para que o FileStream funcione adequadamente. O primeiro argumento é o endereço do arquivo, que guardamos na variável enderecoDoArquivo:

using ByteBankIO;

class Program
{
    static void Main(string[] args)
    {
        var enderecoDoArquivo = "contas.txt";
        var fluxoDoArquivo = new FileStream(enderecoDoArquivo);

        Console.ReadLine();
    }
}

Visto que existem vários modos de operação do FileStream, também precisamos especificar o que pretendemos fazer com contas.txt: criar, abrir, modificar, concatenar etc. Então, no segundo argumento, vamos indicar que queremos abrir o arquivo, com FileMode.Open:

using ByteBankIO;

class Program
{
    static void Main(string[] args)
    {
        var enderecoDoArquivo = "contas.txt";
        var fluxoDoArquivo = new FileStream(enderecoDoArquivo, FileMode.Open);

        Console.ReadLine();
    }
}

Assim, a variável está pronta para trabalhar com o fluxo do nosso arquivo. Já informamos o arquivo com o qual trabalharemos e indicamos que vamos abri-lo. Na sequência, o objetivo será recuperar o bytes que estão dentro desse arquivo.

O método Read

O FileStream possui um método chamado Read. A seguir, vamos explorar sua estrutura:

public override int Read(byte[] array, int offset, int count);

O método Read recebe três argumentos. O primeiro é o byte[] array, onde serão armazenados os bytes lidos pelo método — ou seja, retomando a ideia de fluxo, de trabalhar de parte em parte no código. Esse conceito ficará mais claro, à medida que entendermos como fornecemos esse array.

O segundo argumento é o int offset, que delimita o índice em que o método começará a preencher o array. Por exemplo: para preencher a partir da primeira posição (índice 0), informaremos o número 0 no offset. Caso indiquemos o número 10, começaremos a preencher o array a partir do índice 10 e as dez primeiras posições (índices 0 a 9) ficarão reservadas.

O terceiro argumento é o int count, que informa quantas posições preencher. Por exemplo, se indicarmos o offset como 0 e o count como 10, preencheremos do índice 0 a 9. Vale lembrar que iniciamos a contagem dos índices no 0, por isso, a posição 10 corresponde ao índice 9.

Em resumo: é preciso fornecer o array, o índice em que começaremos a preenchê-lo e quantas posições serão usadas.

O array informado ao FileStream possui um nome bastante comum, com o qual nos deparamos com frequência no meio da programação: buffer. Trata-se de um array que reutilizamos para guardar informações temporárias. Novamente, voltamos ao conceito de fluxo e uso de dados por trechos, sem carregar tudo de uma vez. O buffer armazenará trechos temporários do arquivo.

A seguir, começaremos a construir o Read do nosso FileStream:

using ByteBankIO;

class Program
{
    static void Main(string[] args)
    {
        var enderecoDoArquivo = "contas.txt";
        var fluxoDoArquivo = new FileStream(enderecoDoArquivo, FileMode.Open);

                fluxoDoArquivo.Read();

                // public override int Read(byte[] array, int offset, int count)

        Console.ReadLine();
    }
}

Antes de informar os argumentos, vamos criar uma variável chamada buffer que receberá o nosso array, um novo byte com 1024 posições, isto é, o tamanho de 1 kB:

using ByteBankIO;

class Program
{
    static void Main(string[] args)
    {
        var enderecoDoArquivo = "contas.txt";
        var fluxoDoArquivo = new FileStream(enderecoDoArquivo, FileMode.Open);

        var buffer = new byte[1024]; //1KB

        fluxoDoArquivo.Read();

        // public override int Read(byte[] array, int offset, int count)

        Console.ReadLine();
    }
}

O método Read receberá o buffer como primeiro argumento. Quanto aos demais argumentos, nossa intenção é que o FileStream comece a gravar a partir da posição 0 até o fim do array:

using ByteBankIO;

class Program
{
    static void Main(string[] args)
    {
        var enderecoDoArquivo = "contas.txt";
        var fluxoDoArquivo = new FileStream(enderecoDoArquivo, FileMode.Open);

        var buffer = new byte[1024]; //1KB

        fluxoDoArquivo.Read(buffer, 0, 1024);

        // public override int Read(byte[] array, int offset, int count)

        Console.ReadLine();
    }
}

Assim, iniciamos a implementação do conceito de fluxo. Criamos um byte e vamos preenchê-lo a partir da posição 0 até a 1024, começamos a entender o uso do FileStream, do FileMode.Open para informar ao programa o que fazer com o arquivo, e do método Read.

A partir dos próximos vídeos, nos aprofundaremos em como melhorar nosso código e visualizar o arquivo.

Sobre o curso C#: trabalhando com arquivos

O curso C#: trabalhando com arquivos possui 196 minutos de vídeos, em um total de 44 atividades. Gostou? Conheça nossos outros cursos de .NET em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda .NET acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas