PR1022804 2025 1 AULA12: mudanças entre as edições

De MediaWiki do Campus São José
Ir para navegação Ir para pesquisar
Douglas (discussão | contribs)
Criou página com '=Operações com Arquivos= ;OBJETIVOS *O sistema de arquivos no Linux; *Tratamento de arquivos no C; *O acesso com funções de alto nível. ;INTRODUÇÃO :O sistema de arquivos é um "conjunto de estruturas lógicas" usado em todos os HDs, SSDs e chips de memória flash. Caso os componentes não tenham o sistema, os dados armazenados não poderão ser localizados e muito menos lidos em computadores e celulares com Windows, Linux, iOS ou Android. Na prática, um sis...'
 
Douglas (discussão | contribs)
Sem resumo de edição
 
Linha 1: Linha 1:
=Operações com Arquivos=
=Listas Encadeada=
 


;OBJETIVOS
;OBJETIVOS


*O sistema de arquivos no Linux;
:O aluno deverá conhecer:
*Tratamento de arquivos no C;
 
*O acesso com funções de alto nível.
:*Listas encadeadas;  
:*Algumas aplicações.




;INTRODUÇÃO
;METODOLOGIA: A aula será expositiva e dialogada, utilizando apresentação de texto base na Internet, onde serão mostrados exemplos testados programas no microcomputador do laboratório de informática.


:O sistema de arquivos é um "conjunto de estruturas lógicas" usado em todos os HDs, SSDs e chips de memória flash. Caso os componentes não tenham o sistema, os dados armazenados não poderão ser localizados e muito menos lidos em computadores e celulares com Windows, Linux, iOS ou Android. Na prática, um sistema de arquivo (''file system'') é um conjunto de estruturas lógicas, ou seja, feitas diretamente via software, que permite ao sistema operacional ter acesso e controlar os dados gravados no disco.


:Cada sistema operacional lida com um sistema de arquivos diferente e cada sistema de arquivos possui as suas peculiaridades, como limitações, qualidade, velocidade, gerenciamento de espaço, entre outras características. É o sistema de arquivos que define como os bytes que compõem um arquivo serão armazenados no disco e de que forma o sistema operacional terá acesso aos dados. Para efeito dos nossos estudos vamos utilizar o sistema de arquivos do Linux.
;INTRODUÇÃO: A Lista Encadeada nada mais é que uma representação de uma sequência de objetos, do mesmo tipo, na memória do computador. Cada elemento da sequência é armazenado em uma célula da lista: o primeiro elemento na primeira célula, o segundo na segunda e assim por diante. ''Linked list'' ou lista encadeada e um tipo de estrutura de dados que contém um grupo de nós (conjunto de dados) interligados através de ponteiros, onde o ponteiro dentro da estrutura aponta para o próximo nó até que o ponteiro seja '''NULL''' indicando assim o fim da lista.


==Estrutura de uma lista encadeada==


==O Arquivo==
Uma lista encadeada  (''linked list'') é uma sequência de células onde cada célula contém um objeto de algum tipo e o endereço da célula seguinte.


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.
Vamos supor que os objetos armazenados nas células são do tipo '''int'''. A estrutura de cada célula de uma determinada lista pode ser definida assim:


Note que um arquivo pode conter dados como por exemplo: um programa C, um programa executável, textos, música, vídeos e fotos. Seja qual for a natureza dos dados o armazenamento será na forma de bits.
<syntaxhighlight lang=c>
struct cel {
    int conteudo;
    struct cel *prox;
};
</syntaxhighlight>


Normalmente, arquivos são armazenados em memórias secundárias, tais como CD e HD (''Hard Disk''). Também podem ser armazenados na memória principal RAM ou uma memória rápida 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 em que em conjunto de 8, representam os bytes, que por sua vez estão associados a um código ASCII.
{| border="1" cellpadding="5" cellspacing="0"
! style="background: #FAF0E6;" | 123
! style="background: #FAF0E6;" | -->
|}


==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.
;SENDO:
:'''123''': conteudo
:'''-->''': *prox


Tipicamente, um sistema de arquivos ocupa uma área de um disco (ou mídia de armazenamento).
É conveniente tratar as células como um novo '''tipo de dados''' (struct) e atribuir um nome a esse novo tipo:
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.
<syntaxhighlight lang=c>
typedef struct cel celula;  // célula
</syntaxhighlight>


