Aula 39a — Arquivos em C Parte 1 de 2

Persistência de dados — fopen, fprintf, fscanf e arquivos texto

1. Por que Precisamos de Arquivos?

Todos os programas criados até aqui têm um problema: quando a execução termina, todos os dados somem. Variáveis, structs, listas — tudo é destruído. A próxima vez que o usuário rodar o programa, começa do zero.

Arquivos resolvem isso. Eles permitem que dados sejam gravados em disco e recuperados na próxima execução — tornando o programa genuinamente útil no mundo real.

Em C, operações com arquivos são feitas através da biblioteca padrão <stdio.h> — a mesma de printf e scanf. A abstração central é o tipo FILE *: um ponteiro que representa um arquivo aberto.

Dois tipos de arquivo

Tipo Conteúdo Quando usar Abre no Bloco de Notas?
Texto Caracteres legíveis (letras, números, quebras de linha) Dados simples, configurações, logs ✅ Sim — legível
Binário Bytes brutos (representação interna da memória) Structs completas, dados grandes e eficientes ❌ Não — aparece como caracteres estranhos

Esta aula cobre arquivos texto. A Aula 39b cobre arquivos binários — usados no projeto da Aula 40.

2. Abrindo e Fechando: fopen e fclose

fopen(caminho, modo) abre um arquivo e retorna um ponteiro FILE *. Se o arquivo não puder ser aberto, retorna NULL. fclose(f) fecha o arquivo e garante que os dados pendentes sejam gravados em disco.
#include <stdio.h>

int main() {
    FILE *f;                                  // ponteiro para arquivo

    f = fopen("dados.txt", "w");              // abre para escrita

    if (f == NULL) {                          // SEMPRE verificar!
        printf("Erro ao abrir o arquivo.\n");
        return 1;
    }

    fprintf(f, "Olá, arquivo!\n");            // escreve no arquivo

    fclose(f);                                // SEMPRE fechar!
    printf("Arquivo gravado com sucesso.\n");
    return 0;
}

Modos de abertura

Modo Significado Arquivo não existe? Apaga conteúdo anterior?
"r"LeituraRetorna NULL (erro)Não apaga
"w"EscritaCria o arquivo✅ Apaga tudo
"a"Append — acrescenta ao finalCria o arquivoNão apaga
"r+"Leitura + escritaRetorna NULL (erro)Não apaga
"rb", "wb"Binário (leitura/escrita)Igual, mas modo binárioVer Aula 39b
⚠️ Dois erros clássicos com arquivos em C:

1. Não verificar se fopen retornou NULL. Se o caminho não existe ou o arquivo está bloqueado, fopen retorna NULL. Usar esse ponteiro sem verificar causa segfault imediato.

2. Não chamar fclose. Sem fechar, os dados podem não ser gravados em disco (ficam no buffer de I/O) e o arquivo pode ficar corrompido.

3. Escrevendo em Arquivo Texto: fprintf e fputs

fprintf funciona exatamente como printf — mas o primeiro argumento é o ponteiro de arquivo, não a tela.

#include <stdio.h>

int main() {
    FILE *f = fopen("alunos.txt", "w");
    if (f == NULL) { printf("Erro!\n"); return 1; }

    fprintf(f, "Nome: Ana Souza\n");
    fprintf(f, "Matrícula: %d\n", 1001);
    fprintf(f, "Nota: %.1f\n", 8.5f);
    fprintf(f, "---\n");
    fprintf(f, "Nome: Bruno Lima\n");
    fprintf(f, "Matrícula: %d\n", 1002);
    fprintf(f, "Nota: %.1f\n", 6.0f);

    fclose(f);
    printf("alunos.txt criado.\n");
    return 0;
}
Conteúdo gravado em alunos.txt:
Nome: Ana Souza
Matrícula: 1001
Nota: 8.5
---
Nome: Bruno Lima
Matrícula: 1002
Nota: 6.0
C — escrita em arquivo
FILE *f = fopen("out.txt", "w");
fprintf(f, "valor: %d\n", 42);
fclose(f);
Java — equivalente
FileWriter fw =
    new FileWriter("out.txt");
fw.write("valor: 42\n");
fw.close();

4. Lendo Arquivo Texto: fscanf e fgets

fscanf lê dados formatados de um arquivo — como scanf lê do teclado. fgets lê uma linha inteira, incluindo espaços.

fscanf — leitura formatada

#include <stdio.h>

int main() {
    FILE *f = fopen("numeros.txt", "r");
    if (f == NULL) { printf("Arquivo não encontrado.\n"); return 1; }

    int n;
    /* fscanf retorna o número de itens lidos — EOF (-1) quando o arquivo termina */
    while (fscanf(f, "%d", &n) == 1) {
        printf("Lido: %d\n", n);
    }

    fclose(f);
    return 0;
}

fgets — leitura linha a linha (mais segura para strings)

