UDESC - SBS
Departamento de Sistemas de Informação
LPG-I: Entrada & Saída
de Dados - Arquivos
Prof. Flavio Marcello Strelow
[email protected]
BD, Banco de Dados
“conjunto de cadastros (tabelas)
que se relacionam entre si”.
DB, Database
Definições e Conceitos
Uma TABELA é uma estrutura de dados que agrupa
informações em linhas e colunas.
Cada linha de uma tabela, denominada REGISTRO,
contém informações sobre um determinado item ou
elemento, distribuídas em colunas.
Cada coluna da tabela é denominada de CAMPO.
O tipo de dado do campo é definido pela natureza da
informação que ele armazena. Pode ser numérico (inteiro
ou real), alfabético ou alfanumérico (combinação de
alfabético e numérico).
Glossário - Parte I
• Banco de dados: conjunto de cadastros (tabelas)
que se relacionam entre si (ex. estoque).
• Tabela, ou arquivo: conjunto de registros (ex.
relação dos clientes da empresa).
• Registros: elementos, ou itens, do arquivo (ex.
Restaurante Merion, Faculdade Mater Dei, ...).
• Campos: armazenam as informações do cadastro
(código, nome, endereço, telefone, ...)
O que é uma Tabela ?
colunas = campos
linhas
=
registros
indicador de posição
ponteiro de registro (registro atual)- denota o
registro com o qual o usuário está trabalhando
ESTRUTURA INTERNA DA TABELA (OU ARQUIVO)
ponteiro de registro (indicador de posição)

