Departamento de Ciência da Computação - IME - USP

MAC2166 Introdução à Computação

Strings e manipulação de arquivos

 

Até agora, todos os programas feitos nesta disciplina lêem dados do teclado (entrada padrão) e escrevem na tela do monitor (saída padrão). Aqui é mostrado como um programa em C lê dados de um arquivo que está no "disco" (HD, PENDRIVE, . .  .) e como grava um arquivo no "disco". Um tratamento profundo do assunto, discutido brevemente aqui, pode ser encontrado no livro:

The C programming language
Brian W. Kernigham e Dennis M. Ritchie
Printice-Hall

Leitura e impressão de strings

Um string é uma seqüência de caracteres terminada pelo caractere especial '\0'. Um string é armazenado em um vetor de caracteres. Por exemplo, o trecho de programa abaixo armazena no vetor nome o string poli\0. Note que é preciso que o tamanho do vetor nome seja pelo menos 5 para armazenar, além dos 4 caracteres da palavra "poli", o caractere especial '\0'. No exemplo, o vetor tem tamanho 6 e o caractere nome[5] não foi utilizado.

char nome[6];

[...]
nome[0] = 'p';
nome[1] = 'o';
nome[2] = 'l';
nome[3] = 'i';
nome[4] = '\0';
[...]
Para imprimir um string, podemos usar o comando printf com %s. O caractere especial '\0' não é impresso.
[...]
printf("%s\n", nome);
[...]
Para ler um string, podemos usar o comando scanf com %s. O caractere especial '\0' é acrescentado automaticamente ao vetor de caracteres.
[...]
scanf("%s", nome);
[...]
Cuidado que, como um dos parâmetros do scanf é o vetor nome, não há & antes do nome, como ocorre sempre na chamada de uma função que tem um vetor como parâmetro.

Um cuidado especial deve ser tomado quando um string é lido. Embora um string possa conter normalmente o caractere ' ' (espaço), a leitura de um string via scanf é encerrada quando um espaço em branco é encontrado. Portanto, não usem espaços nos nomes dos arquivos a serem lidos no EP.

Um pouco mais sobre strings pode ser visto na página Cadeias de caracteres (strings) escrita por Paulo Feofiloff.

Abertura de um arquivo (fopen)

Neste ponto tratamos a seguinte questão:

Como fazer que um arquivo em disco, que tem o seu nome externo ao programa, é associado a algum objeto ou variável que é interna ou conhecida do nosso programa?
A regra é clara (como diria o Arnaldo Cézar Coelho :-) ). Antes que possa ser lido ou gravado um arquivo deve ser aberto pela função de biblioteca fopen. A função fopen associa o nome de uma arquivo externo ao programa a uma variável do programa. Isto é feito da seguinte maneira
FILE *arq_texto;
char nome[128];

[...]
/* o nome a ser digitado  não pode conter espaços em branco */
printf("Digite o nome do arquivo para leitura de dados: ");
scanf("%s", nome);
arq_texto = fopen(nome, "r");
[...]
O primeiro parâmetro da função fopen é um string contendo o nome do arquivo externo. Já o segundo parâmetro, que também é um string, indica o modo como se pretende usar o arquivo: leitura ("r"), gravação ("w"), anexação ("a"). Por exemplo o comando
FILE *arq_ent;
char nome[128];

[...]
printf("Digite o nome do arquivo para leitura de dados: ");
scanf("%s", nome);
arq_ent = fopen(nome, "r");
if (arq_ent == NULL)
  {
    printf("Um erro ocorreu ao tentar abrir o arquivo %s.\n", nome);
    return 1; /* abandona a execucao do programa */
  }
[...]
associa à variável arq_ent do nosso programa o arquivo em disco correspondente ao nome que foi digitado. O arquivo de nome nome deve estar na mesma pasta (diretório, folder) que o nosso programa, digamos, o ep4.exe. O arquivo é aberto para leitura ("r" de read). Se ocorrer um erro, como tentar associar arq_ent a um arquivo que não existe, fopen retorna o valor representado pela constante NULL.