#include <stdio.h>
#include <string.h>

int main() {
    FILE *f = fopen("alunos.txt", "r");
    if (f == NULL) { printf("Erro!\n"); return 1; }

    char linha[200];

    /* fgets lê até 199 caracteres OU até \n — retorna NULL no fim do arquivo */
    while (fgets(linha, sizeof(linha), f) != NULL) {
        linha[strcspn(linha, "\n")] = '\0'; // remove o \n do final
        printf("[%s]\n", linha);
    }

    fclose(f);
    return 0;
}
fgets vs fscanf — quando usar: Use fgets para leitura linha a linha quando os dados têm espaços ou formato variável. Use fscanf para dados com formato fixo e conhecido. Para structs completas em formato eficiente, prefira arquivos binários (Aula 39b).

5. Exemplo Completo — Gravar e Reler um Cadastro

#include <stdio.h>

typedef struct {
    int   matricula;
    char  nome[50];
    float nota;
} Aluno;

int gravarAlunos(const char *caminho, Aluno v[], int n) {
    FILE *f = fopen(caminho, "w");
    if (f == NULL) return 0;

    fprintf(f, "%d\n", n);                   // 1ª linha: quantidade
    for (int i = 0; i < n; i++)
        fprintf(f, "%d %s %.1f\n",
                v[i].matricula, v[i].nome, v[i].nota);

    fclose(f);
    return 1;
}

int lerAlunos(const char *caminho, Aluno v[], int maxN) {
    FILE *f = fopen(caminho, "r");
    if (f == NULL) return -1;                // -1 = arquivo não existe

    int n;
    fscanf(f, "%d\n", &n);
    if (n > maxN) n = maxN;

    for (int i = 0; i < n; i++)
        fscanf(f, "%d %s %f\n",
               &v[i].matricula, v[i].nome, &v[i].nota);

    fclose(f);
    return n;
}

int main() {
    Aluno turma[] = {
        {1001, "Ana",   8.5f},
        {1002, "Bruno", 6.0f},
        {1003, "Carla", 9.5f}
    };

    if (gravarAlunos("turma.txt", turma, 3))
        printf("Dados gravados em turma.txt\n");

    Aluno recuperados[10];
    int total = lerAlunos("turma.txt", recuperados, 10);

    printf("\n=== Dados recuperados ===\n");
    for (int i = 0; i < total; i++)
        printf("  %d | %-15s | %.1f\n",
               recuperados[i].matricula,
               recuperados[i].nome,
               recuperados[i].nota);
    return 0;
}
Dados gravados em turma.txt

=== Dados recuperados ===
  1001 | Ana | 8.5
  1002 | Bruno | 6.0
  1003 | Carla | 9.5
⚠️ Limitação do arquivo texto com nomes compostos: fscanf("%s") para no primeiro espaço — "Ana Silva" seria lido apenas como "Ana". Para nomes com espaços, use fgets e parse manual, ou prefira arquivo binário (Aula 39b) que grava e lê a struct inteira de uma vez.

6. Acrescentando ao Final: modo "a"

O modo "a" (append) preserva o conteúdo existente e posiciona o cursor no final. Ideal para logs e históricos:

#include <stdio.h>

void registrarLog(const char *mensagem) {
    FILE *f = fopen("log.txt", "a");    // abre ou cria sem apagar
    if (f == NULL) return;
    fprintf(f, "%s\n", mensagem);
    fclose(f);
}

int main() {
    registrarLog("Programa iniciado");
    registrarLog("Usuário fez login");
    registrarLog("Busca realizada: matrícula 1001");
    registrarLog("Programa encerrado");
    printf("Log gravado em log.txt\n");
    return 0;
}

A cada nova execução do programa, novas linhas são adicionadas ao final — o histórico se acumula sem perder dados anteriores.

7. C vs. Java — Arquivos Texto

AspectoCJava
Biblioteca<stdio.h> (inclusa por padrão)java.io.*
Abrirfopen(caminho, modo)new FileWriter(...) / new BufferedReader(...)
Escreverfprintf(f, "...")fw.write("...")
Ler linhafgets(buf, tam, f)br.readLine()
Fecharfclose(f) — manual e obrigatóriotry-with-resources (automático)
Erro de aberturafopen retorna NULL — você verificaLança IOException — compilador força o tratamento
A grande diferença: Em Java, o compilador força o tratamento de exceções de I/O. Em C, verificar o retorno de fopen e chamar fclose é responsabilidade exclusiva do programador — esquecer é um dos erros mais comuns em código C real.

Quiz — Aula 39a

1. O que acontece quando fopen("dados.txt", "w") é chamado e o arquivo já existe?

2. Por que é essencial verificar se fopen retornou NULL?

3. Qual a diferença entre os modos "w" e "a" ao abrir um arquivo que já existe?

4. Por que fscanf(f, "%s", nome) é problemático para nomes como "Ana Silva"?