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

CSHARP
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);
▶ Experimente
TEXT 📖 Somente leitura
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

CSHARP
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);
▶ Experimente
TEXT 📖 Somente leitura
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

CSHARP
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);
▶ Experimente
TEXT 📖 Somente leitura
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

CSHARP
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);
▶ Experimente
TEXT 📖 Somente leitura
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

CSHARP
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);
▶ Experimente
TEXT 📖 Somente leitura
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

CSHARP
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);
▶ Experimente
TEXT 📖 Somente leitura
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

CSHARP
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()}");
▶ Experimente
TEXT 📖 Somente leitura
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

CSHARP
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);
▶ Experimente
TEXT 📖 Somente leitura
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ção using é compilada como try/finally, o que chama o método Dispose mesmo 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

📝 Exercícios

  1. Escreva um programa que utilize o método File.WriteAllText para salvar um poema e, em seguida, leia e exiba esse poema com o método File.ReadAllText
  2. Use o método Directory.CreateDirectory para criar um diretório com vários níveis e, em seguida, percorra e exiba seus subdiretórios e arquivos
  3. 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
  4. 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
  5. 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
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%