R1
0
1 * tam
2 * tam
...
(n – 1) * tam
R2
R3
registros do
arquivo
...
Rn
marca de final de
arquivo (End Of File)
posição física do registro
onde: “tam” representa o tamanho em bytes do registro (sizeof)
Definições e Conceitos
De forma geral, o termo banco de dados pode ser utilizado
para indicar um conjunto de tabelas.
Uma tabela, por sua vez, é um conjunto de registros,
ou linhas, subdivididos em muitas colunas, uma para
cada campo do registro.
Outro conceito destacável é o conceito de registro atual,
que denota o registro com o qual o usuário está
trabalhando, registro este que é indicado pelo ponteiro de
registro (indicador de posição) disponível para cada tabela
aberta no programa.
Em Síntese:
BD- Banco de Dados ou
DB- Database
Campo1
Campo2
Registro1
...
Tabela1
Registro2
...
Banco de Dados
Tabela2
...
TabelaN
RegistroN
CampoN
Definições e Conceitos
Os conceitos de tabelas de memória através da utilização
de vetores e o conceito de utilização do tipo definido
registro (struct), formam a base para a utilização
de um arquivo, pois um arquivo é na verdade uma tabela
de informações gravada em um meio físico. Enquanto os
vetores são tabelas manipuladas na memória RAM.
Os vetores são manipulados através de um ou mais índices
de controle enquanto os arquivos são manipulados por um
ponteiro de registro.
Vetores + Registro (struct) = Arquivo (FILE)
Vantagens na Utilização de
Arquivos
• armazenamento em meios magnéticos
– as informações são gravadas permanentemente
em meio físico (memória secundária: disquete,
HD, fitas magnéticas entre outros)
• maior capacidade de armazenamento
– armazena um número grande de registros,
estando apenas limitado ao tamanho do meio
físico utilizado para a sua gravação
Arquivos
A memória principal do microcomputador é volátil, não permitindo
que as informações fiquem armazenadas permanentemente, pois se o
micro for desligado, as informações armazenadas são apagadas e se
perdem.
O mesmo ocorre com as tabelas (vetores) criadas na memória do
microcomputador: se acontecer algum problema com o micro
durante a execução de um programa que cria ou modifica uma tabela,
as informações contidas nesta tabela serão perdidas.
A solução para este tipo de problema é utilizar uma estrutura de dados
que permita a gravação de informações em dispositivos de memória
secundária (disquete, HD, ...). Nas linguagens de programação a
estrutura de dados que guarda informações em disco é chamada de
ARQUIVO (FILE).
Sistema de E/S:
Programa escrito em Linguagem C (*.cpp)
Entrada
(input)
camada,
ou interface de
“abstração”
stream
dispositivos reais:
Arquivo em disco
Teclado
Sistema de E/S:
Programa escrito em Linguagem C (*.cpp)
Saída
(output)
camada,
ou interface de
“abstração”
stream
dispositivos reais:
Arquivo em disco
Console, ou terminal de vídeo
Impressora
Streams e Arquivos
O sistema de E/S de C fornece uma “interface” consistente ao
programador C, independente do dispositivo real que é acessado. Isto é,
o sistema de E/S de C provê um nível de abstração entre o programador
e o dispositivo utilizado. Essa abstração é chamada de stream e o
dispositivo real é chamado de arquivo.
Stream
O sistema de arquivos de C é projetado para trabalhar com uma ampla
variedade de dispositivos, incluindo terminais, acionadores de disco e
acionadores de fitas. Embora cada um dos dispositivos seja muito
diferente, o sistema de arquivo com buffer transforma-os em um
dispositivo lógico chamado stream, ou seja, a mesma função pode
escrever em um arquivo em disco ou em algum outro dispositivo, como
o console.
Stream
Existem dois tipos de streams: texto e binária.
Stream de Texto
Uma stream de texto é um seqüência de caracteres
organizadas em linhas terminadas por um caractere de nova
linha (“\n”).
Streams Binárias
Uma stream binária é uma seqüência de bytes com uma
correspondência de “um para um” com aqueles encontrados
no dispositivo externo, ou seja, o número de bytes escritos
(ou lidos) é o mesmo que o encontrado no dispositivo
externo.
Arquivos (1/2)
Em C, um arquivo pode ser qualquer coisa, desde um arquivo em disco
até um terminal ou uma impressora. Você associa uma stream com um
arquivo específico realizando uma operação de abertura (fopen()). Uma
vez o arquivo aberto, informações podem ser trocadas entre ele e o seu
programa.
Um arquivo é desassociado de uma stream específica por meio de uma
operação de fechamento (fclose()). Se um arquivo aberto para saída for
fechado, o conteúdo, se houver algum, de sua stream associada será
escrito no dispositivo externo. Esse processo é geralmente referido
como descarga (flushing) da stream e garante que nenhuma informação
seja acidentalmente deixada no “buffer de disco”. Todos os arquivos
são fechados automaticamente quando o programa termina
normalmente, com main() retornando ao sistema operacional ou uma
chamada exit(). Os arquivos “não” são fechados quando um programa
quebra (crash) ou quando ele chama abort().
Arquivos (2/2)
Cada stream associada a um arquivo tem uma estrutura de controle de
arquivo do tipo FILE. Essa estrutura é definida no arquivo cabeçalho
“stdio.h”.
typedef
struct {
short
level;
unsigned
flags;
char
fd;
unsigned char hold;
short
bsize;
unsigned char *buffer, *curp;
unsigned
istemp;
short
token;
} FILE;
A separação que C faz entre streams e arquivos pode parecer
desnecessária ou estranha, mas a sua principal finalidade é a
consistência da interface. Em C, você precisa pensar em termos de
stream e usar apenas um sistema de arquivos para realizar todas as
operações de E/S. O compilador C converte a entrada ou saída em
linha em uma stream facilmente gerenciada.
Fundamentos do Sistema de Arquivos
O sistema de arquivos C é composto de diversas funções
inter-relacionadas. Essas funções exigem que o cabeçalho “stdio.h”
seja incluído em qualquer programa em que são utilizadas.
Nome
Função
fopen()
Abre um arquivo
fclose()
Fecha um arquivo
fputc() e fputs()
Escreve um caractere e uma string no arquivo
fgetc() e fgets()
Lê um caractere e uma string de um arquivo
fseek()
Posiciona o arquivo em um byte específico
fprintf()
É para um arquivo o que printf() é para o console
fscanf()
É para um arquivo o que scanf() é para o console
feof()
Verdadeiro se o fim de arquivo for encontrado
ferror()
Verdadeiro se ocorreu um erro
rewind()
Recoloca o ponteiro de registro (indicador de
posição) no início do arquivo
remove()
Apaga um arquivo
fflush()
Descarrega um arquivo
O Ponteiro de Arquivo
Um ponteiro de arquivo é um ponteiro para informações que definem
várias coisas sobre o arquivo, incluindo seu nome, status e a posição
atual do arquivo. Basicamente, o ponteiro de arquivo identifica um
arquivo específico em disco e é usado pela stream associada para
direcionar as operações das funções de E/S.
Um ponteiro de arquivo é uma variável ponteiro do tipo FILE. Para ler
ou escrever arquivos, seu programa precisa usar ponteiros de arquivos.
Os ponteiros de arquivo devem declarados obedecendo a seguinte
sintaxe:
FILE *fp;
desta forma, indicamos fp como sendo um ponteiro para um arquivo.
Abrindo um Arquivo
A função fopen() abre uma stream para uso e associa um arquivo em
disco a ela. A função fopen() tem o seguinte protótipo:
FILE *fp;
fp = fopen(const char *nomearq, const char *modo);
onde:
nomearq
modo
uma cadeia de caracteres que forma um nome válido de
arquivo (pode incluir uma especificação de caminho),
define qual arquivo deverá aberto.
determina como o arquivo será aberto (para leitura,
para escrita, para leitura/escrita, etc.), define que tipo
de uso pode ser feito como o arquivo aberto.
nota:
A função fopen() devolve um ponteiro de arquivo. Seu arquivo nunca
deve alterar o valor desse ponteiro. Se ocorrer um erro quando estiver
tentando abrir um arquivo, fopen() devolve um ponteiro nulo (NULL).
“Modo” de abertura válidos (1/2)
Modo
Significado
"r"
Abre um arquivo texto para leitura. O arquivo deve existir antes de ser aberto.
"w"
Abrir um arquivo texto para gravação. Se o arquivo não existir, ele será
criado. Se já existir, o conteúdo anterior será destruído.
"a"
Abrir um arquivo texto para gravação. Os dados serão adicionados no fim do
arquivo ("append"), se ele já existir. Se o arquivo não existir, ele será
criado.
"rb"
Abre um arquivo binário para leitura. Igual ao modo "r" anterior, só que o
arquivo é binário.
"wb"
Cria um arquivo binário para escrita, como no modo "w" anterior, só que o
arquivo é binário.
"ab"
Acrescenta dados binários no fim do arquivo, como no modo "a" anterior,
só que o arquivo é binário.
“Modo” de abertura válidos (2/2)
Modo
Significado
"r+"
Abre um arquivo texto para leitura e gravação. O arquivo deve existir e
pode ser modificado.
"w+"
Cria um arquivo texto para leitura e gravação. Se o arquivo existir, o
conteúdo anterior será destruído. Se não existir, ele será criado.
"a+"
Abre um arquivo texto para gravação e leitura. Os dados serão
adicionados no fim do arquivo se ele já existir. Se o arquivo não existir,
ele será criado.
"r+b"
Abre um arquivo “binário” para leitura e escrita.
"w+b" Cria um arquivo “binário” para leitura e escrita.
"a+b"
Acrescenta dados ou cria uma arquivo “binário” para leitura e escrita.
Observações: Abrindo um Arquivo
Em muitas implementações, no modo texto, seqüências de retorno de
carro/alimentação de linha são traduzidas para caracteres de nova linha
(“\n”) na entrada. Na saída, ocorre o inverso: novas linhas são
traduzidas para retorno de carro/alimentação de linha.
Abrindo um arquivo no modo “leitura” ou no modo “escrita” o
ponteiro de registro (ou indicador de posição) é colocado no início do
arquivo, ou seja, no primeiro registro ou byte do arquivo. Abrindo o
arquivo no modo “adição”, ou append o ponteiro de registro é
colocado no fim do arquivo.
Verificando a existência de um arquivo:
FILE *fp
fp = fopen("saida.txt", "r");
if (fp == NULL)
printf("ERRO: arquivo não pode ser aberto.");
Fechando um Arquivo
A função fclose() fecha uma stream que foi aberta por meio de uma
chamada a função fopen(). Ela escreve no meio físico qualquer dado
que ainda permanece no buffer de disco no arquivo e, então, fecha
normalmente o arquivo em nível de sistema operacional.
Uma falha ao fechar a stream atrai todo tipo de problema, incluindo
perda de dados, arquivos destruídos e possíveis erros intermitentes em
seu programa. Uma fclose() também libera o bloco de controle de
arquivo associado a stream, deixando-o disponível para reutilização.
A função fclose() tem o seguinte protótipo:
int fclose(FILE *fp);
Onde fp é o ponteiro de arquivo devolvido pela chamada a fopen(). Um
valor de retorno zero significa uma operação de fechamento
bem-sucedida. Qualquer outro valor indica erro.
Verificando o “Final de Arquivo”
A função feof() determina quando o ponteiro de registro (indicador de
posição) atingiu a marca de final de arquivo (eof, end of file).
A função feof() tem o seguinte protótipo:
int feof(FILE *fp);
Onde fp é o ponteiro de arquivo devolvido pela chamada a fopen().
O valor de retorno igual 1 (true) significa que o “final de arquivo”
foi atingido e 0 (false) caso contrário.
por exemplo:
while (feof(fp) == 0)
ou
while (!feof(fp))
deve ser lido, ou interpretado como:
repete o processo enquanto “não” for final de arquivo
fread() e fwrite()
Para ler e escrever tipos de dados maiores que um byte (stream binário),
o sistema de arquivo C fornece duas funções: fread() e fwrite(). Essas
funções permitem a leitura e a escrita de blocos de qualquer tipo de
dados. Seus protótipos são:
size_t fread(void *rg, size_t numBytes, size_t n, FILE *fp);
size_t fwrite(const void *rg, size_t numBytes, size_t n, FILE *fp);
Para fread(), rg é um ponteiro para uma região de memória que receberá
os dados lidos do arquivo. Para fwrite(), rg é um ponteiro para as
informações que serão escritas no arquivo. O número de bytes a ler ou
escrever é especificado por numBytes. O argumento n determina
quantos itens (cada um contendo numBytes) serão lidos ou escritos.
Finalmente, fp é um ponteiro para uma stream aberta anteriormente.
Estas funções retornam o número de itens lidos ou escritos. Esse valor
será igual a n a menos que ocorra um erro.
Roteiro para Trabalhar com Arquivos Binários:
1. declarar a estrutura e a variável registro (struct);
2. declarar e inicializar os contadores e somatórios;
3. declarar o ponteiro de arquivo (FILE *fp);
4. abrir (fopen) o arquivo no modo desejado;
5. ler o “primeiro” registro do arquivo;
6. ler o arquivo até o final: while (!feof(fp)) { ... };
7. processar as informações, ou campos, do registro lido;
8. ler o “próximo” registro do arquivo;
9. fechar (fclose) o arquivo;
10. exibir os resultados obtidos.
// Este trecho de programa lê um arquivo do primeiro registro
// até o último (marca de final de arquivo- EOF).
...
struct RgFunc { 1
Lê um bloco com sizeof(varRgFunc) bytes e
char nome[35];
armazena dados lidos na variável varRgFunc.
char sexo;
float salario;
Obs. Este processo de leitura avança o indicador
} varRgFunc;
de
posição para o primeiro byte do próximo registro.
void main() {
int ctF = 0, ctM = 0; // declaração dos contadores
FILE *fp; 3
fp = fopen("func.dat", "rb");
2
4
printf("Processando os dados armazenados no arquivo...\n\n");
5
fread(&varRgFunc, sizeof(varRgFunc), 1, fp);
while (!feof(fp)) {
6 if (varRgFunc.sexo == ‘M’) 7
ctM = ctM + 1;
else
ctF = ctF + 1;
fread(&varRgFunc, sizeof(varRgFunc), 1, fp);
}
9 fclose(fp);
8
printf("Processamento concluído !\n\n");
printf("%2d funcionários do sexo feminino.\n", ctF);
printf("%2d funcionários do sexo masculino.\n", ctM);
}
1
0
MEMÓRIA SECUNDÁRIA
(disquete, HD, ...)
MEMÓRIA PRINCIPAL
ponteiro de registro
(indicador de posição)
varRgFunc


