C# Arquivos
Até aqui, tudo o que seus programas sabiam sumia quando eles terminavam: as variáveis vivem na memória, e a memória é apagada no fim. Um arquivo fica no disco e continua lá no dia seguinte. Nesta aula você vai usar a classe File ("arquivo") para gravar a lista de compras da Ana, ler as notas da turma, acrescentar linhas num registro (log) e copiar, mover e apagar arquivos. Também vai conhecer os erros de arquivo mais comuns e como tratá-los.
- Gravar um texto com
File.WriteAllTexte ler de volta comFile.ReadAllText. - Trabalhar linha por linha com
File.WriteAllLines,File.ReadAllLineseFile.ReadLines. - Acrescentar no fim sem apagar o que havia:
File.AppendAllTexteFile.AppendAllLines. - Perguntar e mexer no arquivo:
File.Exists,File.Copy,File.MoveeFile.Delete. - O que é UTF-8, por que
ãocupa 2 bytes e como ler um arquivo antigo em Latin-1. - As exceções
FileNotFoundException,DirectoryNotFoundException,IOExceptioneUnauthorizedAccessException, e o "8.5 que virou 85".
Esta aula usa arrays, foreach e try e catch. Todos os programas foram rodados com dotnet run (aula Rodar com dotnet run) no .NET 10 da máquina da Ana (usuária ana, computador notebook), dentro da pasta ~/cs, que é /home/ana/cs.
A ideia: o caderno na gaveta
Pense na memória do computador como um quadro branco: rápido de escrever, mas alguém apaga tudo no fim da aula. O disco é um caderno guardado na gaveta: o que você escreve ali continua lá amanhã. Para usar o caderno você precisa de duas coisas: o nome dele (o caminho do arquivo, como compras.txt) e alguém que saiba abrir, ler, escrever e fechar. Em C#, esse alguém é a classe File.
A classe File fica no namespace System.IO (de input/output, entrada e saída). Você não precisa escrever using System.IO;: no .NET 10, ele já vem incluído nos programas de console (são os usings implícitos, da aula Namespaces e using). Todos os métodos desta aula são estáticos: você chama direto File.Método(…), sem criar objeto.
| Método | Tradução | O que faz |
|---|---|---|
File.WriteAllText(caminho, texto) | escrever todo o texto | Cria (ou substitui) o arquivo com o texto |
File.ReadAllText(caminho) | ler todo o texto | Devolve o arquivo inteiro numa string |
File.WriteAllLines(caminho, linhas) | escrever todas as linhas | Grava cada item numa linha |
File.ReadAllLines(caminho) | ler todas as linhas | Devolve um string[], uma linha por posição |
File.AppendAllText(caminho, texto) | acrescentar texto | Grava no fim, mantendo o que havia |
1. Gravar e ler um texto: WriteAllText e ReadAllText
A Ana quer guardar a lista de compras num arquivo. Este programa grava a lista, lê o arquivo de volta e mostra o que leu:
File.WriteAllText("compras.txt", "pão\nleite\n");
string texto = File.ReadAllText("compras.txt");
Console.Write(texto);
Console.WriteLine($"{texto.Length} caracteres");
- File.WriteAllText("compras.txt", "pão\nleite\n");
- Abre o arquivo
compras.txt, grava o texto e fecha. Se o arquivo não existe, ele é criado. Se já existe, o conteúdo antigo é apagado e substituído. O\né a quebra de linha (aula Escapes): oWriteAllTextgrava exatamente o que você mandar, sem pular linha sozinho. - string texto = File.ReadAllText("compras.txt");
- Abre o arquivo, lê tudo, fecha e devolve o conteúdo numa única
string, com as quebras de linha dentro. - Console.Write(texto);
Write, e nãoWriteLine: o texto já termina com\n. ComWriteLineapareceria uma linha em branco a mais.- texto.Length
- Quantos caracteres a
stringtem:pão(3) +\n(1) +leite(5) +\n(1) = 10.
$ dotnet run compras.cs pão leite 10 caracteres $ cat compras.txt « o arquivo continua lá depois que o programa terminou pão leite
O nome "compras.txt" não diz em que pasta o arquivo fica. Um nome assim é um caminho relativo: ele é procurado (ou criado) na pasta atual, a pasta em que o terminal estava quando a Ana deu o dotnet run. Aqui, /home/ana/cs. A aula Pastas e Caminhos mostra como montar caminhos completos.
Rodar o programa duas vezes não deixa a lista com 4 itens: a segunda vez substitui o arquivo inteiro. Não há "desfazer" nem lixeira. Para acrescentar no fim, use AppendAllText (seção 3).
2. Linha por linha: WriteAllLines e ReadAllLines
Quase todo arquivo de texto é uma lista de linhas: uma compra por linha, um aluno por linha, um produto por linha. Os métodos …Lines trabalham direto com essas listas, sem você precisar mexer com \n.
string[] alunos = ["Ana;8,5", "Bruno;6", "Carla;9,5"];
File.WriteAllLines("notas.txt", alunos);
string[] linhas = File.ReadAllLines("notas.txt");
Console.WriteLine($"Linhas: {linhas.Length}");
foreach (string linha in linhas)
{
string[] partes = linha.Split(';');
Console.WriteLine($"{partes[0]} tirou {partes[1]}");
}
- ["Ana;8,5", "Bruno;6", "Carla;9,5"]
- Um array de
stringescrito com a expressão de coleção C# 12 (aula Arrays). Cada texto tem o nome e a nota separados por;. - File.WriteAllLines("notas.txt", alunos);
- Grava cada item do array numa linha, com a quebra de linha no fim de cada uma (inclusive da última). Aceita um array, uma
List<string>ou qualquer sequência de textos. - File.ReadAllLines("notas.txt")
- Lê o arquivo e devolve um
string[]com uma linha em cada posição, já sem as quebras de linha. - linha.Split(';')
- Corta a linha em cada
;(aula Métodos de String):partes[0]é o nome epartes[1]é a nota, ainda como texto.
$ dotnet run notas.cs Linhas: 3 Ana tirou 8,5 Bruno tirou 6 Carla tirou 9,5 $ cat notas.txt Ana;8,5 Bruno;6 Carla;9,5
Um detalhe que confunde: o arquivo termina com uma quebra de linha depois de Carla;9,5, mas o ReadAllLines devolve 3 linhas, e não 4. A última quebra só fecha a última linha; ela não abre uma linha vazia. Um arquivo vazio dá um array vazio (Length = 0).
Missão: o programa grava a lista de compras e mostra quantos itens ela tem. Depois do WriteAllText, acrescente café no fim do arquivo com File.AppendAllText("compras.txt", "café\n"); e rode de novo: a saída deve ser 3 itens: pão, leite, café.
File.WriteAllText("compras.txt", "pão\nleite\n");
string[] itens = File.ReadAllLines("compras.txt");
Console.WriteLine($"{itens.Length} itens: {string.Join(", ", itens)}");
File.WriteAllText("compras.txt", "pão\nleite\n");
File.AppendAllText("compras.txt", "café\n");
string[] itens = File.ReadAllLines("compras.txt");
Console.WriteLine($"{itens.Length} itens: {string.Join(", ", itens)}");
O string.Join(", ", itens) junta os itens do array num texto só, com ", " entre eles (aula Métodos de String).
3. Acrescentar no fim: AppendAllText e AppendAllLines
Um log (registro) é um arquivo que só cresce: cada vez que algo acontece, o programa escreve uma linha nova no fim. Para isso existem os métodos Append… ("acrescentar"):
File.AppendAllText("log.txt", "Programa iniciado\n");
File.AppendAllLines("log.txt", ["Venda: caderno", "Venda: caneta"]);
Console.WriteLine($"O log tem {File.ReadAllLines("log.txt").Length} linhas");
- File.AppendAllText("log.txt", "Programa iniciado\n");
- Grava o texto no fim do arquivo, mantendo o que já estava lá. Se o arquivo não existe, ele é criado, como no
WriteAllText. A quebra de linha\ncontinua sendo sua. - File.AppendAllLines("log.txt", [ … ]);
- O mesmo, para uma lista: cada item vira uma linha no fim do arquivo, cada uma com a sua quebra.
$ dotnet run log.cs O log tem 3 linhas $ dotnet run log.cs « de novo: as linhas antigas ficam O log tem 6 linhas $ cat log.txt Programa iniciado Venda: caderno Venda: caneta Programa iniciado Venda: caderno Venda: caneta
4. Arquivos grandes: ReadLines
ReadAllText e ReadAllLines carregam o arquivo inteiro na memória antes de devolver qualquer coisa. Para a lista de compras, ótimo. Para um log de 2 GB, o programa pode ficar sem memória. O File.ReadLines (sem o All) entrega uma linha por vez, conforme o foreach pede:
int n = 0;
foreach (string linha in File.ReadLines("log.txt"))
{
n++;
if (linha.StartsWith("Venda"))
{
Console.WriteLine($"{n}: {linha}");
}
}
- File.ReadLines("log.txt")
- Não devolve um array: devolve uma sequência (
IEnumerable<string>, aula IEnumerable e foreach). Cada volta doforeachlê a próxima linha do disco. Só uma linha fica na memória de cada vez. - n++
- Conta as linhas, para mostrar o número de cada uma.
- linha.StartsWith("Venda")
- Verdadeiro se a linha começa com
Venda.
$ dotnet run vendas.cs 2: Venda: caderno 3: Venda: caneta 5: Venda: caderno 6: Venda: caneta
A diferença não aparece na saída: aparece na memória. Se você precisa do Length ou de linhas[3], use ReadAllLines. Se só vai passar pelas linhas uma vez, do começo ao fim, prefira ReadLines. Para ler e escrever aos poucos com mais controle, a próxima aula apresenta o StreamReader e o StreamWriter.
5. Existe? Copiar, mover e apagar
Console.WriteLine(File.Exists("compras.txt"));
Console.WriteLine(File.Exists("fotos.zip"));
File.Copy("compras.txt", "compras-backup.txt", overwrite: true);
File.Move("compras-backup.txt", "backup.txt", overwrite: true);
Console.WriteLine(File.Exists("compras-backup.txt"));
Console.WriteLine(File.Exists("backup.txt"));
File.Delete("backup.txt");
File.Delete("backup.txt");
Console.WriteLine(File.Exists("backup.txt"));
- File.Exists("compras.txt")
- "O arquivo existe?" Devolve
trueoufalsee nunca lança exceção, nem com um nome estranho. - File.Copy(origem, destino, overwrite: true)
- Copia. Sem o
overwrite: true("sobrescrever", um argumento nomeado), ele recusa um destino que já existe e lançaIOException(veja "Quando dá errado"). - File.Move(origem, destino, overwrite: true)
- Move ou renomeia: depois dele, o nome antigo não existe mais. O
overwritefunciona como noCopy. - File.Delete("backup.txt")
- Apaga de verdade, sem lixeira. Se o arquivo já não existe, ele não reclama: por isso a segunda linha igual não deu erro.
$ dotnet run backup.cs True False False True False
Conferir com if (File.Exists(nome)) antes de ler evita a exceção no caso comum ("o arquivo ainda não foi criado"). Mas o arquivo pode sumir entre o Exists e a leitura, ou existir e estar trancado. Por isso, em programas para outras pessoas, a leitura também fica dentro de um try (seção "Quando dá errado").
6. Texto e bytes: UTF-8
No disco, um arquivo é uma fila de bytes (números de 0 a 255). A codificação (encoding) é a tabela que diz qual byte vira qual letra. Para as letras sem acento, quase todas as tabelas concordam. Para ã, é e ç, não.
O .NET grava e lê em UTF-8 quando você não diz nada, que é a codificação padrão da internet e do Linux. Em UTF-8, cada letra acentuada ocupa 2 bytes. O comando xxd mostra os bytes do arquivo de compras (com o café acrescentado na missão):
$ xxd compras.txt 00000000: 70c3 a36f 0a6c 6569 7465 0a63 6166 c3a9 p..o.leite.caf.. 00000010: 0a .
70 é o p, c3 a3 é o ã (dois bytes), 6f é o o e 0a é a quebra de linha. São 15 caracteres, mas 17 bytes.
Arquivos antigos, gerados por programas velhos do Windows, às vezes vêm em Latin-1 (ISO-8859-1), em que ã é um byte só. A Ana recebeu uma lista de clientes assim. O comando printf do terminal cria um arquivo igual, e o comando file descobre a codificação:
using System.Text;
Console.WriteLine(File.ReadAllText("clientes-antigo.txt"));
Console.WriteLine(File.ReadAllText("clientes-antigo.txt", Encoding.Latin1));
$ printf 'Jo\343o;S\343o Paulo\n' > clientes-antigo.txt $ file clientes-antigo.txt clientes-antigo.txt: ISO-8859 text $ dotnet run latin.cs Jo�o;S�o Paulo João;São Paulo
- using System.Text;
- A classe
Encoding("codificação") fica no namespaceSystem.Text, que não vem incluído sozinho. - File.ReadAllText(caminho)
- Lê como UTF-8. O byte do
ãem Latin-1 não forma uma letra válida em UTF-8, e o .NET o troca pelo símbolo�("caractere de substituição"). Não há exceção: o texto simplesmente sai estragado. - File.ReadAllText(caminho, Encoding.Latin1)
- Todos os métodos de
Fileaceitam a codificação como último argumento. Com a tabela certa, o texto sai certo.
As linhas em branco na saída são o \n do fim do arquivo mais o do WriteLine. Se aparecer �, ou textos como João (UTF-8 lido como Latin-1), o arquivo e o programa estão usando tabelas diferentes. Para gravar, fique sempre com o padrão, UTF-8.
Qual método usar
| Você quer… | Use |
|---|---|
| Gravar um texto (criar ou substituir o arquivo) | File.WriteAllText(caminho, texto) |
| Gravar uma lista, um item por linha | File.WriteAllLines(caminho, lista) |
| Acrescentar no fim (log, registro) | File.AppendAllText ou File.AppendAllLines |
| Ler um arquivo pequeno inteiro | File.ReadAllText(caminho) |
Ler as linhas e usar Length ou linhas[i] | File.ReadAllLines(caminho) |
| Passar uma vez por um arquivo grande | foreach (… in File.ReadLines(caminho)) |
| Ler ou escrever aos poucos, com controle | StreamReader e StreamWriter (aula Ler e Escrever) |
| Ler sem travar o programa (em apps e servidores) | File.ReadAllTextAsync e os outros …Async (aula async e await) |
Quando dá errado
Os erros de arquivo são exceções (aula Exceções): o programa compila, mas para no meio, com o código de saída 134. Todas trazem o caminho completo na mensagem, o que ajuda muito a descobrir o problema. As mensagens das exceções saem em inglês.
FileNotFoundException: o arquivo não existe (ali)
string texto = File.ReadAllText("nota.txt");
Console.WriteLine(texto);
$ dotnet run ler.cs; echo "código: $?" Unhandled exception. System.IO.FileNotFoundException: Could not find file '/home/ana/cs/nota.txt'. File name: '/home/ana/cs/nota.txt' at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirError) at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String path, OpenFlags flags, Int32 mode, Boolean failForSymlink, Boolean& wasSymlink, Func`4 createOpenException) at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, UnixFileMode openPermissions, Int64& fileLength, UnixFileMode& filePermissions, Boolean failForSymlink, Boolean& wasSymlink, Func`4 createOpenException) at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode) at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, Int64 preallocationSize) at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Int32 bufferSize) at System.IO.File.ReadAllText(String path, Encoding encoding) at Program.<Main>$(String[] args) in /home/ana/cs/ler.cs:line 1 código: 134
O que aconteceu: "Não foi possível encontrar o arquivo". O arquivo se chama notas.txt; faltou o s. A primeira linha diz o caminho exato que o .NET tentou; a última diz a linha do seu programa (ler.cs:line 1). As do meio são o caminho que a chamada fez dentro do .NET e podem ser ignoradas.
Como resolver: confira o nome (no Linux, Notas.txt e notas.txt são arquivos diferentes), a extensão e a pasta em que você rodou o programa. Num programa para outras pessoas, trate a exceção e mostre uma mensagem amigável:
try
{
string texto = File.ReadAllText("nota.txt");
Console.WriteLine(texto);
}
catch (FileNotFoundException e)
{
Console.WriteLine($"Não achei o arquivo: {e.FileName}");
}
$ dotnet run tratar.cs Não achei o arquivo: /home/ana/cs/nota.txt
DirectoryNotFoundException: a pasta não existe
File.WriteAllText("relatorios/outubro.txt", "Vendas: 1520\n");
$ dotnet run pasta.cs Unhandled exception. System.IO.DirectoryNotFoundException: Could not find a part of the path '/home/ana/cs/relatorios/outubro.txt'. at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirError) … « mais 4 linhas de dentro do .NET at Program.<Main>$(String[] args) in /home/ana/cs/pasta.cs:line 1
O que aconteceu: "Não foi possível encontrar uma parte do caminho". O WriteAllText cria o arquivo, mas não cria pastas, e a pasta relatorios não existe.
Como resolver: crie a pasta antes com Directory.CreateDirectory("relatorios");, assunto da aula Pastas e Caminhos.
IOException: o destino da cópia já existe
File.Copy("compras.txt", "copia.txt");
Console.WriteLine("Copiei");
$ dotnet run copiar.cs Copiei $ dotnet run copiar.cs « de novo: agora copia.txt já existe Unhandled exception. System.IO.IOException: The file '/home/ana/cs/copia.txt' already exists. at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirError) … « mais 3 linhas de dentro do .NET at Program.<Main>$(String[] args) in /home/ana/cs/copiar.cs:line 1
O que aconteceu: "O arquivo já existe". O Copy e o Move se recusam a apagar um arquivo que já está no destino, para você não perder dados sem querer. (O WriteAllText, ao contrário, substitui sem perguntar.)
Como resolver: se substituir é o que você quer, diga isso: File.Copy("compras.txt", "copia.txt", overwrite: true);. Se não, confira antes com File.Exists("copia.txt").
UnauthorizedAccessException: sem permissão
$ echo segredo > trancado.txt
$ chmod 444 trancado.txt « só leitura, para todo mundo
$ cat trancado.cs
File.WriteAllText("trancado.txt", "novo");
$ dotnet run trancado.cs
Unhandled exception. System.UnauthorizedAccessException: Access to the path '/home/ana/cs/trancado.txt' is denied.
---> System.IO.IOException: Permission denied
--- End of inner exception stack trace ---
at Interop.ThrowExceptionForIoErrno(ErrorInfo errorInfo, String path, Boolean isDirError)
… « mais 4 linhas de dentro do .NET
at Program.<Main>$(String[] args) in /home/ana/cs/trancado.cs:line 1
O que aconteceu: "O acesso ao caminho foi negado". O arquivo é só de leitura (as permissões do Linux, que o Guia Bash explica com o chmod). O mesmo acontece ao tentar gravar numa pasta do sistema, como /etc, sem ser administradora.
Como resolver: grave em outro lugar (a sua pasta pessoal) ou devolva a permissão com chmod u+w trancado.txt. Para tratar todos os erros de arquivo de uma vez, use catch (IOException e) (pega FileNotFoundException, DirectoryNotFoundException e as outras filhas) seguido de catch (UnauthorizedAccessException e), que é de outra família.
A planilha da escola veio com as notas escritas com ponto, como nos países de língua inglesa: Ana;8.5. A Ana converteu o texto em número com double.Parse (aula Parse e TryParse):
string[] linhas = File.ReadAllLines("notas.txt");
foreach (string linha in linhas)
{
string[] partes = linha.Split(';');
double nota = double.Parse(partes[1]);
Console.WriteLine($"{partes[0]} tirou {nota}");
}
$ cat notas.txt Ana;8.5 Bruno;6.0 Carla;9.5 $ dotnet run media.cs Ana tirou 85 Bruno tirou 60 Carla tirou 95
O que aconteceu: nenhuma exceção, e o resultado está errado, o pior tipo de erro. O computador da Ana está em português, e em pt-BR a vírgula separa os decimais e o ponto separa os milhares (como em 1.500). Para o double.Parse, o ponto de 8.5 é um separador de milhares, que ele aceita e ignora: sobra 85. A aula Cultura explica isso em detalhes.
Como resolver: quando o arquivo usa ponto, diga ao Parse que ele está no formato "neutro", o CultureInfo.InvariantCulture:
using System.Globalization;
string[] linhas = File.ReadAllLines("notas.txt");
double soma = 0;
foreach (string linha in linhas)
{
string[] partes = linha.Split(';');
double nota = double.Parse(partes[1], CultureInfo.InvariantCulture);
Console.WriteLine($"{partes[0]} tirou {nota}");
soma += nota;
}
Console.WriteLine($"Média: {soma / linhas.Length:F2}");
$ dotnet run media.cs Ana tirou 8,5 Bruno tirou 6 Carla tirou 9,5 Média: 8,00
O arquivo continua com ponto; na tela, os números aparecem do jeito brasileiro.
Missão: o notas.txt da outra aba usa ponto nas notas, e a média saiu 80,00. Conserte o double.Parse com CultureInfo.InvariantCulture para a saída terminar com Média: 8,00.
using System.Globalization;
string[] linhas = File.ReadAllLines("notas.txt");
double soma = 0;
foreach (string linha in linhas)
{
string[] partes = linha.Split(';');
double nota = double.Parse(partes[1]);
Console.WriteLine($"{partes[0]} tirou {nota}");
soma += nota;
}
Console.WriteLine($"Média: {soma / linhas.Length:F2}");
Ana;8.5 Bruno;6.0 Carla;9.5
using System.Globalization;
string[] linhas = File.ReadAllLines("notas.txt");
double soma = 0;
foreach (string linha in linhas)
{
string[] partes = linha.Split(';');
double nota = double.Parse(partes[1], CultureInfo.InvariantCulture);
Console.WriteLine($"{partes[0]} tirou {nota}");
soma += nota;
}
Console.WriteLine($"Média: {soma / linhas.Length:F2}");
CS0029: ReadAllText no lugar de ReadAllLines
string[] linhas = File.ReadAllText("notas.txt");
Console.WriteLine(linhas.Length);
$ dotnet run erro.cs /home/ana/cs/erro.cs(1,19): error CS0029: Não é possível converter implicitamente tipo "string" em "string[]" Ocorreu uma falha no build. Corrija os erros de build e execute novamente.
O que aconteceu: um erro de compilação (em inglês, Cannot implicitly convert type 'string' to 'string[]'). O ReadAllText devolve uma string, e a variável é um array de strings. Os nomes são parecidos, e o autocompletar do editor troca um pelo outro com facilidade.
Como resolver: para uma linha em cada posição, File.ReadAllLines. Para o texto inteiro, string texto = File.ReadAllText(…).
Referência rápida
| Código | O que faz |
|---|---|
File.WriteAllText(c, texto) | Cria ou substitui o arquivo com o texto |
File.WriteAllLines(c, lista) | Cria ou substitui, um item por linha |
File.AppendAllText(c, texto) | Acrescenta no fim (cria se não existir) |
File.AppendAllLines(c, lista) | Acrescenta linhas no fim |
File.ReadAllText(c) | Arquivo inteiro → string |
File.ReadAllLines(c) | Arquivo → string[], sem as quebras |
File.ReadLines(c) | Linhas uma por vez, para o foreach |
File.Exists(c) | true se o arquivo existe; nunca lança exceção |
File.Copy(a, b) / File.Copy(a, b, overwrite: true) | Copia; sem overwrite, recusa um destino existente |
File.Move(a, b) | Move ou renomeia |
File.Delete(c) | Apaga (sem lixeira); não reclama se o arquivo não existe |
File.ReadAllText(c, Encoding.Latin1) | Lê com outra codificação (using System.Text;) |
File.ReadAllTextAsync(c) | Versão assíncrona (aula async e await) |
Exercícios
1. O arquivo inteiro
Complete para ler o arquivo inteiro numa única string:
2. No fim, sem apagar
Complete para acrescentar uma linha no fim do log, mantendo o que já existe:
3. Copiar por cima
Complete para a cópia substituir o backup.txt, se ele já existir:
4. O arquivo log.txt tem 10 linhas. O que acontece com File.WriteAllText("log.txt", "oi\n")?
5. Um arquivo tem as linhas a, b e c, cada uma terminada por uma quebra de linha. Quanto vale File.ReadAllLines(…).Length?
6. Num computador em pt-BR, quanto vale double.Parse("2.5")?
Missão final: o relatório do estoque
Missão: o estoque.csv (na outra aba) tem, em cada linha, o produto, a quantidade e o preço (com ponto). O programa já lê o arquivo, pula o cabeçalho e calcula o valor de cada produto. Complete-o:
- dentro do laço, acrescente na lista
relatorioo texto$"{campos[0]}: {valor:F2}"e some ovalornototal; - depois do laço, acrescente
$"Total: {total:F2}"na lista; - grave a lista em
relatorio.txtcomFile.WriteAllLines; - leia o
relatorio.txtde volta comFile.ReadAllTexte mostre comConsole.Write.
A saída deve ser:
Caderno: 190,80 Caneta azul: 100,00 Cola branca: 34,00 Total: 324,80
using System.Globalization;
string[] linhas = File.ReadAllLines("estoque.csv");
List<string> relatorio = [];
decimal total = 0;
for (int i = 1; i < linhas.Length; i++)
{
string[] campos = linhas[i].Split(';');
int qtd = int.Parse(campos[1]);
decimal preco = decimal.Parse(campos[2], CultureInfo.InvariantCulture);
decimal valor = qtd * preco;
// 1. acrescente o produto na lista e some o valor no total
}
// 2. acrescente o total na lista
// 3. grave a lista em relatorio.txt
// 4. leia o relatorio.txt e mostre
produto;qtd;preco Caderno;12;15.90 Caneta azul;40;2.50 Cola branca;8;4.25
using System.Globalization;
string[] linhas = File.ReadAllLines("estoque.csv");
List<string> relatorio = [];
decimal total = 0;
for (int i = 1; i < linhas.Length; i++)
{
string[] campos = linhas[i].Split(';');
int qtd = int.Parse(campos[1]);
decimal preco = decimal.Parse(campos[2], CultureInfo.InvariantCulture);
decimal valor = qtd * preco;
relatorio.Add($"{campos[0]}: {valor:F2}");
total += valor;
}
relatorio.Add($"Total: {total:F2}");
File.WriteAllLines("relatorio.txt", relatorio);
Console.Write(File.ReadAllText("relatorio.txt"));
O preço está em decimal, o tipo certo para dinheiro (aula float, double e decimal), e o for começa em 1 para pular o cabeçalho produto;qtd;preco. Se quiser ir além: acrescente uma linha Régua;5;1.20 no CSV e veja o total mudar para 330,80.
Resumo
- A classe
File(namespaceSystem.IO, já incluído) lê e grava arquivos com uma linha de código. Um nome sem pasta, como"compras.txt", é procurado na pasta em que o programa foi rodado. WriteAllTexteWriteAllLinescriam ou substituem o arquivo.AppendAllTexteAppendAllLinesacrescentam no fim.ReadAllTextdevolve umastring;ReadAllLines, umstring[]sem as quebras. Os dois leem tudo de uma vez; para arquivos grandes,ReadLinesentrega uma linha por vez.Existspergunta sem lançar exceção;CopyeMoverecusam um destino existente semoverwrite: true;Deleteapaga de verdade.- O padrão é UTF-8, em que
ãocupa 2 bytes. Para um arquivo antigo, passe a codificação:Encoding.Latin1. - Erros comuns:
FileNotFoundException,DirectoryNotFoundException,IOExceptioneUnauthorizedAccessException. E números com ponto:double.Parse(texto, CultureInfo.InvariantCulture).