Mudanças entre as edições de "AULA 22 - Programação 1 - Engenharia"

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
 
(111 revisões intermediárias por 5 usuários não estão sendo mostradas)
Linha 1: Linha 1:
===Objetivos===
+
=Objetivos=
  
*Tratamento de arquivos no C
+
Após esta aula o aluno terá:
*O sistema de arquivos no Linux
 
*O acesso com funções de alto nível
 
  
===O sistema de arquivos no Linux===
+
*Noções básicas do sistema de arquivos no Linux
 +
*Capacidade de desenvolver aplicações simples em C e que fazem acesso a arquivos com funções de alto nível da biblioteca.
 +
 
 +
=O sistema de arquivos no Linux=
 +
 
 +
*[http://wiki.inf.ufpr.br/maziero/lib/exe/fetch.php?media=socm:socm-22.pdf O que é um Arquivo]
  
 
====O Arquivo====
 
====O Arquivo====
Linha 14: Linha 17:
 
Normalmente, arquivos são armazenados em memórias secundárias, tais como CD, hard disk etc, mas eles podem se armazenados na memória principal também (RAM, FLASH).
 
Normalmente, arquivos são armazenados em memórias secundárias, tais como CD, hard disk etc, mas eles podem se armazenados na memória principal também (RAM, FLASH).
  
Quanto a forma como os dados são armazenados, podemos dizer que os arquivos são [http://en.wikipedia.org/wiki/Binary_file binários] ou [http://en.wikipedia.org/wiki/Text_file texto]. Qualquer um deles armazena bits mas os bytes de um arquivo texto representam códigos ASCII.
+
Quanto a forma como os dados são armazenados, podemos dizer que os arquivos são [http://en.wikipedia.org/wiki/Binary_file binários] ou [http://en.wikipedia.org/wiki/Text_file texto]. Qualquer um deles armazena bits mas os bytes de um arquivo texto devem ser interpretados como códigos ASCII e são organizados em linhas.
 +
 
 +
Se abrir um arquivo texto com o "vi" pode-se "ver" os bytes armazenados no arquivo:
 +
 
 +
vi /etc/passwd
 +
 
 +
Entrar em modo comando (SHIFT :) e fazer:
 +
:%!xxd
 +
 
 +
'''Ao abrir um arquivo a partir de um programa em C, pode-se ter comportamentos diferentes se optar por abrir em modo texto ou binário'''
  
 
====Sistema de Arquivos====
 
====Sistema de Arquivos====
Linha 23: Linha 35:
 
Nesta área ficam armazenados blocos de armazenamento dos dados dos arquivos e também as estruturas chamadas de '''inodes'''.
 
Nesta área ficam armazenados blocos de armazenamento dos dados dos arquivos e também as estruturas chamadas de '''inodes'''.
  
Um [http://upload.wikimedia.org/wikipedia/commons/a/a2/Ext2-inode.gif inode] é um estrutura que possui as propriedades do arquivo e ponteiros para os blocos que contém os dados do arquivo. Tipicamente um sistema de arquivos possui uma lista de inodes que permite "indexar" cada um dos arquivos do sistema de arquivos.
+
Um [https://en.wikipedia.org/wiki/Inode_pointer_structure inode] é um estrutura que possui as propriedades do arquivo e ponteiros para os blocos que contém os dados do arquivo. Tipicamente um sistema de arquivos possui uma lista de inodes que permite "indexar" cada um dos arquivos do sistema de arquivos.
  
 
Existem vários formatos de sistema de arquivos, dependendo do sistema operacional. No linux os formatos mais conhecidos são: ext2, ext3, ext4 etc.
 
Existem vários formatos de sistema de arquivos, dependendo do sistema operacional. No linux os formatos mais conhecidos são: ext2, ext3, ext4 etc.
Linha 29: Linha 41:
 
Um sistema de arquivos normalmente possui uma estrutura de dados inicial chamada de superbloco. O superbloco traz informações sobre o tamanho de blocos do sistema, o início da lista de inodes (/), etc.
 
Um sistema de arquivos normalmente possui uma estrutura de dados inicial chamada de superbloco. O superbloco traz informações sobre o tamanho de blocos do sistema, o início da lista de inodes (/), etc.
  
<code>
+
<syntaxhighlight>
 
+-------------+-----------------+------------------------+
 
+-------------+-----------------+------------------------+
 
| superbloco  | lista de inodes | blocos dos arquivos    |
 
| superbloco  | lista de inodes | blocos dos arquivos    |
 
+-------------+-----------------+------------------------+
 
+-------------+-----------------+------------------------+
 
</syntaxhighlight>
 
</syntaxhighlight>
 
  
 
====Diretórios====
 
====Diretórios====
Linha 67: Linha 78:
 
Como consequência, a partir de um programa em C, é possível abrir e escrever/ler em um dispositivo (desde que se tenha autorização).
 
Como consequência, a partir de um programa em C, é possível abrir e escrever/ler em um dispositivo (desde que se tenha autorização).
  
===Acessando arquivos a partir de programas C===
+
=Acessando arquivos a partir de programas C=
 +
 
 +
*[http://www.ic.unicamp.br/~oliveira/doc/mc102_2s2004/aula16.pdf Ver link da Aula Prof.Rodrigo de Oliveira]
  
 
====Acesso a arquivos: funções de baixo e alto nível====
 
====Acesso a arquivos: funções de baixo e alto nível====
  
 
Em sistemas do porte do Linux e Windows, por questões de segurança e controle, todo acesso a arquivo é realizado através de código do sistema operacional. Desta forma, um programa que deseja acessar um arquivo deve gerar uma CHAMADA AO SISTEMA.
 
Em sistemas do porte do Linux e Windows, por questões de segurança e controle, todo acesso a arquivo é realizado através de código do sistema operacional. Desta forma, um programa que deseja acessar um arquivo deve gerar uma CHAMADA AO SISTEMA.
O Linux (assim como outros sistemas) possui uma API (Application Programming Interface) bem definida, na forma de um conjunto de chamadas que permitem realizar uma série de funcionalidades (serviços) para os processos(programas em execução).
+
O Linux (assim como outros sistemas) possui uma API (Application Programming Interface) bem definida, na forma de um conjunto de chamadas que permitem realizar uma série de funcionalidades (serviços) para os processos (programas em execução).
  
 
Um programa em C realiza uma CHAMADA AO SISTEMA com auxílio de funções da biblioteca C (a glib no caso do Linux).
 
Um programa em C realiza uma CHAMADA AO SISTEMA com auxílio de funções da biblioteca C (a glib no caso do Linux).
Linha 82: Linha 95:
  
 
O acesso em alto nível é realizado usando uma estrutura do tipo [http://www.gnu.org/software/libc/manual/html_node/Streams.html#Streams FILE] definida no stdio.h.
 
O acesso em alto nível é realizado usando uma estrutura do tipo [http://www.gnu.org/software/libc/manual/html_node/Streams.html#Streams FILE] definida no stdio.h.
Todo acesso passa inicialmente por abrir o arquivo (função fopen), ler/escrever (várias funções, tipo fread(), fwrite()) e fechar o arquivo (fclose()).
+
Todo acesso passa inicialmente por abrir o arquivo (função fopen), ler/escrever (várias funções, tipo fread(), fwrite()) e fechar o arquivo (fclose()). As principais funções são mostradas na tabela abaixo.
  
 +
{| class="wikitable"
 +
! função
 +
! Descrição
 +
! Comentário
 +
|-
 +
| fopen
 +
| abrir arquivo
 +
| retorna um ponteiro para FILE dado o nome do arquivo
 +
|-
 +
| fclose
 +
| fechar arquivo descrito por FILE
 +
|
 +
|-
 +
| fprintf
 +
| escrever no arquivo de forma formatada
 +
|
 +
|-
 +
| fscanf
 +
| ler de um arquivo de forma formatada
 +
| A função retorna o "define" (um inteiro) EOF (end-of-file) quando não existe mais dados a serem lidos.
 +
|-
 +
| fputc
 +
| escrever um caracter do arquivo
 +
|
 +
|-
 +
| fgetc
 +
| ler um caracter do arquivo
 +
|
 +
|-
 +
| fputs
 +
| escrever string no arquivo
 +
|
 +
|-
 +
| fgets
 +
| ler string no arquivo
 +
|
 +
|-
 +
| fwrite
 +
| escrever dados de forma não formatada
 +
|
 +
|-
 +
| fread
 +
| ler dados de forma não formada
 +
|
 +
|}
  
=====Exemplo 1: Escrevendo e lendo um arquivo texto de forma formatada=====
+
==Escrevendo e lendo um arquivo texto de forma formatada==
  
 
Nos exemplos que se seguem, serão usadas as funções [http://www.cplusplus.com/reference/cstdio/fopen/ fopen] e [http://www.cplusplus.com/reference/cstdio/fclose/?kw=fclose fclose], para abrir e fechar arquivo e [http://www.cplusplus.com/reference/cstdio/fprintf/?kw=fprintf fprintf()] e [http://www.cplusplus.com/reference/cstdio/fscanf/?kw=fscanf fscanf()] para leitura e escrita.
 
Nos exemplos que se seguem, serão usadas as funções [http://www.cplusplus.com/reference/cstdio/fopen/ fopen] e [http://www.cplusplus.com/reference/cstdio/fclose/?kw=fclose fclose], para abrir e fechar arquivo e [http://www.cplusplus.com/reference/cstdio/fprintf/?kw=fprintf fprintf()] e [http://www.cplusplus.com/reference/cstdio/fscanf/?kw=fscanf fscanf()] para leitura e escrita.
A forma é similar ao ''printf'' e ''scanf''(ver http://diuf.unifr.ch/pai/ip/tutorials/c/TC02_scanf.pdf), a não ser pelo fato de que a escrita e leitura é realizada no arquivo indicado
+
A forma é similar ao ''printf'' e ''scanf'', a não ser pelo fato de que a escrita e leitura é realizada no arquivo indicado
 
por  ''p_arq''
 
por  ''p_arq''
 +
 +
  Observar que um arquivo que está sendo acessado possui uma posição corrente de acesso de forma que uma nova leitura ou escrita sempre avança para "frente" nos dados do arquivo.
 +
 +
===Escrevendo de forma formatada com fprintf===
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
 +
#include <stdlib.h>
  
void main()
+
/*escrevendo no arquivo IFSC.txt*/
 +
int main()
 
{
 
{
 
   FILE *p_arq;
 
   FILE *p_arq;
Linha 101: Linha 165:
 
   if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
 
   if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
 
     printf("Problemas na abertura do arquivo\n");
 
     printf("Problemas na abertura do arquivo\n");
     return;
+
     exit(EXIT_FAILURE);
 
   }
 
   }
  
Linha 107: Linha 171:
 
   /* A funcao fprintf devolve o número de bytes gravados  ou EOF se houve erro na gravação */
 
   /* A funcao fprintf devolve o número de bytes gravados  ou EOF se houve erro na gravação */
 
       if((res = fprintf(p_arq,"Linha %d\n",i))==EOF) {         
 
       if((res = fprintf(p_arq,"Linha %d\n",i))==EOF) {         
        printf("Erro\n");
+
          printf("Erro\n");
  break;
+
          break;
 
       }
 
       }
 
   }
 
   }
 
   fclose(p_arq);
 
   fclose(p_arq);
 +
  return EXIT_SUCCESS;
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
  Note que se o arquivo IFSC.txt não existir, ele será criado.
 
  Note que se o arquivo IFSC.txt não existir, ele será criado.
Para ver o que fois escrito faça:
+
Para ver o que foi escrito no arquivo faça:
 
  cat IFSC.txt
 
  cat IFSC.txt
 +
Use o vi para ver os bytes armazenados no arquivo IFSC.TXT.
 +
 +
===Lendo de forma formata com fscanf===
 +
 +
Para ler o arquivo IFSC.txt pode-se usar o fscanf().
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
 +
#include <stdlib.h>
  
void main()
+
/*lendo do arquivo IFSC.txt*/
 +
int main()
 
{
 
{
 
   FILE *p_arq;
 
   FILE *p_arq;
Linha 131: Linha 203:
 
   if ((p_arq=fopen("IFSC.txt", "r")) == NULL) {
 
   if ((p_arq=fopen("IFSC.txt", "r")) == NULL) {
 
     printf("Problemas na abertura do arquivo\n");
 
     printf("Problemas na abertura do arquivo\n");
     return;
+
     exit(EXIT_FAILURE);
 
   }
 
   }
  
 
   for (i = 0; i<10;i++) {
 
   for (i = 0; i<10;i++) {
 
       if((res = fscanf(p_arq,"%s %d",buff,&j))==EOF) {         
 
       if((res = fscanf(p_arq,"%s %d",buff,&j))==EOF) {         
        printf("Fim de leitura\n");
+
        printf("Fim de leitura\n");
  break;
+
        break;
 
       }
 
       }
 
       printf("%s %d\n",buff,j);
 
       printf("%s %d\n",buff,j);
 
   }
 
   }
 
   fclose(p_arq);
 
   fclose(p_arq);
 +
  return EXIT_SUCCESS;
 +
}
 +
</syntaxhighlight>
 +
 +
Note que o fscanf se comporta de forma similar ao scanf. A função retorna o define EOF (end-of-file) quando não existe mais dados a serem lidos.
 +
 +
{{collapse top | Exemplo 1: lendo conteúdo com fscanf}}
 +
Neste exemplo o programa abre um arquivo texto chamado data.txt  que contém uma linha com dia, mês e ano separado por espaço em branco. O arquivo deve ser criado previamente.
 +
 +
Crie o arquivo data.txt e coloque dados dentro da forma:
 +
10/03/2021
 +
15/04/2022
 +
 +
<syntaxhighlight lang=c>
 +
#include<stdio.h>
 +
 +
int main (void){
 +
 +
  int dia, mes, ano;
 +
  FILE *arq;
 +
 +
  arq = fopen("data.txt", "r");
 +
  if (arq != NULL)
 +
    printf("Arquivo aberto com sucesso, continuando...\n");
 +
  else
 +
    printf("Não foi possível abrir o arquivo\n");
 +
 +
  fscanf(arq, "%d/%d/%d", &dia, &mes, &ano);
 +
 +
  printf("O dia eh: %d/%d/%d\n", dia, mes, ano);
 +
 +
  fclose(arq);
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
{{collapse bottom}}
 +
 +
{{collapse top | Exemplo 2: escrevendo e lendo conteúdo em arquivos}}
 +
 +
O programa abaixo escreve a data (ver exemplo anterior) em um arquivo. Ele pode ser usado conjuntamente com o programa do exercício anterior para inserir dados em um arquivo.
 +
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <stdlib.h>
  
Note que o fscanf se comporta de forma similar ao scanf. A função retorna o caracter EOF (end-of-file) quando não existe mais dados a serem lidos.
+
int main (void){
  
*Exercícios
+
  FILE *pont_arq; // cria variável ponteiro para o arquivo
 +
  char palavra[20]; // variável do tipo string
  
#Implementar uma função que soma duas matrizes fornecidas em dois arquivos texto separados: MatA.dat e MatB.dat. Colocar o resultado em um arquivo chamado MatC.dat.
+
  //abrindo o arquivo com tipo de abertura w
 +
  pont_arq = fopen("arquivo_palavra.txt", "w"); //modo padrão de abertura do arquivo é texto (adicionar b se quiser binário - Ex "rb", "wb", etc)
  
 +
  //testando se o arquivo foi realmente criado
 +
  if (pont_arq != NULL)
 +
    printf("Arquivo aberto com sucesso, continuando...\n");
 +
  else
 +
    printf("Não foi possível abrir o arquivo\n");
 +
 +
  printf("Escreva uma palavra para testar gravacao de arquivo: ");
 +
  scanf("%[^\n]s", palavra); // [^\n] ler espaço como caracter válido
 +
 +
  //escreve no arquivo texto:"2/5/2006"
 +
  fprintf(pont_arq, "%d/%d/%d\n", 2, 5, 2006);
 +
 +
  //usando fprintf para armazenar a string no arquivo
 +
  fprintf(pont_arq, "%s\n", palavra);
 +
 +
  //usando fclose para fechar o arquivo
 +
  fclose(pont_arq);
 +
 +
  printf("Dados gravados com sucesso\n!");
 +
 +
  return(0);
 +
}
 +
</syntaxhighlight>
 +
{{collapse bottom}}
 +
 +
<!--
 +
{{collapse top | Exemplo 3: escrevendo e lendo conteúdo em arquivos}}
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
+
#include <stdlib.h>
void main()
+
 
 +
/*escrevendo no arquivo IFSC.txt*/
 +
int main(void)
 
{
 
{
 +
  FILE *p_arq;
 +
  int i;
 +
  int res;
 +
 +
  if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
 +
    printf("Problemas na abertura do arquivo\n");
 +
    exit(EXIT_FAILURE);
 +
  }
 +
 +
  for (i = 0; i<10;i++) {
 +
  /* A funcao fprintf devolve o número de bytes gravados  ou EOF se houve erro na gravação */
 +
      if((res = fprintf(p_arq,"Linha %d\n",i)) == EOF) {
 +
  printf("Erro\n");
 +
  break;
 +
      }
 +
  }
 +
  fclose(p_arq);
 +
  return EXIT_SUCCESS;
 +
}
 +
</syntaxhighlight>
 +
{{collapse bottom}}
 +
-->
 +
 +
===Exercício 1===
 +
 +
Implementar um programa que soma duas matrizes fornecidas em dois arquivos texto separados: MatA.dat e MatB.dat. Colocar o resultado em um arquivo chamado MatC.dat.
 +
 +
{{collapse top | solução Ex. 1}}
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <stdlib.h>
 +
 +
void main(void){
 +
 
   FILE *p_arq;
 
   FILE *p_arq;
 
   int i,j,res;
 
   int i,j,res;
 
   float MatA[5][5], MatB[5][5], MatR[5][5];
 
   float MatA[5][5], MatB[5][5], MatR[5][5];
 
+
 
 
   /* Ler a matriz MatA do arquivo */
 
   /* Ler a matriz MatA do arquivo */
 
   if ((p_arq=fopen("MatA.dat", "r")) == NULL) {
 
   if ((p_arq=fopen("MatA.dat", "r")) == NULL) {
Linha 165: Linha 343:
 
     return;
 
     return;
 
   }
 
   }
 
+
 
 
   for (i =0;i<5;i++) {
 
   for (i =0;i<5;i++) {
 
       for (j=0;j<5;j++) {
 
       for (j=0;j<5;j++) {
       if((res = fscanf(p_arq,"%f",&MatA[i][j]))==EOF){    
+
       if((res = fscanf(p_arq,"%f",&MatA[i][j]))==EOF){
 
        printf("Fim de leitura\n");
 
        printf("Fim de leitura\n");
 
break;
 
break;
Linha 177: Linha 355:
 
   }
 
   }
 
   fclose(p_arq);
 
   fclose(p_arq);
 
+
 
 
   /* Ler a matriz MatB do arquivo */
 
   /* Ler a matriz MatB do arquivo */
  
 
   printf("\n\n");
 
   printf("\n\n");
     
+
 
 
   /* Ler a matriz MatB do arquivo */
 
   /* Ler a matriz MatB do arquivo */
 
   if ((p_arq=fopen("MatB.dat", "r")) == NULL) {
 
   if ((p_arq=fopen("MatB.dat", "r")) == NULL) {
 
     printf("Problemas na abertura do arquivo\n");
 
     printf("Problemas na abertura do arquivo\n");
    return;
+
      exit(EXIT_FAILURE);
 
   }
 
   }
 
+
 
 
   for (i =0;i<5;i++) {
 
   for (i =0;i<5;i++) {
 
       for (j=0;j<5;j++) {
 
       for (j=0;j<5;j++) {
       if((res = fscanf(p_arq,"%f",&MatB[i][j]))==EOF){    
+
       if((res = fscanf(p_arq,"%f",&MatB[i][j]))==EOF){
 
        printf("Fim de leitura\n");
 
        printf("Fim de leitura\n");
 
break;
 
break;
Linha 199: Linha 377:
 
   }
 
   }
 
   fclose(p_arq);
 
   fclose(p_arq);
   
+
 
 
   /* Calcular a soma das matrizes e colocar resultado em MatR */
 
   /* Calcular a soma das matrizes e colocar resultado em MatR */
  
Linha 206: Linha 384:
 
       MatR[i][j]=MatA[i][j]+MatB[i][j];
 
       MatR[i][j]=MatA[i][j]+MatB[i][j];
 
       }
 
       }
   }  
+
   }
   printf("Resultado da Adição\n");  
+
   printf("Resultado da Adição\n");
 
   for (i =0;i<5;i++) {
 
   for (i =0;i<5;i++) {
 
       for (j=0;j<5;j++) {
 
       for (j=0;j<5;j++) {
           printf("%f ", MatR[i][j]);
+
           printf("%f ", MatR[i][j]);
 
       }
 
       }
       printf("\n");    
+
       printf("\n");
   }
+
   }
 
   /* Armazenar MatR em arquivo */
 
   /* Armazenar MatR em arquivo */
  
Linha 219: Linha 397:
 
     printf("Problemas na abertura do arquivo\n");
 
     printf("Problemas na abertura do arquivo\n");
 
     return;
 
     return;
   }  
+
   }
 
+
 
 
   for (i =0;i<5;i++) {
 
   for (i =0;i<5;i++) {
 
       for (j=0;j<5;j++) {
 
       for (j=0;j<5;j++) {
         if((res = fprintf(p_arq,"%f ",MatR[i][j]))==EOF) {    
+
         if((res = fprintf(p_arq,"%f ",MatR[i][j]))==EOF) {
 
        printf("erro\n");
 
        printf("erro\n");
 
break;
 
break;
Linha 230: Linha 408:
 
       fprintf(p_arq,"\n");
 
       fprintf(p_arq,"\n");
 
   }
 
   }
   fclose(p_arq);       
+
   fclose(p_arq);
 +
}
 +
</syntaxhighlight>
 +
{{collapse bottom}}
 +
 
 +
===Exemplo de gravação na forma texto de uma tabela com estruturas ===
 +
 
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <string.h>
 +
#include <stdlib.h>
 +
 
 +
#define TAM_TAB2 3
 +
 
 +
 
 +
struct tipo_registro2{
 +
  char nome[30];
 +
  int idade;
 +
  float nota_final;
 +
} tab_registros[TAM_TAB2] = {
 +
       {"alfa da silva", 10, 9.5},
 +
      {"beta da silva", 30, 6.9},
 +
      {"epson da silva", 20, 7.5},
 +
  };
 +
 
 +
/*
 +
  Esta função mostra como salvar a tabela de estruturas na forma de um arquivo texto contendo uma estrutura por lionha. Os campos são separados com :
 +
*/
 +
 
 +
void salvar_am_arq()
 +
{
 +
  FILE *ptr;
 +
  int i;
 +
 
 +
  if ((ptr = fopen("teste.dat", "w+"))== NULL) {
 +
    printf("Erro na abertura de arquivo\n");
 +
    exit(-1);
 +
  }
 +
  for (i=0;i<TAM_TAB2;i++)
 +
    fprintf (ptr, "%s:%d:%f\n", tab_registros[i].nome, tab_registros[i].idade, tab_registros[i].nota_final);
 +
  fclose(ptr);
 +
}
 +
 
 +
/*
 +
  Esta função mostra como recuperar a tabela de estruturas na forma de um arquivo texto contendo uma estrutura por linha. Os campos são separados com :
 +
*/
 +
void restaurar_tab_do_arq()
 +
{
 +
  char buffer[100+1];
 +
  FILE *ptr;
 +
  int ret=0,i=0;
 +
 
 +
  if ((ptr = fopen("teste.dat", "r"))== NULL) {
 +
    printf("Erro na abertura de arquivo\n");
 +
    exit(-1);
 +
  }
 +
  while  (ret != EOF) {
 +
    ret = fscanf (ptr, " %[^:]:%d:%f", tab_registros[i].nome, &tab_registros[i].idade, &tab_registros[i].nota_final);
 +
    if (ret==3) { /* ret contém o número de itens lidos */
 +
        printf("REGISTRO %d %s %d %f\n",i,tab_registros[i].nome, tab_registros[i].idade, tab_registros[i].nota_final);
 +
    } else if (ret!=EOF)
 +
        printf("Provavelmente existe um erro no arrquivo de dados\n");
 +
    i++;
 +
  }
 +
fclose(ptr);
 +
}
 +
 
 +
int main(void){
 +
  salvar_am_arq();
 +
  restaurar_tab_do_arq();
 +
  return 0;
 +
 
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
====Exemplo2====
 
  
Neste exemplo usaremos as funções fgetc e fputc para ler e cescrever caracteres ASCII nos arquivos. Seja um programa que conta o número de ocorrências do caracter 'a' em um arquivo e reescreve o arquivo sem os "as":
+
==Escrevendo e Lendo usando fgets e fputs==
  
 +
====Exercício 1====
 +
 +
Neste exemplo usaremos as funções fgetc e fputc para ler e escrever caracteres ASCII nos arquivos. Seja um programa que conta o número de ocorrências do caracter 'a', remova esses caracteres 'a' e salve os caracteres em um novo arquivo destino:
 +
 +
{{collapse top | solução Ex. 2}}
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <stdio.h>
 
#include <stdio.h>
Linha 260: Linha 513:
 
       else
 
       else
 
if((x=fputc(x,fp_destino))==EOF) {
 
if((x=fputc(x,fp_destino))==EOF) {
puts("Não conseguiu abrir o arquivo\n");
+
puts("Não conseguiu escrever no arquivo\n");
 
exit(1);
 
exit(1);
 
}
 
}
Linha 269: Linha 522:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
{{collapse bottom}}
  
 
  Note que ''fputc'' recebe o caracter como um inteiro mas realiza um ''cast'' para
 
  Note que ''fputc'' recebe o caracter como um inteiro mas realiza um ''cast'' para
 
  ''unsigned char'' com fins de escrevê-lo no arquivo.
 
  ''unsigned char'' com fins de escrevê-lo no arquivo.
 +
 +
 +
 +
{{collapse top | Exemplo 4: escrevendo e lendo string em arquivos}}
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <stdlib.h>
 +
 +
int main (void){
 +
 +
  FILE *pont_arq;
 +
  char texto_str[20];
 +
 +
  //abrindo o arquivo_frase em modo "somente leitura"
 +
  pont_arq = fopen("arquivo_palavra.txt", "r");
 +
 +
  //enquanto não for fim de arquivo o looping será executado
 +
  //e será impresso o texto
 +
  while(fgets(texto_str, 20, pont_arq) != NULL)
 +
    printf("%s", texto_str);
 +
 +
  //fechando o arquivo
 +
  fclose(pont_arq);
 +
 +
  return(0);
 +
}
 +
</syntaxhighlight>
 +
{{collapse bottom}}
  
 
====Exemplo 3====
 
====Exemplo 3====
  
Neste exemplo vamos explorar o modo de abertura do arquivo para fazer um append (escrever no final do arquivo).
+
Neste exemplo vamos explorar o modo de abertura do arquivo para fazer um append (escrever no final do arquivo). Ver time() e ctime() em http://www.cplusplus.com/reference/ctime/.
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
 
#include <time.h>
 
#include <time.h>
 
#include <stdio.h>
 
#include <stdio.h>
 +
#include <stdlib.h>
 
   
 
   
 
int main(void)
 
int main(void)
Linha 287: Linha 570:
 
   int num;
 
   int num;
  
 +
  /* ler a hora/data do sistema e armazenar em ltime */
 
   time(&ltime);  
 
   time(&ltime);  
  
 
   if ((fp=fopen("DATA.txt", "a")) == NULL) {
 
   if ((fp=fopen("DATA.txt", "a")) == NULL) {
 
       printf("Problemas na abertura do arquivo\n");
 
       printf("Problemas na abertura do arquivo\n");
       return;
+
       exit(EXIT_FAILURE);
 
   }
 
   }
 +
  /* escrever hora/data lida em arquivo, mas converter antes para string (ctime) */
 
   if ((num = fputs( ctime(&ltime), fp )) != EOF ) {
 
   if ((num = fputs( ctime(&ltime), fp )) != EOF ) {
 
       fclose(fp);
 
       fclose(fp);
Linha 298: Linha 583:
 
       printf("Erro na escrita do arquivo!\n");
 
       printf("Erro na escrita do arquivo!\n");
 
   }
 
   }
 +
  return (EXIT_SUCCESS);
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
Linha 311: Linha 597:
 
*Exercício: Substitua o modo de append por uma simples escrita (w). Reexecute o programa conforme especificado anteriormente.
 
*Exercício: Substitua o modo de append por uma simples escrita (w). Reexecute o programa conforme especificado anteriormente.
  
===Ainda funções de acesso a arquivos===
+
 
 +
 
 +
==Exemplo de gravação/leitura de uma tabela de estruturas na forma binária usando fwrite e fread==
 +
 
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <string.h>
 +
#include <stdlib.h>
 +
 +
#define TAM_TAB2 3
 +
 +
 +
struct tipo_registro2{
 +
  char nome[30];
 +
  int idade;
 +
  float nota_final;
 +
} tab_registros[TAM_TAB2] = {
 +
      {"alfa da silva", 10, 9.5},
 +
      {"beta da silva", 30, 6.9},
 +
      {"epson da silva", 20, 7.5},
 +
  };
 +
 
 +
  struct tipo_registro2 aux[TAM_TAB2];
 +
/*
 +
  Esta função mostra como salvar a tabela de estruturas na forma de um arquivo texto contendo uma estrutura por lionha. Os campos são separados com :
 +
*/
 +
 
 +
void salvar_am_arq()
 +
{
 +
  FILE *ptr;
 +
  int i;
 +
 +
  if ((ptr = fopen("teste.dat", "w+"))== NULL) {
 +
    printf("Erro na abertura de arquivo\n");
 +
    exit(-1);
 +
  }
 +
  fwrite (tab_registros, sizeof(struct tipo_registro2),TAM_TAB2,ptr);
 +
  fclose(ptr);
 +
}
 +
 
 +
/*
 +
  Esta função mostra como recuperar a tabela de estruturas na forma de um arquivo texto contendo uma estrutura por linha. Os campos são separados com :
 +
*/
 +
void restaurar_tab_do_arq()
 +
{
 +
  char buffer[100+1];
 +
  FILE *ptr;
 +
  int ret;
 +
 +
  if ((ptr = fopen("teste.dat", "r"))== NULL) {
 +
    printf("Erro na abertura de arquivo\n");
 +
    exit(-1);
 +
  }
 +
  ret = fread(aux,sizeof(struct tipo_registro2),TAM_TAB2,ptr);
 +
  printf("número de itens lidos = %d\n", ret);
 +
  fclose(ptr);
 +
}
 +
 +
main()
 +
{
 +
  salvar_am_arq();
 +
  restaurar_tab_do_arq();
 +
  printf("%s\n", aux[2].nome);
 +
}
 +
</syntaxhighlight>
 +
 
 +
==Exemplo de gravação de uma tabela com estruturas contendo ponteiros==
 +
 
 +
Observe no exemplo abaixo que um vetor de estruturas (tabela) contendo ponteiros pode ter problemas no seu salvamento, caso as áreas apontadas não sejam gravadas.
 +
A tabela tab_registros2 se for gravada com salvar_em_arq2() perderá dados pois os dados apontados por nome não serão salvos com fwrite.
 +
Pode-se contornar este problema gravando cada estrutura em uma linha de forma formatada, separada por delimitadores.  Ver função salvar_am_arq2_vOK().
 +
A recuperação dos dados deve prever a reconstituição da tabela (ver restaurar_tab_do_arq2()).
 +
 
 +
Note o significado das formatações usadas no scanf/fscanf: [https://en.wikipedia.org/wiki/Scanf_format_string]
 +
*qualquer caracter em branco na string de formato faz com que a função ignore quaisquer espaço em branco, newline, tab ou qualquee
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
#include <string.h>
 +
#include <stdlib.h>
 +
 
 +
#define TAM_TAB2 3
 +
 
 +
 
 +
struct tipo_registro2{
 +
  char *nome;
 +
  int idade;
 +
} tab_registros2[TAM_TAB2] = {
 +
      {"alfa", 10},
 +
      {"beta", 30},
 +
      {"epson", 20},
 +
  };
 +
 
 +
void salvar_em_arq2()
 +
{
 +
  /* PROBLEMAS... - a estrutura tem ponteiros... */
 +
  FILE *ptr;
 +
  ptr =fopen("teste2.dat", "w+");
 +
  fwrite(tab_registros2, sizeof(struct tipo_registro2),TAM_TAB2,ptr);
 +
  fclose(ptr);
 +
}
 +
 
 +
void salvar_am_arq2_vOK()
 +
{
 +
  FILE *ptr;
 +
  int i;
 +
 
 +
  ptr =fopen("teste2.dat", "w+");
 +
  for (i=0;i<TAM_TAB2;i++)
 +
    fprintf (ptr, "%s:%d\n", tab_registros2[i].nome, tab_registros2[i].idade);
 +
  fclose(ptr);
 +
}
 +
 
 +
 
 +
void print_tab2()
 +
{
 +
  int i;
 +
 
 +
  for (i=0;i<TAM_TAB2;i++) {
 +
    printf ("%s:%d\n", tab_registros2[i].nome, tab_registros2[i].idade);
 +
  }
 +
}
 +
 
 +
void restaurar_tab_do_arq2()
 +
{
 +
  char buffer[100+1];
 +
  FILE *ptr;
 +
  int ret,i=0;
 +
 
 +
  ptr =fopen("teste2.dat", "r");
 +
  while  (ret!=EOF) {
 +
    ret=fscanf (ptr, " %[^:]:%d", buffer,&tab_registros2[i].idade);
 +
    if (ret==2) {
 +
        tab_registros2[i].nome=(char *)malloc(strlen(buffer));
 +
        strcpy(tab_registros2[i].nome, buffer);
 +
        printf("i=%d ret=%d %s %d\n",i,ret,tab_registros2[i].nome, tab_registros2[i].idade);
 +
    } else if (ret!=EOF)
 +
        printf("Provavelmente existe um erro no arrquivo de dados\n");
 +
    i++;
 +
  }
 +
fclose(ptr);
 +
}
 +
 
 +
main()
 +
{
 +
  salvar_am_arq2_vOK();
 +
  restaurar_tab_do_arq2();
 +
  print_tab2();
 +
  /* a fazer = liberar areas alocadas dinamicamente */
 +
}
 +
</syntaxhighlight>
 +
 
 +
==Ainda funções de acesso a arquivos - posicionando cursor sobre o arquivo==
  
 
A localização corrente do acesso a um arquivo pode ser modificada antes de uma leitura ou escrita. Tipicamente, quando se abre
 
A localização corrente do acesso a um arquivo pode ser modificada antes de uma leitura ou escrita. Tipicamente, quando se abre
 
uma arquivo para leitura/escrita, o "cursor" de acesso é 0, ou seja início do arquivo. Se o arquivo for aberto em modo append, o "cursor" é posicionado no final. A cada acesso (leitura ou esrita), este cursor é incrementado conforme o número de dados lidos ou escritos.
 
uma arquivo para leitura/escrita, o "cursor" de acesso é 0, ou seja início do arquivo. Se o arquivo for aberto em modo append, o "cursor" é posicionado no final. A cada acesso (leitura ou esrita), este cursor é incrementado conforme o número de dados lidos ou escritos.
  
É possível entretanto modificar a posição deste cursor, tipicamente com as funções ''fseek()'' e ''rewind()''. (ver http://www.gnu.org/software/libc/manual/html_node/File-Positioning.html#File-Positioning)
+
É possível entretanto modificar a posição deste cursor, tipicamente com as funções ''fseek()'' e ''rewind()''. (ver [http://www.gnu.org/software/libc/manual/html_node/File-Positioning.html#File-Positioning)]).
  
 
Considere o programa abaixo que escreve um vetor de 100 inteiros em um arquivo.
 
Considere o programa abaixo que escreve um vetor de 100 inteiros em um arquivo.
Linha 341: Linha 778:
  
 
Agora observe o programa seguinte que lê especificamente a posição 90 e na sequência restabelece o cursor no início. Note que o fread lê somente um inteiro na posição corrente do arquivo. A posição corrente foi determinada por ''fseek'' e depois retornada para o início com ''rewind''.
 
Agora observe o programa seguinte que lê especificamente a posição 90 e na sequência restabelece o cursor no início. Note que o fread lê somente um inteiro na posição corrente do arquivo. A posição corrente foi determinada por ''fseek'' e depois retornada para o início com ''rewind''.
 
  
 
<syntaxhighlight lang=c>
 
<syntaxhighlight lang=c>
Linha 353: Linha 789:
  
 
   fp = fopen("teste.dat","r");
 
   fp = fopen("teste.dat","r");
   fseek(fp, 90*sizeof(int), SEEK_CUR);
+
   fseek(fp, 90*sizeof(int), SEEK_SET);
 
   fread(&y, sizeof(int),1,fp);
 
   fread(&y, sizeof(int),1,fp);
 
   printf("y=%d\n", y);
 
   printf("y=%d\n", y);
Linha 363: Linha 799:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Execício
+
===Exercício===
 +
 
 +
Escreva um programa que receba do usuário 5 números inteiros e o nome do arquivo no qual eles devem ser armazenados. Em seguida, ler do arquivo estes valores armazenados copiando-os para um vetor de inteiros e imprimindo na tela.
 +
 
 +
{{collapse top | solução Ex. 1}}
 +
<syntaxhighlight lang=c>
 +
#include <stdio.h>
 +
 
 +
int main(){
 +
 
 +
FILE *arq;
 +
int i, temp, vet[5];
 +
char nome_arq[20];
 +
 
 +
printf("Informe o nome do arquivo: ");
 +
scanf("%[^\n]s",nome_arq);
 +
 
 +
if ((arq = fopen (nome_arq, "w+"))== NULL){
 +
  printf("Erro ao abrir o arquivo %s", nome_arq);
 +
  return (1);
 +
}
 +
 
 +
for (i=0; i<5 ; i++){
 +
  printf("informe um valor inteiro: ");
 +
  scanf("%d", &temp);
 +
  fprintf(arq, "%d", temp);
 +
  if (i < 4)
 +
    fprintf(arq, "\n");
 +
}
 +
rewind(arq);  //Retorna o indicador de posição do ponteiro FILE em relação ao arquivo para o seu início.
 +
 
 +
i = 0;
 +
while ( !feof(arq) ){    //Retorna zero se o final do arquivo foi atingido e um valor não nulo caso contrário.
 +
  fscanf (arq, "%d", &vet[i]);
 +
  printf("%d\n",vet[i]);
 +
  i++;
 +
}
 +
fclose(arq);
 +
return 0;
 +
}
 +
</syntaxhighlight>
 +
{{collapse bottom}}
  
Considero o programa abaixo. Ele deve acessar uma tabela que se encontra em um arquivo binário. Cada item da tabela
+
<!--
 +
Considere o programa abaixo. Ele deve acessar uma tabela que se encontra em um arquivo binário. Cada item da tabela
 
se apresenta conforme o registro TRegistro. Implemente a função ''LerTab'' e crie um programa para escrever uma tabela iniciada com
 
se apresenta conforme o registro TRegistro. Implemente a função ''LerTab'' e crie um programa para escrever uma tabela iniciada com
um tabela de 5 registros a fim de testar a função implementada.
+
uma tabela de 5 registros a fim de testar a função implementada.
  
 
<syntaxhighlight lang= c>
 
<syntaxhighlight lang= c>
Linha 377: Linha 855:
 
} Registro, *pAux;
 
} Registro, *pAux;
  
struct Tregistro *LerTab(int indice)
+
struct TRegistro *LerTab(int indice)
 
{
 
{
 
}
 
}
Linha 389: Linha 867:
 
}
 
}
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
-->

Edição atual tal como às 09h14min de 15 de março de 2022

Objetivos

Após esta aula o aluno terá:

  • Noções básicas do sistema de arquivos no Linux
  • Capacidade de desenvolver aplicações simples em C e que fazem acesso a arquivos com funções de alto nível da biblioteca.

O sistema de arquivos no Linux

O Arquivo

Um arquivo é um conjunto de dados que pode ser referenciado por um nome e pode ter outros atributos tais como: permissão para leitura e escrita, data da criação, data da última modificação etc.

Note que um arquivo pode conter dados (um relatório, por exemplo), um programa C, um programa executável, música, fotos etc. Seja qual for a natureza dos dados o armazenamento será na forma de bits.

Normalmente, arquivos são armazenados em memórias secundárias, tais como CD, hard disk etc, mas eles podem se armazenados na memória principal também (RAM, FLASH).

Quanto a forma como os dados são armazenados, podemos dizer que os arquivos são binários ou texto. Qualquer um deles armazena bits mas os bytes de um arquivo texto devem ser interpretados como códigos ASCII e são organizados em linhas.

Se abrir um arquivo texto com o "vi" pode-se "ver" os bytes armazenados no arquivo:
 	
vi /etc/passwd
Entrar em modo comando (SHIFT :) e fazer:
:%!xxd

Ao abrir um arquivo a partir de um programa em C, pode-se ter comportamentos diferentes se optar por abrir em modo texto ou binário

Sistema de Arquivos

Um sistema tal como o Linux possui milhares de arquivos. Para que arquivos possam ser acessados e armazenados de forma consistente, eles são organizados em um sistema de arquivos.

Tipicamente, um sistema de arquivos ocupa uma área de um disco (ou mídia de armazenamento). Nesta área ficam armazenados blocos de armazenamento dos dados dos arquivos e também as estruturas chamadas de inodes.

Um inode é um estrutura que possui as propriedades do arquivo e ponteiros para os blocos que contém os dados do arquivo. Tipicamente um sistema de arquivos possui uma lista de inodes que permite "indexar" cada um dos arquivos do sistema de arquivos.

Existem vários formatos de sistema de arquivos, dependendo do sistema operacional. No linux os formatos mais conhecidos são: ext2, ext3, ext4 etc.

Um sistema de arquivos normalmente possui uma estrutura de dados inicial chamada de superbloco. O superbloco traz informações sobre o tamanho de blocos do sistema, o início da lista de inodes (/), etc.

+-------------+-----------------+------------------------+
| superbloco  | lista de inodes | blocos dos arquivos    |
+-------------+-----------------+------------------------+

Diretórios

São arquivos especiais que contém basicamente uma lista contendo nome e inode correspondente dos arquivos que o diretório contém.

Em um sistema de arquivos, o diretório / é o diretório raiz do sistema, a partir do qual pode-se encontrar todos os demais arquivos.

Referência a um arquivo

A localização de um arquivo pode ser realizada pela referência absoluta, ou seja, desde o diretório / do sistema:

cat /etc/passwd

O comando cat tem por objetivo mostrar no terminal o conteúdo do arquivo passwd. Para que este arquivo seja encontrado na árvore de diretórios, deve-se fornecer a referência absoluta, desde o diretório /

Uma alternativa de acesso, é o uso da referência relativa ao diretório de trabalho. O conceito de diretório de trabalho é criado pelo interpretador de comandos (shell). Desta forma pode-se por exemplo fazer o comando:

cat passwd

O sistema procurará o arquivo passwd dentro do diretório de trabalho, que é referenciado armazenado pelo shell. Neste caso, o diretório de trabalho deveria ser /etc

cd /etc
cat passwd
No Linux/Unix tudo é arquivo

No Linux, qualquer referência/acesso ao hardware/dispositivos é realizada na forma de acesso a arquivo. Ver arquivos no diretório de dispositivos:

 ls -l /dev

Como consequência, a partir de um programa em C, é possível abrir e escrever/ler em um dispositivo (desde que se tenha autorização).

Acessando arquivos a partir de programas C

Acesso a arquivos: funções de baixo e alto nível

Em sistemas do porte do Linux e Windows, por questões de segurança e controle, todo acesso a arquivo é realizado através de código do sistema operacional. Desta forma, um programa que deseja acessar um arquivo deve gerar uma CHAMADA AO SISTEMA. O Linux (assim como outros sistemas) possui uma API (Application Programming Interface) bem definida, na forma de um conjunto de chamadas que permitem realizar uma série de funcionalidades (serviços) para os processos (programas em execução).

Um programa em C realiza uma CHAMADA AO SISTEMA com auxílio de funções da biblioteca C (a glib no caso do Linux). Duas categorias de funções para acesso a arquivo são disponibilizadas (ver detalhes aqui):

  • Funções de baixo nível: acessam diretamente o sistema;
  • Funções de alto nível: as funções intermediam o acesso, criando buffers no espaço de endereçamento do processo. As funções de baixo nível são invocadas para ler e escrever dados no buffer.

Acesso a arquivo em MODO TEXTO através de funções de alto nível

O acesso em alto nível é realizado usando uma estrutura do tipo FILE definida no stdio.h. Todo acesso passa inicialmente por abrir o arquivo (função fopen), ler/escrever (várias funções, tipo fread(), fwrite()) e fechar o arquivo (fclose()). As principais funções são mostradas na tabela abaixo.

função Descrição Comentário
fopen abrir arquivo retorna um ponteiro para FILE dado o nome do arquivo
fclose fechar arquivo descrito por FILE
fprintf escrever no arquivo de forma formatada
fscanf ler de um arquivo de forma formatada A função retorna o "define" (um inteiro) EOF (end-of-file) quando não existe mais dados a serem lidos.
fputc escrever um caracter do arquivo
fgetc ler um caracter do arquivo
fputs escrever string no arquivo
fgets ler string no arquivo
fwrite escrever dados de forma não formatada
fread ler dados de forma não formada

Escrevendo e lendo um arquivo texto de forma formatada

Nos exemplos que se seguem, serão usadas as funções fopen e fclose, para abrir e fechar arquivo e fprintf() e fscanf() para leitura e escrita. A forma é similar ao printf e scanf, a não ser pelo fato de que a escrita e leitura é realizada no arquivo indicado por p_arq

  Observar que um arquivo que está sendo acessado possui uma posição corrente de acesso de forma que uma nova leitura ou escrita sempre avança para "frente" nos dados do arquivo. 

Escrevendo de forma formatada com fprintf

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

/*escrevendo no arquivo IFSC.txt*/
int main()
{
  FILE *p_arq;
  int i;
  int res;

  if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
     printf("Problemas na abertura do arquivo\n");
     exit(EXIT_FAILURE);
  }

  for (i = 0; i<10;i++) {
  /* A funcao fprintf devolve o número de bytes gravados  ou EOF se houve erro na gravação */
      if((res = fprintf(p_arq,"Linha %d\n",i))==EOF) {  					  	    
          printf("Erro\n");
          break;
      }
  }
  fclose(p_arq);
  return EXIT_SUCCESS;
}
Note que se o arquivo IFSC.txt não existir, ele será criado.

Para ver o que foi escrito no arquivo faça:

cat IFSC.txt

Use o vi para ver os bytes armazenados no arquivo IFSC.TXT.

Lendo de forma formata com fscanf

Para ler o arquivo IFSC.txt pode-se usar o fscanf().

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

/*lendo do arquivo IFSC.txt*/
int main()
{
  FILE *p_arq;
  int i,j;
  int res;
  char buff[100];

  if ((p_arq=fopen("IFSC.txt", "r")) == NULL) {
     printf("Problemas na abertura do arquivo\n");
     exit(EXIT_FAILURE);
  }

  for (i = 0; i<10;i++) {
      if((res = fscanf(p_arq,"%s %d",buff,&j))==EOF) {  					  	    
         printf("Fim de leitura\n");
         break;
      }
      printf("%s %d\n",buff,j);
  }
  fclose(p_arq);
  return EXIT_SUCCESS;
}

Note que o fscanf se comporta de forma similar ao scanf. A função retorna o define EOF (end-of-file) quando não existe mais dados a serem lidos.

Exemplo 1: lendo conteúdo com fscanf

Neste exemplo o programa abre um arquivo texto chamado data.txt que contém uma linha com dia, mês e ano separado por espaço em branco. O arquivo deve ser criado previamente.

Crie o arquivo data.txt e coloque dados dentro da forma:

10/03/2021
15/04/2022
#include<stdio.h>

int main (void){

  int dia, mes, ano;
  FILE *arq;

  arq = fopen("data.txt", "r");
  if (arq != NULL)
    printf("Arquivo aberto com sucesso, continuando...\n");
  else
    printf("Não foi possível abrir o arquivo\n");

  fscanf(arq, "%d/%d/%d", &dia, &mes, &ano);

  printf("O dia eh: %d/%d/%d\n", dia, mes, ano);

  fclose(arq);
}
Exemplo 2: escrevendo e lendo conteúdo em arquivos

O programa abaixo escreve a data (ver exemplo anterior) em um arquivo. Ele pode ser usado conjuntamente com o programa do exercício anterior para inserir dados em um arquivo.

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

int main (void){

  FILE *pont_arq; // cria variável ponteiro para o arquivo
  char palavra[20]; // variável do tipo string

  //abrindo o arquivo com tipo de abertura w
  pont_arq = fopen("arquivo_palavra.txt", "w"); //modo padrão de abertura do arquivo é texto (adicionar b se quiser binário - Ex "rb", "wb", etc)

  //testando se o arquivo foi realmente criado
  if (pont_arq != NULL)
    printf("Arquivo aberto com sucesso, continuando...\n");
  else
    printf("Não foi possível abrir o arquivo\n");

  printf("Escreva uma palavra para testar gravacao de arquivo: ");
  scanf("%[^\n]s", palavra); // [^\n] ler espaço como caracter válido

  //escreve no arquivo texto:"2/5/2006"
  fprintf(pont_arq, "%d/%d/%d\n", 2, 5, 2006);

  //usando fprintf para armazenar a string no arquivo
  fprintf(pont_arq, "%s\n", palavra);

  //usando fclose para fechar o arquivo
  fclose(pont_arq);

  printf("Dados gravados com sucesso\n!");

  return(0);
}


Exercício 1

Implementar um programa que soma duas matrizes fornecidas em dois arquivos texto separados: MatA.dat e MatB.dat. Colocar o resultado em um arquivo chamado MatC.dat.

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

void main(void){

  FILE *p_arq;
  int i,j,res;
  float MatA[5][5], MatB[5][5], MatR[5][5];

  /* Ler a matriz MatA do arquivo */
  if ((p_arq=fopen("MatA.dat", "r")) == NULL) {
     printf("Problemas na abertura do arquivo\n");
     return;
  }

  for (i =0;i<5;i++) {
      for (j=0;j<5;j++) {
      	if((res = fscanf(p_arq,"%f",&MatA[i][j]))==EOF){
	         printf("Fim de leitura\n");
			 break;
      	}
      	printf("%f ",MatA[i][j]);
      }
      printf("\n");
  }
  fclose(p_arq);

  /* Ler a matriz MatB do arquivo */

  printf("\n\n");

  /* Ler a matriz MatB do arquivo */
  if ((p_arq=fopen("MatB.dat", "r")) == NULL) {
     printf("Problemas na abertura do arquivo\n");
       exit(EXIT_FAILURE);
  }

  for (i =0;i<5;i++) {
      for (j=0;j<5;j++) {
      	if((res = fscanf(p_arq,"%f",&MatB[i][j]))==EOF){
	         printf("Fim de leitura\n");
			 break;
      	}
      	printf("%f ",MatB[i][j]);
      }
      printf("\n");
  }
  fclose(p_arq);

  /* Calcular a soma das matrizes e colocar resultado em MatR */

  for (i =0;i<5;i++) {
      for (j=0;j<5;j++) {
      	MatR[i][j]=MatA[i][j]+MatB[i][j];
      }
  }
  printf("Resultado da Adição\n");
  for (i =0;i<5;i++) {
      for (j=0;j<5;j++) {
          printf("%f ", MatR[i][j]);
      }
      printf("\n");
  }
  /* Armazenar MatR em arquivo */

  if ((p_arq=fopen("MatR.dat", "w")) == NULL) {
     printf("Problemas na abertura do arquivo\n");
     return;
  }

  for (i =0;i<5;i++) {
      for (j=0;j<5;j++) {
         if((res = fprintf(p_arq,"%f ",MatR[i][j]))==EOF) {
	         printf("erro\n");
			 break;
      	 }
      }
      fprintf(p_arq,"\n");
  }
  fclose(p_arq);
}

Exemplo de gravação na forma texto de uma tabela com estruturas

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

#define TAM_TAB2 3


struct tipo_registro2{
   char nome[30];
   int idade;
   float nota_final;
} tab_registros[TAM_TAB2] = {
      {"alfa da silva", 10, 9.5},
      {"beta da silva", 30, 6.9},
      {"epson da silva", 20, 7.5},
   };

/*
  Esta função mostra como salvar a tabela de estruturas na forma de um arquivo texto contendo uma estrutura por lionha. Os campos são separados com :
*/

void salvar_am_arq()
{
  FILE *ptr;
  int i;

  if ((ptr = fopen("teste.dat", "w+"))== NULL) {
     printf("Erro na abertura de arquivo\n");
     exit(-1);
  }
  for (i=0;i<TAM_TAB2;i++)
     fprintf (ptr, "%s:%d:%f\n", tab_registros[i].nome, tab_registros[i].idade, tab_registros[i].nota_final);
  fclose(ptr);
}

/*
  Esta função mostra como recuperar a tabela de estruturas na forma de um arquivo texto contendo uma estrutura por linha. Os campos são separados com :
*/
void restaurar_tab_do_arq()
{
  char buffer[100+1];
  FILE *ptr;
  int ret=0,i=0;

  if ((ptr = fopen("teste.dat", "r"))== NULL) {
     printf("Erro na abertura de arquivo\n");
     exit(-1);
  }
  while  (ret != EOF) {
     ret = fscanf (ptr, " %[^:]:%d:%f", tab_registros[i].nome, &tab_registros[i].idade, &tab_registros[i].nota_final);
     if (ret==3) { /* ret contém o número de itens lidos */
         printf("REGISTRO %d %s %d %f\n",i,tab_registros[i].nome, tab_registros[i].idade, tab_registros[i].nota_final);
    } else if (ret!=EOF)
         printf("Provavelmente existe um erro no arrquivo de dados\n");
    i++;
  }
 fclose(ptr);
}

int main(void){
  salvar_am_arq();
  restaurar_tab_do_arq();
  return 0;

}

Escrevendo e Lendo usando fgets e fputs

Exercício 1

Neste exemplo usaremos as funções fgetc e fputc para ler e escrever caracteres ASCII nos arquivos. Seja um programa que conta o número de ocorrências do caracter 'a', remova esses caracteres 'a' e salve os caracteres em um novo arquivo destino:

solução Ex. 2
#include <stdio.h>
#include <stdlib.h>
main()
{
   FILE *fp_fonte,*fp_destino;
   int x,cont=0;
                   
   if((fp_fonte=fopen("teste.dat","r")) == NULL) {
		puts("Não conseguiu abrir o arquivo\n");
		exit(1);
   }
   if((fp_destino=fopen("dest.dat","w")) == NULL){
		puts("Não conseguiu abrir o arquivo\n");
		exit(1);
   }

   /* note que fgetc retorna um inteiro contendo o ASCII lido */
   while ((x=fgetc(fp_fonte)) != EOF){
      if(x=='a')
         cont++;
      else
	 if((x=fputc(x,fp_destino))==EOF) {
		puts("Não conseguiu escrever no arquivo\n");
		exit(1);
	 }	
   }	
   printf("ocorrências de a = %d\n", cont);
   fclose(fp_fonte);
   fclose(fp_destino);
}
Note que fputc recebe o caracter como um inteiro mas realiza um cast para
unsigned char com fins de escrevê-lo no arquivo.


Exemplo 4: escrevendo e lendo string em arquivos
#include <stdio.h>
#include <stdlib.h>

int main (void){

  FILE *pont_arq;
  char texto_str[20];

  //abrindo o arquivo_frase em modo "somente leitura"
  pont_arq = fopen("arquivo_palavra.txt", "r");

  //enquanto não for fim de arquivo o looping será executado
  //e será impresso o texto
  while(fgets(texto_str, 20, pont_arq) != NULL)
    printf("%s", texto_str);

  //fechando o arquivo
  fclose(pont_arq);

  return(0);
}

Exemplo 3

Neste exemplo vamos explorar o modo de abertura do arquivo para fazer um append (escrever no final do arquivo). Ver time() e ctime() em http://www.cplusplus.com/reference/ctime/.

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
 
int main(void)
{
   time_t ltime;
   FILE *fp;
   int num;

   /* ler a hora/data do sistema e armazenar em ltime */
   time(&ltime); 

   if ((fp=fopen("DATA.txt", "a")) == NULL) {
       printf("Problemas na abertura do arquivo\n");
       exit(EXIT_FAILURE);
   }
   /* escrever hora/data lida em arquivo, mas converter antes para string (ctime) */
   if ((num = fputs( ctime(&ltime), fp )) != EOF ) {
       fclose(fp);
   } else {
       printf("Erro na escrita do arquivo!\n");
   }
   return (EXIT_SUCCESS);
}

Execute o programa da forma (suponha que se chame LOG_tempo:

 log_tempo
 cat DATA.txt
 log_tempo
 cat DATA.txt
 log_tempo
 cat DATA.txt  
 
  • Exercício: Substitua o modo de append por uma simples escrita (w). Reexecute o programa conforme especificado anteriormente.


Exemplo de gravação/leitura de uma tabela de estruturas na forma binária usando fwrite e fread

#include <stdio.h>
#include <string.h> 
#include <stdlib.h>
 
#define TAM_TAB2 3
 
 
struct tipo_registro2{
   char nome[30];
   int idade;
   float nota_final;
} tab_registros[TAM_TAB2] = {
      {"alfa da silva", 10, 9.5},
      {"beta da silva", 30, 6.9}, 
      {"epson da silva", 20, 7.5},
   };

  struct tipo_registro2 aux[TAM_TAB2];
/*
  Esta função mostra como salvar a tabela de estruturas na forma de um arquivo texto contendo uma estrutura por lionha. Os campos são separados com :
*/

void salvar_am_arq()
{
  FILE *ptr;
  int i;
 
  if ((ptr = fopen("teste.dat", "w+"))== NULL) {
     printf("Erro na abertura de arquivo\n");
     exit(-1);
  }
  fwrite (tab_registros, sizeof(struct tipo_registro2),TAM_TAB2,ptr);
  fclose(ptr);
}

/*
  Esta função mostra como recuperar a tabela de estruturas na forma de um arquivo texto contendo uma estrutura por linha. Os campos são separados com :
*/
void restaurar_tab_do_arq()
{
  char buffer[100+1];
  FILE *ptr;
  int ret;
 
  if ((ptr = fopen("teste.dat", "r"))== NULL) {
     printf("Erro na abertura de arquivo\n");
     exit(-1);
  }
  ret = fread(aux,sizeof(struct tipo_registro2),TAM_TAB2,ptr);
  printf("número de itens lidos = %d\n", ret);
  fclose(ptr);
}
 
main()
{
  salvar_am_arq();
  restaurar_tab_do_arq();
  printf("%s\n", aux[2].nome);
}

Exemplo de gravação de uma tabela com estruturas contendo ponteiros

Observe no exemplo abaixo que um vetor de estruturas (tabela) contendo ponteiros pode ter problemas no seu salvamento, caso as áreas apontadas não sejam gravadas. A tabela tab_registros2 se for gravada com salvar_em_arq2() perderá dados pois os dados apontados por nome não serão salvos com fwrite. Pode-se contornar este problema gravando cada estrutura em uma linha de forma formatada, separada por delimitadores. Ver função salvar_am_arq2_vOK(). A recuperação dos dados deve prever a reconstituição da tabela (ver restaurar_tab_do_arq2()).

Note o significado das formatações usadas no scanf/fscanf: [1]

  • qualquer caracter em branco na string de formato faz com que a função ignore quaisquer espaço em branco, newline, tab ou qualquee
#include <stdio.h>
#include <string.h> 
#include <stdlib.h>

#define TAM_TAB2 3


struct tipo_registro2{
   char *nome;
   int idade;
} tab_registros2[TAM_TAB2] = {
      {"alfa", 10},
      {"beta", 30}, 
      {"epson", 20},
   };

void salvar_em_arq2()
{
  /* PROBLEMAS... - a estrutura tem ponteiros... */
  FILE *ptr;
  ptr =fopen("teste2.dat", "w+");
  fwrite(tab_registros2, sizeof(struct tipo_registro2),TAM_TAB2,ptr);
  fclose(ptr);
}

void salvar_am_arq2_vOK()
{
  FILE *ptr;
  int i;

  ptr =fopen("teste2.dat", "w+");
  for (i=0;i<TAM_TAB2;i++)
     fprintf (ptr, "%s:%d\n", tab_registros2[i].nome, tab_registros2[i].idade);
  fclose(ptr);
}


void print_tab2()
{
  int i;

  for (i=0;i<TAM_TAB2;i++) {
     printf ("%s:%d\n", tab_registros2[i].nome, tab_registros2[i].idade);
  }
}

void restaurar_tab_do_arq2()
{
  char buffer[100+1];
  FILE *ptr;
  int ret,i=0;

  ptr =fopen("teste2.dat", "r");
  while  (ret!=EOF) { 
     ret=fscanf (ptr, " %[^:]:%d", buffer,&tab_registros2[i].idade);
     if (ret==2) {
         tab_registros2[i].nome=(char *)malloc(strlen(buffer));
         strcpy(tab_registros2[i].nome, buffer);
         printf("i=%d ret=%d %s %d\n",i,ret,tab_registros2[i].nome, tab_registros2[i].idade);
    } else if (ret!=EOF)
         printf("Provavelmente existe um erro no arrquivo de dados\n");
    i++;
  } 
 fclose(ptr);
}

main()
{
  salvar_am_arq2_vOK();
  restaurar_tab_do_arq2();
  print_tab2();
  /* a fazer = liberar areas alocadas dinamicamente */
}

Ainda funções de acesso a arquivos - posicionando cursor sobre o arquivo

A localização corrente do acesso a um arquivo pode ser modificada antes de uma leitura ou escrita. Tipicamente, quando se abre uma arquivo para leitura/escrita, o "cursor" de acesso é 0, ou seja início do arquivo. Se o arquivo for aberto em modo append, o "cursor" é posicionado no final. A cada acesso (leitura ou esrita), este cursor é incrementado conforme o número de dados lidos ou escritos.

É possível entretanto modificar a posição deste cursor, tipicamente com as funções fseek() e rewind(). (ver [2]).

Considere o programa abaixo que escreve um vetor de 100 inteiros em um arquivo.

#include <stdio.h>

int x[100];

void main()
{
  FILE *fp;

  fp = fopen("teste.dat","w");
  x[0]=32;
  x[90]=11;
  fwrite(x, sizeof(int),100,fp);
  fclose(fp);
}

Note que o vetor, sendo uma variável global não inicialiazada, será zerado pelo procedimento de startup do programa. Entretanto, a posição 90 recebe um valor (11).

Agora observe o programa seguinte que lê especificamente a posição 90 e na sequência restabelece o cursor no início. Note que o fread lê somente um inteiro na posição corrente do arquivo. A posição corrente foi determinada por fseek e depois retornada para o início com rewind.

#include <stdio.h>

int y;

void main()
{
  FILE *fp;

  fp = fopen("teste.dat","r");
  fseek(fp, 90*sizeof(int), SEEK_SET);
  fread(&y, sizeof(int),1,fp);
  printf("y=%d\n", y);
  rewind(fp);
  fread(&y, sizeof(int),1,fp);
  printf("y=%d\n", y);
  fclose(fp);
}

Exercício

Escreva um programa que receba do usuário 5 números inteiros e o nome do arquivo no qual eles devem ser armazenados. Em seguida, ler do arquivo estes valores armazenados copiando-os para um vetor de inteiros e imprimindo na tela.

solução Ex. 1
#include <stdio.h>

int main(){

FILE *arq;
int i, temp, vet[5];
char nome_arq[20];

printf("Informe o nome do arquivo: ");
scanf("%[^\n]s",nome_arq);

if ((arq = fopen (nome_arq, "w+"))== NULL){
  printf("Erro ao abrir o arquivo %s", nome_arq);
  return (1);
}

for (i=0; i<5 ; i++){
  printf("informe um valor inteiro: ");
  scanf("%d", &temp);
  fprintf(arq, "%d", temp);
  if (i < 4)
    fprintf(arq, "\n");
}
rewind(arq);  //Retorna o indicador de posição do ponteiro FILE em relação ao arquivo para o seu início.

i = 0;
while ( !feof(arq) ){     //Retorna zero se o final do arquivo foi atingido e um valor não nulo caso contrário.
  fscanf (arq, "%d", &vet[i]);
  printf("%d\n",vet[i]);
  i++;
}
fclose(arq);
return 0;
}