Se um arquivo que não existe no disco for aberto para escrita, ele é criado. Por exemplo, o seguinte trecho de código cria um arquivo:

FILE *arq_saida;
char nome[128];

[...]
printf("Digite o nome do arquivo para impressao de dados: ");
scanf("%s", nome);
arq_saida = fopen(nome, "w");
if (arq_saida == NULL)
  {
    printf("Um erro ocorreu ao tentar criar o arquivo %s.\n", nome);
    return 1; /* abandona a execucao do programa */
  }
[...]
O arquivo de nome nome é criado na mesma pasta/diretório que o arquivo que contém o nosso programa. Se o arquivo já existe ele é destruído e um novo arquivo com o mesmo nome é criado. O arquivo é aberto para escrita ("w" de write).

Sempre que um erro ocorre durante a abertura de um arquivo o valor devolvido pela função fopen é NULL.

Leitura de um arquivo (fscanf)

O próximo passo é saber como ler os dados de uma arquivo. Considere que um arquivo foi associado à variável arq_ent do nosso programa. O comando

FILE *arq_ent;
char nome[128];

[...]
arq_ent = fopen(nome, "r");
[...]
fscanf(arq_ent, "%d", &matriz[i][j]);
[...]
lê um número inteiro do arquivo da mesma maneira que
[...]
scanf("%d", &num);
[...]
lê um número digitado através do teclado. Na verdade, o comando
scanf("%d",&num);
é uma abreviatura do comando
fscanf(stdin, "%d", &num);
onde stdin é a entrada padrão, que no nosso caso, é o teclado.

Escrita em arquivo (fprintf)

Para escrever-se em um arquivo pode-se usar os seguintes comandos

FILE *arq_sai;
char nome[128];
[...]
arq_sai = fopen(nome, "w");
[...]
fprintf(arq_sai, "arquivo de saida\n");
fprintf(arq_sai, "%d %d\n", no_colunas, no_linhas);
[...]
O primeiro fprintf escreve o texto "arquivo de saida\n" no arquivo e o segundo fprintf escreve o conteúdo da variável no_colunas um espaco (" "), o conteúdo da variável no_linhas e um barra-n ("\n") para mudar de linha. Veja um exemplo um pouco mais elaborado:
#define MAX        800

FILE *arq_sai;
char nome[128];

int  matriz[MAX][MAX];
int no_linhas;  /* numero de linhas da matriz  */
int no_colunas; /* numero de colunas da matriz */
[...]
arq_sai = fopen(nome, "w");
if (arq_sai == NULL)
  {
    printf("Um erro ocorreu ao tentar criar o arquivo %s.\n", nome);
    return 1; /* abandona a execucao do programa */
  }
[...]
fprintf(arq_sai, "arquivo de saida\n");
fprintf(arq_sai, "%d %d\n", no_colunas, no_linhas);
[...]
/* escreva matriz no arquivo */
for (i = 0; i < no_linhas; i++)
  {
    for (j = 0; j < no_colunas; j++)
      {
        fprintf(arq_sai, " %d ", matriz[i][j]);
      }
    fprintf(arq_sai, "\n");
  }
[...]

De maneira semelhante ao que ocorre com o scanf, o comando

printf("%d\n", matriz[i][j]);
é uma abreviatura do comando
fprintf(stdout, "%d\n", matriz[i][j]);
onde stdout é a saída padrão, que no nosso caso, é a tela do monitor.

Fechamento de um arquivo (fclose)

A função fclose encerra a associação estabelecida pelo programa entre uma variável e o nome externo do arquivo. O fclose também libera os recursos do sistema que controlam a manipulação do arquivo em disco. A chamada abaixo fecha o arquivo associado à variável arq.
fclose(arq);

Determinação fim de um arquivo (feof)

A função feof recebe uma variável associada a um arquivo e devolve zero se durante a leitura dos dados no arquivo ainda não chegamos ao seu final. Se a leitura já chegou ao final do arquivo a função feof devolve um valor qualquer diferente de zero. O trecho de programa a seguir exibe o conteúdo de um arquivo na tela do monitor:
FILE *arq_ent;
char nome[128];
char ch;

