Enviar um café pro programador

Pode me ajudar a transformar cafeína em código?

Operações com arquivos: Modos de abertura (FILE e fopen), fechamento (EOF, fclose e fcloseall), modo texto e binário

No tutorial passado de nossa apostila de C, falamos sobre o que são e a importância dos arquivos em linguagem C, onde mostramos que para termos programas mais robustos e flexíveis, seria necessário fazer uma comunicação entre nossos aplicativos e o sistema.

Essa comunicação é feita através dos arquivos, que servem tanto para fornecer informações para nossos programas, como para salvar dados deles.

Neste artigo vamos entrar em detalhes sobre a abertura, fechamento e outras operações com arquivos.

O que são arquivos

Basicamente, arquivos são dados.
Mas por dados, podemos estar dizendo muita coisa.
No contexto geral, quando falamos em arquivos podemos estar nos referindo por um arquivo de texto, uma imagem, uma música, um programa de edição de imagem ou o executável que você criou estudando pelo nosso curso de C.

Porém, para o computador (leia-se: hardware), é tudo bit.
Tudo é visto como 1s (true, estado alto) ou 0s (false, estado baixo).
Na verdade, quem já estudou eletrônica, sabe que isso de 1 e 0 é apenas uma abstração, o que ocorre de fato é a existência de voltagem baixa (bit 0) e alta (bit 1), ou existência ou não de corrente.

Existem diversas maneiras de se representar um arquivo, além de toda uma estrutura e hierarquia de dados, sendo o sistema de arquivos de um sistema operacional um ramo bem complexo de estudo.

Em nossa apostila, você pode entender o conceito de arquivo apenas como uma série de bytes, de informações dispostas de maneira sequencial (stream), onde teremos informações e controle sobre seu início e fim.

Modo texto e binário

Como dissemos ao longo de nosso curso, a linguagem C é extremamente poderosa e nos oferece a possibilidade da fazer praticamente qualquer coisa com nossas máquinas (e não só microcomputadores).

Uma dessas capacidades é possibilidade de trabalhar com arquivos no modo binário.
Ou seja, podemos abrir, criar e salvar um arquivo no modo binário.
Por exemplo, é possível fazer uma cópia binária de arquivo. Isto é, pegar bit por bit de um executável e colocar em outro arquivo, duplicando o arquivo.

Porém, hardware é que se dá bem e gosta de trabalhar com bit (quem programa em Assembly também :). Para humanos, uma lista de milhões ou bilhões de números 1 e 0 é algo quase impossível de ser interpretado.

Por isso, os programadores geralmente trabalham com arquivos em modo texto, que será o foco aqui de nossa apostila. Basicamente iremos tratar de arquivos em texto, como os de extensão .txt, que nada mais serão do que uma lista de caracteres (dígitos, letras e símbolos, como espaço e acentuação).

Mais adiante neste tutorial, iremos explicar em detalhes como trabalhar com arquivos em modo texto ou modo binário.



Como abrir um arquivo: A função fopen() e o ponteiro FILE

Para abrir, alterar ou criar um arquivo em seu HD é necessário criar uma espécie de vínculo entre seu programa e um determinado endereço de memória, onde está localizado (ou vai se localizar) o arquivo.

Essa ligação é um pouco mais complexa e de 'baixo nível', mas isso não será motivo de preocupação, pois na linguagem C existe um tipo de dado, o FILE, que serve para trabalharmos especificamente com arquivos, e já está inclusive na biblioteca padrão do C, assim, podemos usar sem nos preocupar em incluir biblioteca ou criar algum código.

Essa ligação, entre nosso programa e seu disco de dados é feita através de um ponteiro.
Tal ponteiro é do tipo FILE, e o nome você escolhe.
A sintaxe é a de criação de um ponteiro:
FILE *arquivo;

Assim, nova variável arquivo, do tipo FILE, será a relação entre nosso programa em C e o arquivo que iremos trabalhar. 
Porém, sabemos que o ponteiro deve apontar para um tipo específico de dados, que no nosso caso é o tipo FILE.

Mas ele precisa apontar para 'alguém', só ter a referência (endereço de memória onde o ponteiro aponta) não serve de muita coisa. E é aí que entra a função fopen().

