C#: E/S de arquivos
1. Classe estática de arquivo
A classe File oferece métodos estáticos para operações com arquivos, adequados para leituras/gravações únicas ou operações simples. Entre os métodos mais comuns estão ReadAllText, WriteAllText, AppendAllText, Exists, Copy, Move, Delete e ReadAllLines.
▶ Exemplo
using System;
using System.IO;
string path = "test.txt";
File.WriteAllText(path, "Hello, World!");
string content = File.ReadAllText(path);
Console.WriteLine(content);
File.AppendAllText(path, "\nAppended line.");
string[] lines = File.ReadAllLines(path);
foreach (string line in lines)
{
Console.WriteLine($"Line: {line}");
}
Console.WriteLine($"Exists: {File.Exists(path)}");
File.Copy(path, "copy.txt", overwrite: true);
File.Move("copy.txt", "moved.txt");
File.Delete("moved.txt");
File.Delete(path);
Hello, World!
Line: Hello, World!
Line: Appended line.
Exists: True
2. Classe estática de diretório
A classe Directory oferece métodos estáticos para operações com diretórios, incluindo a criação, a exclusão, a verificação de existência e a listagem de subdiretórios e arquivos.
▶ Exemplo
using System;
using System.IO;
string dir = "MyFolder";
Directory.CreateDirectory(dir);
Console.WriteLine($"Exists: {Directory.Exists(dir)}");
File.WriteAllText(Path.Combine(dir, "a.txt"), "aaa");
File.WriteAllText(Path.Combine(dir, "b.txt"), "bbb");
Directory.CreateDirectory(Path.Combine(dir, "SubDir"));
string[] files = Directory.GetFiles(dir);
Console.WriteLine("Files:");
foreach (string f in files)
{
Console.WriteLine($" {Path.GetFileName(f)}");
}
string[] dirs = Directory.GetDirectories(dir);
Console.WriteLine("Dirs:");
foreach (string d in dirs)
{
Console.WriteLine($" {Path.GetFileName(d)}");
}
Directory.Delete(dir, recursive: true);
Exists: True
Files:
a.txt
b.txt
Dirs:
SubDir
3. Classes FileInfo e DirectoryInfo
FileInfo e DirectoryInfo são classes de instância, adequadas para realizar várias operações no mesmo arquivo ou diretório. Elas evitam a validação repetida do caminho e oferecem melhor desempenho.
▶ Exemplo
using System;
using System.IO;
FileInfo fi = new FileInfo("demo.txt");
fi.WriteAllText("File info demo");
Console.WriteLine($"Name: {fi.Name}");
Console.WriteLine($"Length: {fi.Length}");
Console.WriteLine($"Extension: {fi.Extension}");
Console.WriteLine($"FullName: {fi.FullName}");
fi.Delete();
DirectoryInfo di = new DirectoryInfo("DemoDir");
di.Create();
Console.WriteLine($"Dir exists: {di.Exists}");
di.CreateSubdirectory("Child");
foreach (DirectoryInfo child in di.GetDirectories())
{
Console.WriteLine($"Sub: {child.Name}");
}
di.Delete(recursive: true);
Name: demo.txt
Length: 14
Extension: .txt
FullName: ...\demo.txt
Dir exists: True
Sub: Child
4. Classe abstrata Stream e FileStream
Stream é a classe-base abstrata para todos os fluxos, fornecendo os métodos Read, Write e Seek. FileStream é sua classe derivada, que realiza operações de leitura e gravação de baixo nível, no nível de bytes.
▶ Exemplo
using System;
using System.IO;
using System.Text;
string filePath = "data.bin";
using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
byte[] data = Encoding.UTF8.GetBytes("FileStream demo");
fs.Write(data, 0, data.Length);
}
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[fs.Length];
fs.Read(buffer, 0, buffer.Length);
Console.WriteLine(Encoding.UTF8.GetString(buffer));
}
File.Delete(filePath);
FileStream demo
5. StreamReader e StreamWriter
StreamReader e StreamWriter são leitores/gravadores orientados a texto que encapsulam o gerenciamento da codificação, tornando-os mais fáceis de usar do que o FileStream. O StreamReader lê linha por linha por meio do método ReadLine, e o StreamWriter grava por meio do método WriteLine.
▶ Exemplo
using System;
using System.IO;
string path = "text.txt";
using (StreamWriter writer = new StreamWriter(path))
{
writer.WriteLine("First line");
writer.WriteLine("Second line");
writer.Write("No newline here ");
writer.Write("appended.");
}
using (StreamReader reader = new StreamReader(path))
{
string? line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
File.Delete(path);
First line
Second line
No newline here appended.
6. BinaryReader e BinaryWriter
O BinaryReader e o BinaryWriter são usados para ler e gravar dados binários, oferecendo suporte a vários tipos primitivos (int, double, string, etc.). Eles são adequados para lidar com formatos que não sejam de texto.
▶ Exemplo
using System;
using System.IO;
string path = "binary.dat";
using (BinaryWriter bw = new BinaryWriter(File.Open(path, FileMode.Create)))
{
bw.Write(42);
bw.Write(3.14);
bw.Write("hello");
}
using (BinaryReader br = new BinaryReader(File.Open(path, FileMode.Open)))
{
int num = br.ReadInt32();
double d = br.ReadDouble();
string s = br.ReadString();
Console.WriteLine($"Int: {num}, Double: {d}, String: {s}");
}
File.Delete(path);
Int: 42, Double: 3.14, String: hello
7. Classe auxiliar de caminho
A classe Path fornece métodos estáticos para operações com caminhos, lidando com separadores de caminho de maneira multiplataforma para evitar erros decorrentes da concatenação manual de strings. Entre os métodos mais comuns estão Combine, GetExtension, GetFileName, GetDirectoryName e GetTempPath.
▶ Exemplo
using System;
using System.IO;
string fullPath = Path.Combine("folder", "sub", "file.txt");
Console.WriteLine($"Combine: {fullPath}");
Console.WriteLine($"Extension: {Path.GetExtension(fullPath)}");
Console.WriteLine($"FileName: {Path.GetFileName(fullPath)}");
Console.WriteLine($"FileNameNoExt: {Path.GetFileNameWithoutExtension(fullPath)}");
Console.WriteLine($"DirectoryName: {Path.GetDirectoryName(fullPath)}");
Console.WriteLine($"TempPath: {Path.GetTempPath()}");
Combine: folder\sub\file.txt
Extension: .txt
FileName: file.txt
FileNameNoExt: file
DirectoryName: folder\sub
TempPath: ...\Temp\
8. Uso de Statement e IDisposable
Recursos como arquivos e fluxos precisam ser liberados imediatamente. A instrução using garante que o método Dispose seja chamado, mesmo que ocorra uma exceção. O C# 8+ oferece suporte à sintaxe de declaração using var. Todas as classes de recursos que precisam de limpeza implementam a interface IDisposable.
▶ Exemplo
using System;
using System.IO;
string path = "using_demo.txt";
using (StreamWriter sw = new StreamWriter(path))
{
sw.WriteLine("Block using");
}
using StreamReader sr = new StreamReader(path);
Console.WriteLine(sr.ReadLine());
File.Delete(path);
Console.WriteLine(typeof(Stream).GetInterface("IDisposable") != null);
Block using
True
❓ Perguntas Frequentes
P: Devo usar File ou FileInfo? R: Para operações únicas, os métodos estáticos de File são mais concisos; para várias operações no mesmo arquivo, FileInfo oferece melhor desempenho.
P: Qual é a diferença entre FileStream e StreamReader? R: O FileStream lê e grava bytes; o StreamReader lê e grava texto com tratamento automático de codificação. É recomendável usar o StreamReader para operações com texto.
P: Os recursos serão liberados se ocorrer uma exceção dentro de uma instrução
using? R: Sim — a instruçãousingé compilada comotry/finally, o que chama o métodoDisposemesmo quando uma exceção é lançada.
P: Por que não se deve concatenar caminhos com strings? R: Sistemas operacionais diferentes utilizam separadores de caminho distintos. O Path.Combine lida com a concatenação de caminhos entre plataformas de maneira segura.
📖 Resumo
- A classe estática
Fileé adequada para operações únicas com arquivos - A classe estática
Directorygerencia a criação, a exclusão e a navegação por diretórios - As classes de instância FileInfo/DirectoryInfo são mais adequadas para operações múltiplas no mesmo alvo
- Stream é a classe-base abstrata para fluxos; FileStream oferece leitura e gravação em nível de byte
- StreamReader/StreamWriter encapsulam a codificação e simplificam a E/S de texto
- BinaryReader/BinaryWriter lidam com dados de tipos primitivos binários
- A classe
Pathlida com operações de caminho entre plataformas de maneira segura - A instrução
usinggarante que os recursosIDisposablesejam liberados imediatamente
📝 Exercícios
- Escreva um programa que utilize o método
File.WriteAllTextpara salvar um poema e, em seguida, leia e exiba esse poema com o métodoFile.ReadAllText - Use o método
Directory.CreateDirectorypara criar um diretório com vários níveis e, em seguida, percorra e exiba seus subdiretórios e arquivos - Use o StreamWriter para gravar 5 linhas de entradas de log; em seguida, use o StreamReader para ler linha por linha e imprimir os números das linhas
- Use o BinaryWriter para gravar um conjunto de valores do tipo int; em seguida, use o BinaryReader para lê-los novamente e calcular a soma
- Use o Path.Combine para unir um caminho e, em seguida, exiba o nome do arquivo, a extensão e o nome do diretório separadamente