Desafio C

Cópia de arquivo com tratamento de erros

Enunciado

Implemente um programa que copie o conteúdo de um arquivo de origem para um arquivo de destino. O programa deve receber os nomes dos arquivos como argumentos de linha de comando (./copia origem destino). Use fread e fwrite para copiar em blocos de 1024 bytes. Trate todos os possíveis erros: número incorreto de argumentos, falha ao abrir origem/destino, falha na leitura/escrita. Ao final, exiba uma mensagem indicando quantos bytes foram copiados.

Requisitos

  • Verificar se argc é igual a 3; caso contrário, exibir mensagem de uso e sair com código 1.
  • Abrir o arquivo de origem no modo "rb" e o de destino no modo "wb".
  • Usar um buffer de 1024 bytes e fread/fwrite em loop.
  • Verificar se fread retornou menos que o solicitado devido a erro (usar ferror).
  • Verificar se fwrite gravou todos os bytes lidos.
  • Fechar ambos os arquivos e imprimir o total de bytes copiados.

Código inicial

#include <stdio.h>
#include <stdlib.h>

#define BUFSIZE 1024

int main(int argc, char *argv[]) {
    // Seu código aqui
    return 0;
}

Saída esperada

Uso: ./copia origem destino
Ver dica

Após fread, se o retorno for menor que BUFSIZE, verifique feof e ferror. Para fwrite, compare o retorno com o número de bytes lidos.

Mostrar solução
#include <stdio.h>
#include <stdlib.h>

#define BUFSIZE 1024

int main(int argc, char *argv[]) {
    // Verifica argumentos
    if (argc != 3) {
        fprintf(stderr, "Uso: %s origem destino\n", argv[0]);
        return 1;
    }

    // Abre origem para leitura binária
    FILE *origem = fopen(argv[1], "rb");
    if (origem == NULL) {
        perror("Erro ao abrir origem");
        return 1;
    }

    // Abre destino para escrita binária
    FILE *destino = fopen(argv[2], "wb");
    if (destino == NULL) {
        perror("Erro ao abrir destino");
        fclose(origem);
        return 1;
    }

    unsigned char buffer[BUFSIZE];
    size_t total = 0;
    size_t lidos;

    // Loop de cópia
    while ((lidos = fread(buffer, 1, BUFSIZE, origem)) > 0) {
        size_t escritos = fwrite(buffer, 1, lidos, destino);
        if (escritos != lidos) {
            perror("Erro ao escrever no destino");
            fclose(origem);
            fclose(destino);
            return 1;
        }
        total += lidos;
    }

    // Verifica se a leitura parou por erro
    if (ferror(origem)) {
        perror("Erro ao ler da origem");
        fclose(origem);
        fclose(destino);
        return 1;
    }

    // Fecha arquivos
    fclose(origem);
    fclose(destino);

    printf("Bytes copiados: %zu\n", total);
    return 0;
}

Passo a passo

  1. Verificamos se argc é 3. Se não, imprimimos a mensagem de uso e retornamos 1.
  2. Abrimos o arquivo de origem com fopen(argv[1], "rb"). Se falhar, perror mostra o motivo e saímos.
  3. Abrimos o destino com fopen(argv[2], "wb"). Se falhar, fechamos a origem e saímos.
  4. Declaramos um buffer de 1024 bytes e variáveis para contagem.
  5. No loop, fread lê até 1024 bytes. Se retornar 0, pode ser fim de arquivo ou erro; saímos do loop.
  6. Para cada bloco lido, fwrite grava exatamente lidos bytes. Se escritos != lidos, houve erro de escrita; tratamos e saímos.
  7. Após o loop, verificamos ferror(origem) para distinguir fim de arquivo de erro de leitura.
  8. Fechamos ambos os arquivos e imprimimos o total de bytes copiados.

Por que funciona

A cópia em blocos é eficiente e funciona para qualquer tipo de arquivo (texto ou binário). Usar modo binário evita traduções indesejadas. Verificar fread e fwrite garante que erros de E/S sejam detectados. A verificação de ferror após o loop captura erros que não resultam em retorno 0.

Erros comuns

  • Não verificar o retorno de fwrite: se o disco encher, a gravação pode ser parcial. Compare escritos com lidos.
  • Usar while (!feof(origem)): isso causa uma iteração extra porque feof só é verdadeiro após uma tentativa de leitura falhar. Prefira while ((lidos = fread(...)) > 0).
  • Esquecer de fechar a origem se o destino falhar: vazamento de recurso. Sempre feche o que já foi aberto.
  • Não tratar ferror após o loop: um erro de leitura pode ser confundido com fim de arquivo.

Outra forma de resolver

Poderia usar fgetc/fputc para copiar byte a byte, mas seria muito mais lento. Para arquivos pequenos, é aceitável; para grandes, a cópia em blocos é preferível. Também é possível usar sendfile em sistemas POSIX, mas não é portável.

Saída esperada

Uso: ./copia origem destino