A função fopen() serve para especificarmos algumas características do arquivo.
Especificamente, como pode ser visto na declaração desta função, precisamos fornecer dois dados:
Endereço do arquivo no seu computador (representado por uma string)
Modo de abertura do arquivo (outra string contendo o método de abertura)

O protótipo dessa função é:
FILE *fopen(const char *filename, const char *mode)

Nome de um arquivo

Para associar nossa variável ponteiro para um arquivo que será lido ou criado em seu computador, é necessário que você forneça a localização desse arquivo, afinal o C não pode adivinhar isso.

Isso é feito através de uma string passada para a função fopen(), e essa string deve conter o endereço completo do arquivo, e isso inclui o nome do arquivo.
Vamos supor que vamos trabalhar com um arquivo chamado "arquivo.txt".

Se queremos usar este arquivo e ele está na MESMA PASTA (ou diretório) do seu executável, precisamos apenas fornecer o nome completo (com extensão) do arquivo:
"arquivo.txt"

E caso ele esteja em outra pasta, como no C: ?
Fornecemos o caminho inteiro, que é C:\arquivo.txt

Porém, aqui vai um detalhe que é um erro bem comum entre iniciantes: para representar a barra "\" em uma string, usamos DUAS BARRAS: \\

Assim, nossa string seria: "C:\\arquivo.txt"
Se o arquivo estiver numa pasta (vamos chamar de "meus_arquivos", dentro da pasta que está o executável, fazemos: "meus_arquivos\\arquivo.txt"

Ou podemos pedir o endereço para o usuário, que poderá escrever usando apenas uma barra "\", pois a representação dela em string é "\\" e o C faz essa interpretação automaticamente.

Modos de abertura: read (r), write (w) e append(a)

read(r) - Leitura de arquivo

r

Sempre que quisermos ler um arquivo, usamos o modo read, que será representado simplesmente pela letra "r":
FILE *arquivo = fopen("arquivo.txt", "r");

Isso faz o arquivo ser aberto unicamente para ser lido (não podemos alterar nada).

r+

Se adicionarmos um sinal de "+" no "r", será possível abrir o arquivo tanto para leitura como para escrita, e caso ele não exista, será automaticamente criado.
E caso exista, o seu conteúdo anterior será apagado e substituído pelo novo:
FILE *arquivo = fopen("arquivo.txt", "r+")

rb

Abre o arquivo para leitura, mas em modo binário:
FILE *arquivo = fopen("arquivo.txt", "rb")

write(w) - Escrita em arquivo

w

Para abrirmos um arquivo para escrita (colocar informações do nosso programa no arquivo), usamos a letra "w". Esse modo automaticamente cria o arquivo, ou substitui seu conteúdo anterior:
FILE *arquivo = fopen("arquivo.txt", "W")

w+

Abertura do arquivo tanto para leitura como para escrita.
Caso já exista o arquivo, seu conteúdo será substituído:
FILE *arquivo = fopen("arquivo.txt", "w+")

wb

Escrita em arquivos no modo binário:
FILE *arquivo = fopen("arquivo.txt", "wb")

append(a) - Escrevendo ao final do arquivo (anexando)

a

Ao usar o "w" para escrever (write), começamos a inserir informação no início do arquivo.
Mas, e se quisermos inserir ao final? Se não quisermos apagar o que tinha lá antes?

Para isso vamos ANEXAR, informações! Anexar em inglês é append, e fazemos isso abrindo o arquivo usando o modo de abertura "a":
FILE *arquivo = fopen("arquivo.txt", "a")

a+

Caso queiramos abrir um arquivo para leitura ou para escrita ao FINAL do arquivo (anexar), usamos o símbolo "+" depois da letra "a":
FILE *arquivo = fopen("arquivo.txt", "a+")

ab

Por afim, assim como na leitura "rb" e escrita binária "wb", podemos anexar informações ao final do arquivo, de maneira binária usando o "ab":
FILE *arquivo = fopen("arquivo.txt", "ab")

EOF (end of file), final do arquivo  - Fechando arquivos com fclose() e fcloseall()

Para saber o final do arquivo (que para o sistema, é uma sequência de bytes), o C vai procurar um sinal, uma constante conhecida por EOF, que sinaliza o final do arquivo.

Para identificar o final de um arquivo, podemos usar a função fclose, que recebe um ponteiro para o tipo FILE e retorna um inteiro:
int fclose(FILE *arq)

Caso o byte lido represente o EOF, a função "fecha" a abertura do arquivo. Ou seja, libera a memória associado ao ponteiro do FILE*.

Assim como ensinamos em ponteiros, ao usar a função free() para liberar memória alocada, fechar os arquivos que você não está mais usando é uma excelente prática de programação.

E caso trabalhe com diversos arquivos, você poderá fechá-los todos de uma vez, através da função:
int fcloseall()

Erros em abertura de arquivos

Quando abrimos um arquivo, o ponteiro que criamos do tipo FILE armazenará o endereço de um arquivo.

Porém, nem sempre esta tarefa é possível, gerando um erro.
Quando este erro ocorre o ponteiro irá apontar para NULL, sendo essa prática (checagem se o ponteiro aponta para NULL) muito importante para o tratamento de erros na abertura de arquivos em C.

Este erro pode ocorrer por vários motivos. O mais óbvio é abrir um arquivo que não existe (geralmente ocorre quando escrevemos errado o endereço do arquivo).

Em sistemas operacionais mais seguros, como os Linux, o acesso aos arquivos (seja para leitura, escrita ou anexação) é geralmente limitado, não sendo possível acessar e alterar qualquer arquivo (como geralmente ocorre em sistemas mais vulneráveis, como o Windows).

Caso você tente alterar ou acessar um arquivo sem permissão, a função fopen também irá retornar NULL para o ponteiro.

if(arquivo == NULL)
   printf("Nao foi possivel abrir o arquivo!");

PS: agradecimento ao leitor steniovm, que ajudou na correção do artigo :D

12 comentários:

Jucimara Rodrigues -- Jucinha disse...

Ola, tudo bem?
Essa ultima parte if(arquivo == NULL)
printf("Nao foi possivel abrir o arquivo!");

meu programa não imprime =/, ele imprimi outra mensagem "segmentation fault (core dumped) linux"

Anônimo disse...

Jucimara, tente colocando a função dessa maneira:

if((arquivo=fopen("arquivo.txt","r"))==NULL)

Att.

Anônimo disse...

segmentation fault é causado por problema de alocação de memória. Você deve estar tentando ler/escrever em um local não inicializado, ou algo semelhante.

Unknown disse...

Como faço para pedir par o usario fazer a leitura de um caminho de uma pasta. sendo que normalmente indicamos este caminho.

sky-10 disse...

E se eu quiser mostrar tudo que está dentro do arquivo .txt?
como fazer?

Anônimo disse...

Texto muito útil. Me ajudou bastante.

Unknown disse...

Fernando
Opa Tudo bem?

Eu gostaria de saber se tem como alterar informações de um arquivo no formato txt na linguagem c?

Por exemplo eu crio um arquivo txt contento 5 nomes e quero alterar a idade do Fernando de 34 para 40... Como eu faço isso?

Exemplo:

nome: Idade:

Fernando 34
Leticia 50
Daniel 70
Barbara 23
Pedro 50

Anônimo disse...

//Parabéns para Site, Ótimas dicas, e didática eficiente!!!

Unknown disse...

Opa, boa noite!
Quanto a abrir arquivos que estão em outra pasta, tem alguma maneira de não inserir o caminho completo?
Por exemplo:
Gero um .exe e peço para que ele abra o arquivo CONFIG.txt, o qual está na mesma pasta, na função fopen uso fileCONFIG = fopen("CONFIG.txt", "r"); e maravilha.
Se o CONFIG.txt estiver na pasta CONFIGURACAO, um nível acima do .exe, devo inserir o caminho completo fileCONFIG = fopen("C:\\USUARIO\\PROGRAMA\\EXECUTAVEL\\CONFIGURACAO\\CONFIG.txt", "r"); e maravilha, tudo funciona.
Porém pego esse .exe e o levo para um cliente, onde no computador dele o caminho é desconhecido, como devo endereçar a função fopen?

Unknown disse...

Opa, bom dia! Consegui resolver!
fileCONFIG = fopen("CONFIGURACAO\\CONFIG.txt", "r");
Ou seja, bastou apenas eu descrever o nome da pasta onde estava o arquivo.

Anônimo disse...

r+ não está criando arquivo, apenas esta alterando o arquivo já existente e também não está apagando os dados anteriores! Corrijam isso!

Calisto disse...

E eu aqui se matando para tentar criptografar os arquivos do meu linux hahahah agora ta explicado. valeu