Existem vários formatos de sistema de arquivos, dependendo do sistema operacional. No linux os formatos mais conhecidos são: ext2, ext3, ext4 etc. No Windows os mais conhecidos são FAT32 e NTFS.
Uma célula  c  e um ponteiro  p  para uma célula podem ser declarados assim:


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 (/).
celula  c;
celula *p;




;SISTEMA DE ARQUIVOS
Se '''c''' é uma célula então  '''c.conteudo'''  é o conteúdo da célula e  '''c.prox'''  é o endereço da próxima célula.  Se  '''p''' é o endereço de uma célula, então  '''p->conteudo'''  é o conteúdo da célula e  '''p->prox'''  é o endereço da próxima célula. Se '''p''' é o endereço da última célula da lista então: '''p->prox''' vale  '''NULL'''.


{| border="1" cellpadding="10" cellspacing="10"  
{| border="1" cellpadding="5" cellspacing="0"  
! style="background:#2E8B57; color:white;" | superbloco
! style="background: #FAF0E6;" | 123
! style="background:#2E8B57; color:white;" | lista de ''i''-nodes
! style="background: #FAF0E6;" | -->
! style="background:#2E8B57; color:white;" | blocos dos arquivos
! style="background: #FAF0E6;" | 234
! style="background: #FAF0E6;" | -->
! style="background: #FAF0E6;" | 345
! style="background: #FAF0E6;" | -->
! style="background: #FAF0E6;" | 456
! style="background: #FAF0E6;" | -->
! style="background: #FAF0E6;" | 567
! style="background: #FAF0E6;" | -->
! style="background: #FAF0E6;" | 678
! style="background: #FAF0E6;" | NULL
|}
|}
<br>


==Diretórios==
==Listas com cabeça e sem cabeça==


São arquivos especiais que contém basicamente uma lista contendo nome e inode correspondente dos arquivos que o diretório contém.
Uma lista encadeada pode ser organizada de duas maneiras diferentes.


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.
;LISTA COM CABEÇA:  O conteúdo da primeira célula é irrelevante: ela serve apenas para marcar o início da lista. A primeira célula é a cabeça (= ''head cell'' = ''dummy cell'') da lista.  Digamos que ini é o endereço da primeira célula. Então  ini->prox == NULL se e somente se a lista está vazia. Para criar uma lista vazia, basta fazer:


==Referência a um arquivo==
celula c, *ini;
c.prox = NULL;
ini = &c;


A localização de um arquivo pode ser realizada pela referência absoluta, ou seja, desde o diretório / do sistema:
ou, se preferir alocar a primeira célula dinamicamente,


  cat /etc/passwd
  celula *ini;
ini = malloc(sizeof (celula));
ini->prox = NULL;


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 /
;LISTA SEM CABEÇA:  O conteúdo da primeira célula é tão relevante quanto o das demais. Nesse caso, a lista está vazia se o endereço de sua primeira célula é NULL.  Para criar uma lista vazia basta fazer:


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:
celula *ini; 
ini = NULL;


cat passwd
É preferível as listas sem cabeça porque são mais simples, porém, a vida do programador fica mais fácil quando as listas têm cabeça.


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''
;EXEMPLOS: Para imprimir o conteúdo de uma lista encadeada com cabeça:


cd /etc
<syntaxhighlight lang=c>
cat passwd
// Imprime o conteúdo de uma lista encadeada
// com cabeça. O endereço da primeira célula é ini.


==No Linux/Unix tudo é arquivo==
void imprima(celula *ini) {
  celula *p;
  for (p = ini->prox; p != NULL; p = p->prox)
      printf( "%d\n", p->conteudo);
}
</syntaxhighlight>


No Linux, qualquer referência/acesso ao hardware/dispositivos é realizada na forma de acesso a arquivo. Ver arquivos no diretório de dispositivos:
:Mesma função para lista sem cabeça:


  ls -l /dev
<syntaxhighlight lang=c>
// Imprime o conteúdo de uma lista encadeada ini.
// A lista não tem cabeça.


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).
void imprima(celula *ini) {
  celula *p;
  for (p = ini; p != NULL; p = p->prox)
      printf( "%d\n", p->conteudo);
}
</syntaxhighlight>


=Acessando arquivos a partir de programas C=
;NOTE: Que o tipo de dados '''ini''' não inicializa no próximo (ini->prox) como no exemplo acima.


