.. | ||
README.org |
Curso Básico da Linguagem C
Aula 16: Abertura de arquivos para leitura e escrita
Modos de abertura para leitura e escrita
As strings de modos "r"
, "w"
e "a"
, da função fopen
, quando sucedidas do
caractere +
, indicam a abertura do arquivo também para suas respectivas
operações contrarias:
Modo | Leitura | Escrita | Truncar | Offset | Criar |
---|---|---|---|---|---|
"r" |
Sim | Não | Não | Início | Não |
"r+" |
Sim | Sim | Não | Início | Não |
"w" |
Não | Sim | Sim | Início | Sim |
"w+" |
Sim | Sim | Sim | Início | Sim |
"a" |
Não | Sim | Não | Fim | Sim |
"a+" |
Sim | Sim | Não | Fim | Sim |
De acordo com a tabela acima, nós precisamos observar alguns pontos importantes:
- Se a string de modo iniciar com
w
, o arquivo sempre será truncado na sua abertura. - Se a string de modo iniciar com
a
, o ponteiro interno sempre será posicionado no fim do arquivo na sua abertura. - Se a string de modo iniciar com
r
, o ponteiro interno sempre será posicionado no início do arquivo na sua abertura. - Se não existir, o arquivo só será criado se a string de modo
iniciar com
w
oua
.
Manipulação do ponteiro interno
O ponteiro interno do arquivo representa o número do byte que será lido
ou escrito no próximo acesso: ou seja, seu offset (ou ponto de partida).
Isso é particularmente importante quando abrimos arquivos para ambas as
operações com as strings de modo "r+"
ou "a+"
, visto que o modo "w+"
truncará o conteúdo do arquivo.
A função fseek
As funções da família fseek
(stdio.h
) são utilizadas para posicionar
ou obter informações sobre o ponteiro interno do arquivo:
Função | Descrição | Retorno |
---|---|---|
fseek |
Define a posição do ponteiro interno. | Sucesso: 0 (int ) |
ftell |
Obtém a posição corrente do ponteiro interno. | Sucesso: número do byte (long ) |
rewind |
Posiciona o ponteiro interno no byte 0 . |
void |
fsetpos |
Interface para fseek referenciando a definição do offset a partir do início do arquivo. |
Sucesso: 0 (int ) |
fgetpos |
Interface para ftell escrevendo o offset obtido em um dado endereço. |
Sucesso: 0 (int ) |
Notas:
- Com a função
fseek
, o novo offset é definido com uma expressão do tipolong
. - Com a função
fsetpos
, o novo offset é definido em um ponteiro do tipofpos_t
. - Com a função
fgetpos
, o offset corrente é escrito em um ponteiro do tipofpos_t
.
A função
fseek
trabalha com streams, mas é possível manipular o ponteiro interno a partir de descritores de arquivos com a chamada de sistemalseek
.
Pontos de origem de fseek
A função fseek
recebe 3 argumentos:
- O stream associado ao arquivo;
- A expressão do valor do offset (tipo
long
); - Um inteiro determinando o ponto de origem para a definição do novo offset.
Este é o seu protótipo na glibc
(stdio.h
):
int fseek(FILE *stream, long offset, int whence);
O termo whence significa "a partir de onde" e representa o ponto de origem do deslocamento do ponteiro interno do arquivo.
O terceiro argumento (whence
) pode receber qualquer valor inteiro, mas
a glibc
oferece três macros para os pontos de origem mais comuns:
SEEK_SET
: o início do arquivo;SEEK_END
: o fim do arquivo;SEEK_CUR
: o offset corrente.
Abertura de arquivos com "r+"
Como o offset inicial será o byte 0
, se a primeira operação for uma
escrita, os primeiros bytes do arquivo serão sobrescritos. Caso esta
não seja a intenção, será preciso deslocar o ponteiro interno para o
fim do arquivo:
// Abertura do arquivo para leitura e escrita...
char *file = "arquivo.txt";
FILE *stream = fopen(file, "r+");
// Posicionar offset no fim do arquivo...
fseek(stream, 0, SEEK_END);
// Rotinas de escrita...
Aqui, nós deslocamos o ponteiro interno em zero bytes a partir do fim do
arquivo (origem em SEEK_END
).
Abertura de arquivos com "a+"
O offset inicial será o fim do arquivo e, se quisermos começar a leitura a partir de seu início, será preciso deslocar o ponteiro interno para lá:
// Abertura do arquivo para leitura e escrita...
char *file = "arquivo.txt";
FILE *stream = fopen(file, "r+");
// Posicionar offset no início do arquivo...
fseek(stream, 0, SEEK_SET);
// Rotinas de leitura...
Desta vez, nós deslocamos o ponteiro interno em zero bytes a partir do
início do arquivo (origem em SEEK_SET
).
Procedimentos úteis
Para encerrar esta aula (e o nosso curso), aqui estão alguns exemplos de procedimentos gerais úteis envolvendo a manipulação de arquivos.
Quantidade de linhas
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
// Abertura do arquivo para leitura...
char *file = "meuarquivo.txt";
FILE *stream = fopen(file, "r");
if (!stream) {
fprintf(stderr, "Erro ao abrir o arquivo!\n");
return EXIT_FAILURE;
}
// Contagem de linhas...
char buf[BUFSIZ]; // Buffer para ler blocos do arquivo (8192 bytes).
int lines = 0; // Total de linhas encontradas.
// Lê uma linha até encontrar \n, até BUFSIZ-1 ou até EOF...
while (fgets(buf, BUFSIZ, stream) != NULL) {
// Só conta uma linha se houver \n no buffer...
if (buf[strlen(buf) - 1] == '\n' ) lines++;
}
// Se a última linha não contiver \n, ela também será contada...
if (lines > 0 && buf[strlen(buf) - 1] != '\n') lines++;
// Fechamento do arquivo...
fclose(stream);
// Impressão do resultado...
printf("%d linhas", lines);
return EXIT_SUCCESS;
}
Obter o tamanho do arquivo
#include <stdio.h>
#include <stdlib.h>
int main(void) {
// Abertura do arquivo...
char *file = "meuarquivo.txt";
FILE *stream = fopen(file, "rb");
if (!stream) {
perror("Erro ao abrir o arquivo");
return EXIT_FAILURE;
}
// Posiciona o ponteiro de leitura no final do arquivo...
if (fseek(stream, 0, SEEK_END) != 0) {
perror("Erro ao deslocar o offset para o final");
fclose(stream);
return EXIT_FAILURE;
}
// Obtém o offset corrente (tamanho do arquivo em bytes)...
long fsize = ftell(stream);
if (fsize == -1L) {
perror("Erro ao obter o offset corrente");
fclose(stream);
return EXIT_FAILURE;
}
// Exibe o tamanho do arquivo...
printf("Tamanho do arquivo: %ld bytes\n", fsize);
// Fechamento do arquivo...
fclose(stream);
return EXIT_SUCCESS;
}
Testar se um arquivo está vazio (0 bytes)
#include <stdio.h>
#include <stdlib.h>
int main(void) {
// Abertura do arquivo...
char *file = "meuarquivo.txt";
FILE *stream = fopen(file, "r");
if (!stream) {
perror("Erro na abertura do arquivo");
return EXIT_FAILURE;
}
// is_empty recebe 1 se o arquivo estiver vazio...
int is_empty = (fseek(stream, 0, SEEK_END) == 0 && ftell(stream) == 0);
// Fechamento do arquivo...
fclose(stream);
// O programa terminará com sucesso (0) se o arquivo estiver vazio...
return !is_empty;
}