[...]
printf("Digite o nome do arquivo para leitura de dados: ");
scanf("%s", nome);
arq_ent = fopen(nome, "r");
if (arq_ent == NULL)
  {
    printf("Um erro ocorreu ao tentar abrir o arquivo %s.\n", nome);
    return 1; /* abandona a execucao do programa */
  }

fscanf(arq_ent,"%c", &ch);
while (feof(arq_ent) == 0) /* enquanto nao terminou o arquivo */
  {
    printf("%c", ch);
    fscanf(arq_ent,"%c", &ch);
  }

fclose(arq_ent);
[...]

Exemplo

A seguir está um exemplo de programa que copia uma matriz de inteiros de uma arquivo de nome original.dat para um arquivo de nome copia.dat. Os dois primeiros números do arquivo original.dat indicam o número m de linhas e o número n de colunas da matriz. O número de linhas e colunas da matriz não podem ser superior a 1000; ou seja, a matriz terá no máximo 1000*1000==1000000 números.
/*
 * Programa que que faz uma copia de uma matriz em um arquivo
 * de nome original.dat para um arquivo de nome copia.dat. O
 * arquivo copia.dat ficara no mesmo diretorio do arquivo
 * original.dat.
 *
 */


#include <stdio.h>
#define MAX 1000

int main()
{
  FILE *arq_o; /* associado ao arquivo original */
  char nome_o[128]; /* armazena o nome do arquivo original */
  FILE *arq_c; /* associado ao arquivo copia */
  char nome_c[128]; /* armazena o nome do arquivo copia */
  int m; /* numero de linhas da matriz */
  int n; /* numero de colunas da matriz */
  int i, j; /* indices da matriz */
  int matriz[MAX][MAX];

  /* 1. leia o nome do arquivo original */
  printf("Digite o nome do arquivo original: ");
  /* Neste exemplo, digite "original.dat" */
  scanf("%s", nome_o);

  /* 2. abra arquivo original.dat para leitura */
  arq_o = fopen(nome_o, "r");
  if (arq_o == NULL)
    {
      printf("Erro na abertura do arquivo %s.\n", nome_o);
      return 1;  /* abandona a execucao do programa */
    }

  /* 3. leia o nome do arquivo copia */
  printf("Digite o nome do arquivo copia: ");
  /* Neste exemplo, digite "copia.dat" */
  scanf("%s", nome_c);

  /* 4. abra arquivo copia.dat para escrita */
  arq_c = fopen(nome_c, "w");
  if (arq_c == NULL)
    {
      printf("Erro na abertura do arquivo %s.\n", nome_c);
      fclose(arq_o); /* feche o arquivo original.dat */
      return 1; /* abandona a execucao do programa */
    }

  /* 5. leia as dimensoes da matriz do arquivo original.dat */
  fscanf(arq_o, "%d %d", &m, &n);

  /* 6. escreva as dimensoes da matriz no arquivo copia.dat */
  fprintf(arq_c, "%d %d\n", m, n);


  /* 7. leia a matriz do arquivo original.dat */
  for (i = 0; i < m; i++)
    {
       /* leia a linha i */
      for (j = 0; j < n; j++)
        {
          fscanf(arq_o, "%d", &matriz[i][j]);
        }
    }

  /* 8. escreva a matriz no arquivo copia.dat */
  for (i = 0; i < m; i++)
    {
       /* escreva a linha i */
      for (j = 0; j < n; j++)
        {
          fprintf(arq_c, "%d ", matriz[i][j]);
        }
      fprintf(arq_c, "\n"); 
    }

  /* 9. feche o arquivo original.dat */
  fclose(arq_o);

  /* 10. feche o arquivo copia.dat */
  fclose(arq_c);

  return 0;

}

EXERCICIO. Reescreva o programa acima sem usar uma matriz. É necessário limitar a dimensão da matriz?


[Página principal de MAC2166.]
Last modified: Tue May 29 14:10:54 BRT 2012