==Acesso a arquivos: funções de baixo e alto nível==
==Busca em uma lista encadeada==


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.
Veja como é fácil verificar se um inteiro x pertence a uma lista encadeada, ou seja, se é igual ao conteúdo de alguma célula da lista:
O Linux, assim como outros sistemas operacionais, 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).
<syntaxhighlight lang=c>
Duas categorias de funções para acesso a arquivo são disponibilizadas [http://en.wikipedia.org/wiki/C_file_input/output (ver detalhes aqui)]:
// Esta função recebe um inteiro x e uma lista
*Funções de baixo nível: acessam diretamente o sistema;
// encadeada ini de inteiros, com celula-cabeça.
*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.
// A função devolve o endereço de uma celula que
// contém x ou devolve NULL se tal celula não
// existe.


==Acesso a arquivo em MODO TEXTO através de funções de alto nível==
celula *busca( int x, celula *ini)
{
  celula *p;
  p = ini->prox;
  while (p != NULL && p->conteudo != x)
      p = p->prox;
  return p;
}
</syntaxhighlight>
 
=Exercícios=


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.
Por vezes não se conhece o tamanho dos dados que se vai manipular e o uso de uma lista pode ser conveniente para
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()).
armazená-los. Um sistema de estoque de produtos, por exemplo, poderia ser armazenado na forma de uma lista.


A tabela abaixo apresenta as principais funções da
;EXEMPLO: de lista ligada.
linguagem C para manipulação de arquivos:


{| class="wikitable" style="background-color:#ffffff; text-align: center;"
<syntaxhighlight lang=c>
! style="background:#2E8B57; color:white;;" | Função
#include <stdlib.h>
! style="background:#2E8B57; color:white;;" | Ação
#include <stdio.h>
|-
!style="text-align: center;" | fopen()
|Abre um arquivo.
|-
!style="text-align: center;" | fclose()
|Fecha um arquivo.
|-
!style="text-align: center;" | putc() e fputc()
|Escreve um caractere em um arquivo
|-
!style="text-align: center;" | getc() e fgetc()
|Lê um caractere de um arquivo.
|-
!style="text-align: center;" | fseek()
|Posiciona em um registro de um arquivo
|-
!style="text-align: center;" | fprintf()
|Efetua impressão formatada em um arquivo.
|-
!style="text-align: center;" | fscanf()
|Efetua leitura formatada em um arquivo.
|-
!style="text-align: center;" | feof()
|Verifica o final de um arquivo.
|-
!style="text-align: center;" | fwrite()
|Escreve tipos maiores que 1 byte em um arquivo.
|-
!style="text-align: center;" | fread()
|Lê tipos maiores que 1 byte de um arquivo.
|}
<br>


==Aprendendo com Exemplos==
/*========================*/
/** OPERAÇÔES COM LISTA LIGADA ******/
/*========================*/


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
  tipos e variáveis globais
por  ''p_arq''.
*/


<blockquote style="background:#FAFAD2; border: 2px solid #2E8B57; margin-left: 100px; margin-right: 100px; padding: 2em;">
struct TProduto{
;Modos de abertura de arquivos – fopen():
  int codigo;
:        w -> Escrita
  struct TProduto *next;
:        r -> leitura
} *head, *tail;
:        a -> anexar
:        r+ -> leitura e escrita
:        w+ -> leitura e escrita (apaga o conteúdo caso o arquivo exista)
:        a+ -> leitura e escrita (adiciona ao final do arquivo)
</blockquote>


;EXEMPLO 1:


*Escrevendo um arquivo texto de forma formatada:
/*
  adiciona item a cabeça da lista
  retorna 0 se tudo ok e -1 se erro
*/
int add_nodo_head(int codigo)
{


<syntaxhighlight lang=c>
}
#include <stdio.h>


void main()
/*
  adiciona item ao final  lista
  retorna 0 se tudo ok e -1 se erro
*/
int add_nodo_tail(int codigo)
{
{
  FILE *p_arq;
  struct TProduto *p =  malloc (sizeof(struct TProduto));
  int i;
  if (!p)
  int res;
        return -1;


  if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
  p->codigo = codigo;
    printf("Problemas na abertura do arquivo\n");
  p->next = NULL;
    return;
  }


  for (i = 0; i<10;i++) {
  if (tail==NULL) {
  /* A funcao fprintf devolve o número de bytes gravados  ou EOF se houve erro na gravação */
      /* lista vazia */
       if((res = fprintf(p_arq,"Linha %d\n",i))==EOF) {      
       tail = head = p;
        printf("Erro\n");
  }else {
  break;
    /*lista não vazia */
      }
      tail->next = p;
  }
      tail = p;
  fclose(p_arq);
  }
  return 0;
}
}
</syntaxhighlight>


Note que se o arquivo IFSC.txt não existir, ele será criado.
/*
No linux para ver o que foi escrito faça:
  imprimir lista
> cat IFSC.txt
*/