R1
R2
R3
...
RN
fread(&varRgFunc, sizeof(varRgFunc), 1, fp);
R1
registros do
arquivo “func.dat”
marca de final de arquivo (End Of File)
// Esta função realiza o cadastro de dados em um arquivo binário
struct RgAluno {
char sit; // situação do registro: X- ocupado e *- apagado
char nome[35];
float nota1;
Grava o bloco de sizeof(varRgFunc) bytes armazenados
float nota2;
na variável varRgFunc no arquivo “Alunos.dat”.
} varRgAluno;
Obs. Concluído o processo de gravação eof é atingido.
void Cadastrar(void) {
FILE *fp;
fp = fopen("Alunos.dat", "a+b");
char confirma;
while (1) {
clrscr(); printf("Informe o nome do aluno, (FIM) para encerrar:\n");
fflush(stdin); // limpa o buffer do teclado
gets(varRgAluno.nome);
if (strcmp(varRgAluno.nome, "FIM") == 0)
break;
printf("Informe a nota do 1o. Bimestre: "); scanf("%f", &varRgAluno.nota1);
printf("Informe a nota do 2o. Bimestre: "); scanf("%f", &varRgAluno.nota2);
do {
printf("\nConfirma os dados (s/n): ");
confirma = toupper(getche());
} while ((confirma != 'S') && (confirma != 'N'));
if (confirma == 'S') {
varRgAluno.sit = 'X'; // indica que a posição foi ocupada por um registro
fwrite(&varRgAluno, sizeof(varRgAluno), 1, fp);
}
}
fclose(fp);
}
fseek()
Operações de leitura (fread()) e escrita (fwrite()) aleatórias podem ser
executadas utilizando a ajuda da função fseek(), que modifica o
indicador de posição de arquivo. Seu protótipo é mostrado aqui:
int fseek(FILE *fp, id *rg, long numBytes, int origem);
Aqui, fp é um ponteiro de arquivo devolvido por uma chamada fopen().
numBytes, um inteiro longo, é o número de bytes a partir origem, que
se tornará a nova posição corrente, e origem é uma das seguintes
macros:
SEEK_SET
início do arquivo (primeiro byte)
SEEK_CUR
posição atual
SEEK_END
final do arquivo (último byte)
Portanto, para mover numBytes a partir do íncio do arquivo, origem
deve ser SEEK_SET. Para mover da posição atual, deve-se utilizar
SEEK_CUR e para mover a partir do final do arquivo, deve-se utilizar
SEEK_END. A função fseek() devolve 0 quando bem-sucedida e um
valor diferente de zero se ocorrer um erro.
ftell()
A função ftell() devolve o valor atual do indicador de posição de
arquivo para a stream especificada. Para arquivos binários, o valor é o
número de bytes cujo indicador está a partir do início do arquivo.Seu
protótipo é mostrado aqui:
long ftell(FILE *fp);
// Este trecho de programa calcula o número do registro
// dividindo o valor do indicador de posição pela
// quantidade de bytes do registro
printf("Posição Nome do Aluno.........................\n");
long pos;
// lê o primeiro registro
fread(&varRgAluno, sizeof(varRgAluno), 1, fp);
while (!feof(fp)) {
// não exibe os registro apagados
if (varRgAluno.sit == 'X') {
pos = ftell(fp) / sizeof(varRgAluno);
printf("%4ld
%s", pos, varRgAluno.nome);
}
// lê o próximo registro
fread(&varRgAluno, sizeof(varRgAluno), 1, fp);
}
// Esta função lê os registros do arquivo do final para o início.
void UltimoAoPrimeiro(void) {
FILE *fp;
fp = fopen("Alunos.dat", "rb");
printf("Posição Nome do Aluno..........................\n");
printf("===============================================\n");
// posiciona no primeiro byte do último registro
fseek(fp, 0L, SEEK_END);
long pos = ftell(fp) / sizeof(Aluno);
fseek(fp, (pos-1) * sizeof(Aluno), SEEK_SET);
// lê o último registro
fread(&varRgAluno, sizeof(varRgAluno), 1, fp);
while (pos != 0) {
// não exibe os registro apagados
if (varRgAluno.sit == 'X') {
printf("%4ld
%s", pos, varRgAluno.nome);
}
// posiciona no registro anterior
pos = pos - 1;
fseek(fp, (pos-1) * sizeof(varRgAluno), SEEK_SET);
// lê o registro anterior
fread(&varRgAluno, sizeof(varRgAluno), 1, fp);
}
fclose(fp);
}
fgetpos()
int fgetpos(FILE *fp, const fpos_t *posição);
A função fgetpos() armazena o valor atual do indicador de posição de
arquivo no objeto posição.
fsetpos()
int fsetpos(FILE *fp, const fpos_t *posição);
A função fsetpos() move o indicador de posição de arquivo para o
ponto espeficado pelo objeto apontado por posição. Esse valor deve
ser previamente obtido por meio de uma chamada a função fgetpos().
por exemplo:
FILE *fp
fpos_t posicaoSalva;
fgetpos(fp, &posicaoSalva); // posição original
// operações que deslocam o indicador de posição
// ...
fsetpos(fp, &posicaoSalva); // retorna a posição original
rewind()
A função rewind() reposiciona o ponteiro de registro, ou indicador de
posição de arquivo no início do arquivo especificado como seu
argumento. Isto é, ela “rebobina” o arquivo.
A função rewind() tem o seguinte protótipo:
void rewind(FILE *fp);
Onde fp é o ponteiro de arquivo devolvido pela chamada a fopen().
Nota:
1) o início de uma “stream de texto” é o primeiro caractere do arquivo
texto
2) o início de uma “stream binário” é o primeiro byte do arquivo
Apagando Arquivos
A função remove() apaga do diretório do disco (meio externo) o
arquivo especificado como seu argumento. Seu protótipo é:
int remove(const char *nomeArq);
Onde nomeArq representa o nome do arquivo que deverá ser
apagado. A função remove() retorna “zero”, para indicar que a
operação foi bem-sucedida, e um valor diferente de zero, caso
contrário.
por exemplo, para apagar o arquivo “saida.txt”:
if (remove("saida.txt") != 0)
printf("O arquivo não pode ser apagado.");
Esvaziando uma Stream
Para esvaziar o conteúdo de uma stream de saída, deve-se utilizar a
função fflush().
A função fflush() tem o seguinte protótipo:
int fflush(FILE *fp);
Essa função escreve o conteúdo de qualquer dado existente no buffer
para o arquivo associado a fp.
A função fflush() retorna “zero” para indicar sucesso; caso contrário,
devolve EOF.
Nota:
Esta função é muito útil em um processo de atualização quando se faz
leituras e gravações alternadamente.
Operações sobre Tabelas e/ou Arquivos (1/6)
Pesquisa
Esta é talvez a operação mais comum que se realiza sobre
tabelas e consiste basicamente em determinar em que posição
da tabela se encontra um determinado valor.
Há três formas básicas para localização de registros:
(1) pesquisa seqüencial (um registro após o outro),
(2) pesquisa binária (com o conjunto ordenado, divide-se o
conjunto em duas partes)
(3) pesquisa indexada ou direta (posiciona diretamente no
registro correspondente ao índice- usando o método SEEK).
Operações sobre Tabelas e/ou Arquivos (2/6)
Inclusão (ou Inserção)
Inserir um novo elemento (registro) ao conjunto.
Alteração (ou Modificação)
Operação que corresponde a necessidade de alterar uma
informação presente em uma tabela, seja por atualização de
informação (mudança de endereço, aumento de salário), seja
por correção de uma informação errada.
Operações sobre Tabelas e/ou Arquivos (3/6)
Exclusão (ou Remoção)
A remoção de um elemento de uma tabela quase sempre
implica refazer toda a tabela- deslocando os registros
subseqüentes para ocuparem a posição liberada pelo registro
excluído. Essa operação denomina-se REMOÇÃO FÍSICA.
Outra maneira de se remover registros da tabela é definir um
critério que os elimine. Uma forma é "marcar" o elemento a
ser removido, realizando uma operação denominada
REMOÇÃO LÓGICA. A marca pode ser feita, por exemplo,
alterando o conteúdo de um campo extra no registro com o
caracter "*".
Operações sobre Tabelas e/ou Arquivos (4/6)
Seleção (ou Separação)
Pode ocorrer a necessidade de se montar uma tabela com
apenas alguns elementos de uma outra tabela. Esta separação
pode objetivar um conjunto menor de informações relevantes
em uma determinada situação.
Por exemplo, montar uma tabela com os funcionários que
recebem salários acima de R$ 5.000,00.
Esta operação é também conhecida como filtro.
Operações sobre Tabelas e/ou Arquivos (5/6)
Junção (ou Justaposição)
Ocorre quando deseja-se inserir os registros de uma tabela ao
final de outra tabela, criando então uma terceira tabela.
Isto pode ocorrer, por exemplo, pela fusão de dois ou mais
departamentos em um único, sendo necessário unificar as
tabelas de funcionários de cada departamento em uma única
tabela.
Operações sobre Tabelas e/ou Arquivos (6/6)
Intercalação
Quando ocorre a junção de duas ou mais tabelas ordenadas
segundo o mesmo campo, é desejável que a nova tabela
resultante também esteja ordenada.
Para isto, nem sempre podemos simplesmente justapor as
tabelas, sendo necessário intercalar os registros das tabelas
segundo os valores presentes no campo pelo qual as tabelas
estão ordenadas.
Formas de Acesso em um Arquivo (1/3)
Os arquivos criados em meio magnético poderão ser
acessados para leitura e/ou escrita na forma seqüencial, direta
ou indexada.
Arquivo de Acesso Seqüencial
O acesso seqüencial ocorre quando o processo de gravação e
leitura é feito de forma contínua, um após o outro. Desta
forma, para se gravar um novo registro é necessário percorrer
todo o arquivo a partir do primeiro registro, registro a registro,
até localizar a primeira posição vazia após o último registro.
O processo de leitura também ocorre de forma seqüencial.
Se o registro a ser lido é o último, primeiro será necessário
ler todos os registros que o antecedem.
Formas de Acesso em um Arquivo (2/3)
Arquivo de Acesso Direto
O acesso direto (conhecido também por acesso randômico) ocorre
através de um campo chave previamente definido. Desta forma,
passa-se a possuir um vínculo existente entre um dos campos do registro
e sua posição de armazenamento, através da chave. Assim sendo, o
acesso a um registro tanto para leitura como para escrita poderá ser feito
de forma instantânea, utilizando a instrução FSEEK. Isto implica no
fato de que os registros de um arquivo direto possuem um lugar (chave)
previamente "reservado" para serem armazenados.
O termo chave é utilizado para estabelecer o campo de um registro que
será utilizado no processo de localização do registro no arquivo.
No acesso direto, será possível acessar um determinado registro
diretamente, sem se preocupar com os registros que o antecedem.
Formas de Acesso em um Arquivo (3/3)
Arquivo de Acesso Indexado
O acesso indexado ocorre quando se acessa de forma direta um arquivo
seqüencial. Na sua maioria, todo arquivo criado armazena os registros de
forma seqüencial. A forma seqüencial de acesso torna-se inconveniente,
pois à medida que o arquivo aumenta de tamanho, aumenta também o
tempo de acesso ao mesmo.
Para se trabalhar com esta técnica, basta criar um arquivo direto que será
o índice de consulta do arquivo seqüencial, passando este a ser o arquivo
indexado. Assim sendo, existirão dois arquivos: o arquivo índice
(arquivo direto) e o arquivo indexado (arquivo seqüencial). Os acessos
serão feitos como em um livro, primeiro se consulta o arquivo índice, o
qual possui a chave de pesquisa, no caso seria o número da página,
depois basta se posicionar de forma direta na página identificada no
índice, ou seja, no registro do arquivo indexado.
Glossário - Parte II
• Chave: campo ou combinação de campos utilizados
para alguma operação de pesquisa.
• Chave Primária: campo que apresenta um valor
diferente para cada registro da tabela (código)
• Chave Secundária: chave auxiliar de pesquisa devido
a probabilidade de repetição de valores (nome)
• Pesquisa: localizar um determinado registro
utilizando como argumento de pesquisa o campo
chave.
Operações sobre Arquivos Texto (1/2)
fgets(), lendo “uma string de caracteres” de um arquivo aberto no
modo leitura.
char *fgets(char *str, int length, FILE *fp);
a função fgets() retorna EOF quando o final do arquivo for alcançado.
fputs(), escrevendo “uma string de caracteres” em um arquivo aberto
no modo escrita.
int fputs(const char *str, FILE *fp);
onde:
fp é um ponteiro de arquivo do tipo FILE devolvido por fopen();
str é uma variável do tipo cadeia de caracteres (char *str)
length quantidade de caracteres que serão lidos
Usando fopen(), fputs() e fclose():
Esta função cria um arquivo texto denominado “saida.txt” gravando
as cadeias de caracteres digitadas pelo usuário final.
void GravarTxt(void) {
FILE *fp;
fp = fopen("saida.txt", "w"); // cria o arquivo “saida.txt”
char str[80];
clrscr();
printf("Digite um texto mensagem, (FIM) para encerrar:\n");
fflush(stdin); // limpa o buffer do teclado
while (1) {
gets(str);
if (strcmp(str, "FIM") == 0)
break;
strcat(str, "\n"); // acrescenta o fim de linha
fputs(str, fp); // grava uma linha no arquivo texto
}
fclose(fp);
}
Usando fopen(), fgets(), feof() e fclose():
Esta função lê um arquivo texto denominado “saida.txt” exibindo na
tela as cadeias com no máximo 80 caracteres lidas.
void LerTxt(void) {
FILE *fp;
fp = fopen("saida.txt", "r");
char str[80];
clrscr();
printf("Texto lido......\n");
// lê uma linha de 80 caracteres do arquivo texto
fgets(str, 80, fp);
while (!feof(fp)) {
printf("%s", str);
fgets(str, 80, fp);
}
fclose(fp);
gotoxy(01, 24); printf("Pressione [algo] para prosseguir.");
getch();
}
Operações sobre Arquivos Texto (2/2)
fgetc() ou getc(), lendo “um” caractere de um arquivo aberto no
modo leitura.
int fgetc(FILE *fp);
a função fgetc() retorna EOF quando o final do arquivo for alcançado.
fputc() ou putc(), escrevendo “um” caractere em um arquivo aberto
no modo escrita.
int fputc(int ch, FILE *fp);
onde:
fp é um ponteiro de arquivo do tipo FILE devolvido por fopen().
Por razões históricas estas funções retornam um inteiro (que
automaticamente é convertido para sua representação em caractere).
Usando fopen(), fgetc(), feof() e fclose():
Esta função lê os caracteres do arquivo indicado pela variável
“nomeArq” enquanto o final de arquivo não for atingido.
void LerTxt(char nomeArq[80]) {
clrscr();
FILE *fp;
fp = fopen(nomeArq, "r");
if (fp == NULL)
printf("O arquivo \"%s\" não pode ser aberto.", nomeArq);
else {
char ch;
clrscr();
ch = fgetc(fp); // lê um caractere do arquivo texto
while (!feof(fp)) {
putchar(ch);
ch = fgetc(fp); // lê um caractere do arquivo texto
}
fclose(fp);
}
gotoxy(01, 24); printf("Pressione [algo] para prosseguir.");
getch();
}
Finalidade dos Sistemas de Gerenciamento BD (1/5)
Eliminar / minimizar problemas de um sistema de
processamento de arquivos, por exemplo, arquivos criados
por aplicações desenvolvidas em Linguagem C:
FILE *fp;
Sistemas de processamento de arquivos = registros
permanentes são armazenados em vários arquivos e diversos
programas de aplicação são escritos para extrair e gravar
registros nos arquivos apropriados.
a) Redundância: vários arquivos com as mesmas
informações.
b) Inconsistência: dados que são alterados em um arquivo e
não o são em outros.
Finalidade dos Sistemas de Gerenciamento BD (2/5)
c) Dificuldades de Acesso: são necessários programas
específicos para recuperar informações (é necessário
conhecer a estrutura, ou a relação de campos, do registro).
d) Isolamento dos Dados: os dados estão dispersos em vários
arquivos, e estes arquivos podem apresentar diferentes
formatos (dificuldade em desenvolver novas aplicações).
e) Problemas de Integridade: programas devem garantir a
manutenção de restrições de integridade (consistência).
Restrições de Integridade = regras que estabelecem quando
uma base de dados está correta.
Finalidade dos Sistemas de Gerenciamento BD (3/5)
f) Problemas de atomicidade: um sistema de processamento
esta sujeito a falhas. Mecanismos simples como cópias não
são suficientes. Após falhas o banco de dados deve ser
recuperado rapidamente, em seu último estado consistente.
Por exemplo, um programa deve transferir R$ 50,00 da
conta A para uma conta B. Se ocorrer falha no sistema
durante a execução, é possível que os R$ 50,00 sejam
debitados da conta A sem serem creditados na conta B,
criando um estado inconsistente no banco de dados.
Operação Atômica:
deve ocorrer por completo ou não deve ocorrer.
Finalidade dos Sistemas de Gerenciamento BD (4/5)
g) Problemas de Concorrência: acesso por múltiplos usuários.
Os programas devem implementar controle de acesso
concorrente (programação complexa).
Dois clientes retiram
fundos da conta A
“simultaneamente”
conta A Saldo Inicial = R$ 500,00
saque de
R$ 100,00
saque de
R$ 50,00
Qual será o Saldo Final ?
R$ 400,00 - R$ 450,00 - R$ 350,00
Finalidade dos Sistemas de Gerenciamento BD (5/5)
h) Problemas de Segurança: nem todos os usuários de banco
de dados estão autorizados ao acesso a todos os dados.
i) Outros: integridade de armazenamento no caso de quedas
do sistema.
O principal objetivo de um “Sistema de Gerenciamento
de Banco de Dados” é proporcionar um ambiente,
conveniente e eficiente, para a recuperação e
armazenamento das informações do banco de dados.
E/S – Exemplos (01)
/* Cada caracter digitado ser gravado no arquivo */
c = getchar();
while (!feof(stdin)) { // Quando digitado CTRL+Z
fputc(c, pa); c = getchar();
}
rewind(pa); /* volta ao inicio do arquivo */
printf("\nTerminei de escrever, agora vou ler.\n");
c = fgetc(pa);
while (!feof(pa)) {
putchar(c); c = fgetc(pa);
}
fclose(pa);
getchar(); /* Espera o usuario digitar alguma coisa */
}
E/S – Exemplos (02)
/*Programa descrição: Escreve e em seguida lê cadeias de um arquivo. */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAX 80
int main (void ) {
char linha[MAX];
FILE *pa;
char *nome = "texto.txt";
/* Abre o arquivo para leitura e escrita */
if (( pa = fopen(nome, "w+")) == NULL) {
printf("\n\nNao foi possivel abrir o arquivo.\n");
exit(1);
}
E/S – Exemplos (02)
/* Cada linha digitada sera gravada no arquivo */
gets(linha);
while (!feof(stdin)) {
strcat(linha, "\n");
fputs(linha, pa);
gets(linha);
}
rewind(pa);
/* volta ao inicio do arquivo */
printf("\nTerminei de escrever, agora vou ler.\n");
fgets(linha, MAX, pa);
while (!feof(pa)) {
printf("%s",linha);
fgets(linha, MAX, pa);
}
fclose(pa);
getchar();
}
E/S – Exemplos (03)
//O programa abaixo ilustra como podemos escrever e ler estruturas.
#include<stdio.h>
#include<stdlib.h>
#define MAX 4
int main () {
FILE *pa;
char nome[40];
struct pessoa {
char nome[40];
int ano;
} turma [MAX], back[MAX];
int i;
for (i=0; i<MAX; i++) {
puts("Nome ? ");
gets(turma[i].nome);
fflush
(stdin);
puts("Ano ? "); scanf("%d", &turma[i].ano);
fflush (stdin);
}
E/S – Exemplos (03)
puts ("\nImprimindo\n");
for (i=0; i<MAX; i++) {
printf("Nome = %s\n", turma[i].nome);
printf("Ano = %d\n\n", turma[i].ano);
}
puts("\nGravando\n");
puts("Qual o nome do arquivo?"); gets(nome);
if (( pa = fopen(nome, "w+b")) == NULL ) {
puts("Arquivo nao pode ser aberto");
exit(1);
}
for (i=0; i<MAX; i++) {
if (fwrite( &turma[i], sizeof (struct pessoa), 1, pa) != 1)
puts("Erro na escrita.");
}
E/S – Exemplos (03)
rewind(pa);
for (i=0; i<MAX; i++) {
if (fread( &back[i], sizeof (struct pessoa), 1, pa) != 1) {
puts("Erro na escrita.");
if (feof(pa)) break;
puts("Erro na leitura.");
}
}
puts("Imprimindo o vetor lido.");
for (i=0; i<MAX; i++) {
printf("Nome = %s\n", back[i].nome);
printf("Ano = %d\n\n", back[i].ano);
}
exit(0);
}
Referência
• C, Completo e Total
– Herbert Schildt
– E/S com Arquivo, páginas 219..252.
Download

Document