*Lendo um arquivo texto de forma formatada:
void print_list(struct TProduto *ini)
 
{
<syntaxhighlight lang=c>
      struct TProduto *p;
#include <stdio.h>
      for (p = ini->next; p != NULL; p = p->next)
      printf( "%d\n", p->codigo);
}
   
   
void main()
void main()
{
{
  FILE *p_arq;
   int i;
   int i,j;
 
  int res;
   head = tail = NULL;
  char buff[100];
 
   for (i=0;i<5;i++)
   if ((p_arq=fopen("IFSC.txt", "r")) == NULL) {
    add_nodo_tail(i);
    printf("Problemas na abertura do arquivo\n");
 
    return;
   print_list (head);
  }
   while(1) {
      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);
}
}
</syntaxhighlight>
</syntaxhighlight>


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.


;EXERCÍCIO: Implementar uma função que some duas matrizes fornecidas em arquivos de texto separados: MatA.dat e MatB.dat. Colocando o resultado em um outro arquivo de texto com o nome MatR.dat.
;EXERCÍCIO 1: Para o exemplo anterior:


> cat MatA.dat
* Implementar a função add_node_head()
* Implementar a função print_list()
* Implementar a função delete_node(int codigo)


<pre>
1
2
3
4
5
5
4
3
2
1
1
2
3
4
5
5
4
3
2
1
1
2
3
4
5
</pre>
''* esse é o arquivo MatA.dat com 25 elementos (matriz 5x5)''


> cat MatB.dat
;EXERCÍCIO 2: Considere um sistema de estoque de uma livraria representado por uma tabela conforme abaixo. Elabore as funções para adicionar, remover e listar um livro na/da tabela. Considere que uma entrada livre possui o ponteiro titulo igual a NULL.
<pre>
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
</pre>
''* esse é o arquivo MatB.c com 25 elementos (matriz 5x5)''


{{collapse top|bg=#E6E6FA|Solução}}
<syntaxhighlight lang=c>
<syntaxhighlight lang=c>
#include <stdio.h>
#include <stdio.h>
#define TAM 10
   
   
void main()
struct tLivro {
{
   char *titulo;
   FILE *p_arq;
   char *autor;
   int i,j,res;
   char *isbn;
   float MatA[5][5], MatB[5][5], MatR[5][5];
   float *preco;
    
   int  estoque;
  /* 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");
typedef struct tLivro TLivro;
     
  /* Ler a matriz MatB do arquivo */
TLivro Livros[10]; /* iniciar a tabela com NULL */
  if ((p_arq=fopen("MatB.dat", "r")) == NULL) {
    printf("Problemas na abertura do arquivo\n");
    return;
  }
    
    
  for (i =0;i<5;i++) {
TLivro *retornar_entrada_livre()
      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];
int adicionar_livro()
      }
{
  }
char aux_isbn[20];
  printf("Resultado da Adição\n");
  TLivro *p;
  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) {
/* Ler ISBN */
    printf("Problemas na abertura do arquivo\n");
if(verificar_livro_existe(aux_isbn)==1)
    return;
    return 0;
  }
if((p=retornar_entrada_livre())==NULL)
 
    return -1;
  for (i =0;i<5;i++) {
      for (j=0;j<5;j++) {
/* Ler os dados do livro pelo teclado e colocar na estrutura
        if((res = fprintf(p_arq,"%f ",MatR[i][j]))==EOF) {     
    apontada por p*/
        printf("erro\n");
break;
      }
      }
      fprintf(p_arq,"\n");
  }
  fclose(p_arq);     
}
}
</syntaxhighlight>
{{collapse bottom}}
/* retorna 0 se removido com sucesso e -1 caso contrário */
int remover_livro(char *isbn)
{


;EXEMPLO 2: 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 caractere 'a' em um arquivo e reescreve o arquivo sem os "as".
}
void imprimir_dados_livro(char *isbn)
{


Exemplo do arquivo ''teste.dat''*:
}
 
   
  > cat teste.dat
main()
<pre>
Pangrama, ou pantograma, (do grego, pan ou pantós = todos, + grama = letra) é uma frase em que são usadas todas as letras do alfabeto de determinada língua.
</pre>
''* conteúdo do arquivo teste.dat''
 
<syntaxhighlight lang=c>
#include <stdio.h>
#include <stdlib.h>
void main()
{
{
  FILE *fp_fonte,*fp_destino;
/* testar as funções aqui */
  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 abrir o arquivo\n");
exit(1);
}
  }
  printf("ocorrências de a = %d\n", cont);
  fclose(fp_fonte);
  fclose(fp_destino);
}
}
</syntaxhighlight>
</syntaxhighlight>


No linux, para ver o arquivo ''dest.dat'' faça:
;EXERCÍCIO 3: Refazer o exercício anterior usando filas.
 
> cat dest.dat
 
<blockquote style="background:#FAFAD2; border: 2px solid #2E8B57; margin-left: 100px; margin-right: 100px; padding: 2em;">
;NOTE
*Que '''fputc''' recebe o caracter como um inteiro mas realiza um ''cast'' para ''unsigned char'' com fins de escrevê-lo no arquivo.
*Substitua 'a' por '\n' e veja o que acontece com o arquivo destino.
</blockquote>


;EXEMPLO 3: Neste exemplo vamos explorar o modo de abertura do arquivo para fazer um '''append''' (escrever no final do arquivo).
;EXERCÍCIO 4: Seja a seguinte estrutura:


<syntaxhighlight lang=c>
<syntaxhighlight lang=c>
#include <time.h>
struct tipo_carro {
#include <stdio.h>
   char *marca;
   char *modelo;
int main(void)
   int potencia;
{
};
   time_t ltime;
   FILE *fp;
   int num;


  time(&ltime);  
typedef tipo_carro tcarro;
 
  if ((fp=fopen("DATA.txt", "a")) == NULL) {
      printf("Problemas na abertura do arquivo\n");
      return;
  }
  if ((num = fputs( ctime(&ltime), fp )) != EOF ) {
      fclose(fp);
  } else {
      printf("Erro na escrita do arquivo!\n");
  }
}
</syntaxhighlight>
</syntaxhighlight>


Execute o programa da forma (suponha que se chame ''log_tempo.c''):
> ./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.
==Ainda funções de acesso a arquivos==
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 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.
:Implementar as funções abaixo:


<syntaxhighlight lang=c>
<syntaxhighlight lang=c>
#include <stdio.h>
/* cria dinamicamente uma estrutura,
  preenche os dados dos campos
  e retorna um ponteiro para a estrutura criada
  Retorna NULL caso não consiga alocar área
*/
void tcarro *ler_carro()
{


int x[100];
}


void main()
/*
  recebe dois ponteiros para estruturas do tipo carro e
  retorna -1 caso pelo menos um dos ponteiros seja NULL,
  retorna 0 se os modelos forem iguais
  e retorna 1 se os modelos forem diferentes */
int compara_modelo(tcarro *p1, tcarro *p2)
{
{
  FILE *fp;


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


<blockquote style="background:#FAFAD2; border: 2px solid #2E8B57; margin-left: 100px; margin-right: 100px; padding: 2em;">
/* libera a área da estrutura passada como parâmetro */
;NOTE
void deleta_estrutura(tcarro *p)
*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).
{
*Não é possível visualizar o conteúdo do arquivo porque a função '''fwrite()''' grava o arquivo em formato binário.
</blockquote>


{{collapse top|bg=#E6E6FA|Ler o arquivo gravado fwrite()}}
}
<syntaxhighlight lang=c>
#include <stdio.h>
 
int x[100];


void main()
void main()
{
{
   FILE *fp;
   /*testar as funções aqui */
 
  /* criar dois carros */
   fp = fopen("teste.dat","r");
   /* comparar o modelo dos dois carros. Testar o retorno */
   fread(x, sizeof(int),100,fp);
   /* liberar a área das estruturas */
  fclose(fp);
  for(int i=0;i<100;i++)
    printf("%d\n",x[i]);
}
}
</syntaxhighlight>
</syntaxhighlight>
{{collapse bottom}}
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''.


==EXEMPLO DA AULA==


<syntaxhighlight lang=c>
<syntaxhighlight lang=c>
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <string.h>


int y;
struct TFicha {
    int codigo;
    char nome[40];
    char fone[20];
    struct TFicha *prox;
} *cliente,*cabeca;


void main()
void Cadastrar(){
{
struct TFicha *p =  malloc (sizeof(struct TFicha));
  FILE *fp;
    printf("\nDigite codigo: ");
    scanf("%d",&p->codigo);
    printf("\nDigite Nome: ");
    fflush(stdin);
    scanf("%[^\n]",p->nome);
    printf("\nDigite Telefone: ");
    fflush(stdin);
    scanf("%[^\n]",p->fone);


  fp = fopen("teste.dat","r");
    if (cliente==NULL) {
  fseek(fp, 90*sizeof(int), SEEK_SET);
      /* lista vazia */
  fread(&y, sizeof(int),1,fp);
      cliente = cabeca = p;
  printf("y=%d\n", y);
  }else {
  rewind(fp);
    /*lista não vazia */
  fread(&y, sizeof(int),1,fp);
      cliente->prox = p;
  printf("y=%d\n", y);
      cliente = p;
  fclose(fp);
  }
}
}
</syntaxhighlight>


;Exercício: 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 uma tabela de 5 registros a fim de testar a função implementada.
void Imprimir(struct TFicha *ini) {
 
  struct TFicha *p;
<syntaxhighlight lang= c>
  printf("\n\nCodigo  Nome                Fone");
#include <stdio.h>
  for (p = ini; p != NULL; p = p->prox){
 
      printf( "\n%-d", p->codigo);
struct TRegistro {
      printf( "\t%-20s", p->nome);
  char nome[30];
       printf( "\t%-20s", p->fone);
  int idade;
  }
} Registro, *pAux;
 
struct TRegistro *LerTab(int indice)
{
}
 
void main()
{
  pAux=LerTab(3);
  if (pAux!=0) {
       printf("Nome lido %s\n", pAux->nome);
  }
}
}
</syntaxhighlight>


==Decompondo ''string'' com delimitadores==


Esta é uma forma de resolver o problema com "espaço" no texto da ''string''. Pode ser utilizado conjuntamente com operações com arquivos.
void main(){


<syntaxhighlight lang=c>
    cliente=NULL;
#include <string.h>
    int opcao;
#include <stdio.h>
    while (1){
 
        printf("\n\nEscolha:\n");
int main()
        printf("1 - Cadastrar\n");
{
        printf("2 - Imprimir\n");
  const char string_delimitada[80] = "alfa:beta:delta:epson";
        printf("3 - Sair\n");
  const char s[2] = ":";
        printf("-> ");
  char *token;
        scanf("%d",&opcao);
 
        switch (opcao)
  /* Ler o primeiro campo passando a string em str e o delimitador em s */
        {
  token = strtok(string_delimitada, s);
        case 1:
 
                Cadastrar(cliente);
  /* Ler os pŕoximos campos - passar primeiro parâmetro NULL */
                break;
  while( token != NULL )  
            case 2:
  {
                Imprimir(cabeca);
      printf( " %s\n", token );
                break;
   
            case 3:
      token = strtok(NULL, s);
                return 0;
  }
        }
 
    }
  return(0);
}
}
</syntaxhighlight>
</syntaxhighlight>
Linha 580: Linha 413:
=Referências=
=Referências=


[1] https://www.techtudo.com.br/dicas-e-tutoriais/noticia/2016/02/entenda-o-que-e-sistema-de-arquivos-e-sua-utilidade-no-pc-e-no-celular.html
[1] http://www.ime.usp.br/~pf/algoritmos/aulas/lista.html
 
[2] https://wagnergaspar.com/como-ler-um-arquivo-texto-com-a-funcao-fscanf/
 
[3] https://wagnergaspar.com/como-criar-uma-agenda-de-aniversario-e-salvar-em-arquivo-com-c/


[4] [https://drive.google.com/file/d/1HTDPuCYJYr7tcGbkO8Of2alDNSZiB6-A/view?usp=sharing Videoaula | Operações com Arquivos]





Edição atual tal como às 18h49min de 3 de julho de 2025

1 Listas Encadeada

OBJETIVOS
O aluno deverá conhecer:
  • Listas encadeadas;
  • Algumas aplicações.


METODOLOGIA
A aula será expositiva e dialogada, utilizando apresentação de texto base na Internet, onde serão mostrados exemplos testados programas no microcomputador do laboratório de informática.


INTRODUÇÃO
A Lista Encadeada nada mais é que uma representação de uma sequência de objetos, do mesmo tipo, na memória do computador. Cada elemento da sequência é armazenado em uma célula da lista: o primeiro elemento na primeira célula, o segundo na segunda e assim por diante. Linked list ou lista encadeada e um tipo de estrutura de dados que contém um grupo de nós (conjunto de dados) interligados através de ponteiros, onde o ponteiro dentro da estrutura aponta para o próximo nó até que o ponteiro seja NULL indicando assim o fim da lista.

1.1 Estrutura de uma lista encadeada

Uma lista encadeada (linked list) é uma sequência de células onde cada célula contém um objeto de algum tipo e o endereço da célula seguinte.

Vamos supor que os objetos armazenados nas células são do tipo int. A estrutura de cada célula de uma determinada lista pode ser definida assim:

struct cel {
    int conteudo; 
    struct cel *prox;
};


123 -->


SENDO
123: conteudo
-->: *prox

É conveniente tratar as células como um novo tipo de dados (struct) e atribuir um nome a esse novo tipo:

typedef struct cel celula;  // célula

Uma célula c e um ponteiro p para uma célula podem ser declarados assim:

celula  c;
celula *p;


Se c é uma célula então c.conteudo é o conteúdo da célula e c.prox é o endereço da próxima célula. Se p é o endereço de uma célula, então p->conteudo é o conteúdo da célula e p->prox é o endereço da próxima célula. Se p é o endereço da última célula da lista então: p->prox vale NULL.

123 --> 234 --> 345 --> 456 --> 567 --> 678 NULL

1.2 Listas com cabeça e sem cabeça

Uma lista encadeada pode ser organizada de duas maneiras diferentes.

LISTA COM CABEÇA
O conteúdo da primeira célula é irrelevante: ela serve apenas para marcar o início da lista. A primeira célula é a cabeça (= head cell = dummy cell) da lista. Digamos que ini é o endereço da primeira célula. Então ini->prox == NULL se e somente se a lista está vazia. Para criar uma lista vazia, basta fazer:
celula c, *ini;
c.prox = NULL;
ini = &c;

ou, se preferir alocar a primeira célula dinamicamente,

celula *ini;
ini = malloc(sizeof (celula));
ini->prox = NULL;
LISTA SEM CABEÇA
O conteúdo da primeira célula é tão relevante quanto o das demais. Nesse caso, a lista está vazia se o endereço de sua primeira célula é NULL. Para criar uma lista vazia basta fazer:
celula *ini;  
ini = NULL;

É preferível as listas sem cabeça porque são mais simples, porém, a vida do programador fica mais fácil quando as listas têm cabeça.

EXEMPLOS
Para imprimir o conteúdo de uma lista encadeada com cabeça:
// Imprime o conteúdo de uma lista encadeada
// com cabeça. O endereço da primeira célula é ini.

void imprima(celula *ini) {
   celula *p;
   for (p = ini->prox; p != NULL; p = p->prox) 
      printf( "%d\n", p->conteudo);
}
Mesma função para lista sem cabeça:
// Imprime o conteúdo de uma lista encadeada ini.
// A lista não tem cabeça.

void imprima(celula *ini) {
   celula *p;
   for (p = ini; p != NULL; p = p->prox) 
      printf( "%d\n", p->conteudo);
}
NOTE
Que o tipo de dados ini não inicializa no próximo (ini->prox) como no exemplo acima.

1.3 Busca em uma lista encadeada

Veja como é fácil verificar se um inteiro x pertence a uma lista encadeada, ou seja, se é igual ao conteúdo de alguma célula da lista:

// Esta função recebe um inteiro x e uma lista
// encadeada ini de inteiros, com celula-cabeça.
// A função devolve o endereço de uma celula que
// contém x ou devolve NULL se tal celula não
// existe.

celula *busca( int x, celula *ini)
{
   celula *p;
   p = ini->prox;
   while (p != NULL && p->conteudo != x) 
      p = p->prox; 
   return p; 
}

2 Exercícios

Por vezes não se conhece o tamanho dos dados que se vai manipular e o uso de uma lista pode ser conveniente para armazená-los. Um sistema de estoque de produtos, por exemplo, poderia ser armazenado na forma de uma lista.

EXEMPLO
de lista ligada.
#include <stdlib.h>
#include <stdio.h>

/*========================*/
/** OPERAÇÔES COM LISTA LIGADA ******/
/*========================*/

/*
   tipos e variáveis globais
*/

struct TProduto{
   int codigo;
  struct TProduto *next;
} *head, *tail;


/*
   adiciona item a cabeça da lista
   retorna 0 se tudo ok e -1 se erro
*/
int add_nodo_head(int codigo)
{

}

/*
   adiciona item ao final  lista
   retorna 0 se tudo ok e -1 se erro
*/
int add_nodo_tail(int codigo)
{
   struct TProduto *p =  malloc (sizeof(struct TProduto));
   if (!p)
        return -1;

   p->codigo = codigo;
   p->next = NULL;

   if (tail==NULL) {
      /* lista vazia */
      tail = head = p;
   }else {
     /*lista não vazia */
       tail->next = p;
       tail = p;
   }
   return 0;
}

/*
   imprimir lista 
*/

void print_list(struct TProduto *ini)
{
      struct TProduto *p;
      for (p = ini->next; p != NULL; p = p->next)
      printf( "%d\n", p->codigo);
}
 
void main()
{
  int i;

  head = tail = NULL;

  for (i=0;i<5;i++)
     add_nodo_tail(i);

  print_list (head);
}


EXERCÍCIO 1
Para o exemplo anterior:
  • Implementar a função add_node_head()
  • Implementar a função print_list()
  • Implementar a função delete_node(int codigo)


EXERCÍCIO 2
Considere um sistema de estoque de uma livraria representado por uma tabela conforme abaixo. Elabore as funções para adicionar, remover e listar um livro na/da tabela. Considere que uma entrada livre possui o ponteiro titulo igual a NULL.
#include <stdio.h>
#define TAM 10
 
struct tLivro {
  char *titulo;
  char *autor;
  char *isbn;
  float *preco;
  int  estoque;
};

typedef struct tLivro TLivro;
 
TLivro Livros[10]; /* iniciar a tabela com NULL */
 
  
TLivro *retornar_entrada_livre()
{

}
 
int adicionar_livro()
{
 char aux_isbn[20];
 TLivro *p;

 /* Ler ISBN */
 if(verificar_livro_existe(aux_isbn)==1)
    return 0;
 if((p=retornar_entrada_livre())==NULL)
    return -1;
 
 /* Ler os dados do livro pelo teclado e colocar na estrutura 
    apontada por p*/
}
 
/* retorna 0 se removido com sucesso e -1 caso contrário */
int remover_livro(char *isbn)
{

}
 
void imprimir_dados_livro(char *isbn)
{

}
 
main()
{
 /* testar as funções aqui */
}
EXERCÍCIO 3
Refazer o exercício anterior usando filas.
EXERCÍCIO 4
Seja a seguinte estrutura:
struct tipo_carro {
   char *marca;
   char *modelo;
   int  potencia;
};

typedef tipo_carro tcarro;


Implementar as funções abaixo:
/* cria dinamicamente uma estrutura,
   preenche os dados dos campos
   e retorna um ponteiro para a estrutura criada
   Retorna NULL caso não consiga alocar área
 */
void tcarro *ler_carro()
{

}

/*
  recebe dois ponteiros para estruturas do tipo carro e
  retorna -1 caso pelo menos um dos ponteiros seja NULL,
  retorna 0 se os modelos forem iguais
  e retorna 1 se os modelos forem diferentes */
int compara_modelo(tcarro *p1, tcarro *p2)
{

}

/* libera a área da estrutura passada como parâmetro */
void deleta_estrutura(tcarro *p)
{

}

void main()
{
  /*testar as funções aqui */
  /* criar dois carros */
  /* comparar o modelo dos dois carros. Testar o retorno */
  /* liberar a área das estruturas */
}

2.1 EXEMPLO DA AULA

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

struct TFicha {
    int codigo;
    char nome[40];
    char fone[20];
    struct TFicha *prox;
} *cliente,*cabeca;

void Cadastrar(){
 struct TFicha *p =  malloc (sizeof(struct TFicha));
    printf("\nDigite codigo: ");
    scanf("%d",&p->codigo);
    printf("\nDigite Nome: ");
    fflush(stdin);
    scanf("%[^\n]",p->nome);
    printf("\nDigite Telefone: ");
    fflush(stdin);
    scanf("%[^\n]",p->fone);

    if (cliente==NULL) {
      /* lista vazia */
      cliente = cabeca = p;
   }else {
     /*lista não vazia */
       cliente->prox = p;
       cliente = p;
   }
}

void Imprimir(struct TFicha *ini) {
   struct TFicha *p;
   printf("\n\nCodigo  Nome                 Fone");
   for (p = ini; p != NULL; p = p->prox){
      printf( "\n%-d", p->codigo);
      printf( "\t%-20s", p->nome);
      printf( "\t%-20s", p->fone);
   }
}


void main(){

    cliente=NULL;
    int opcao;
    while (1){
        printf("\n\nEscolha:\n");
        printf("1 - Cadastrar\n");
        printf("2 - Imprimir\n");
        printf("3 - Sair\n");
        printf("-> ");
        scanf("%d",&opcao);
        switch (opcao)
        {
        case 1:
                Cadastrar(cliente);
                break;
            case 2:
                Imprimir(cabeca);
                break;
            case 3:
                return 0;
        }
    }
}

3 Referências

[1] http://www.ime.usp.br/~pf/algoritmos/aulas/lista.html