C Escrever Arquivos
Você já sabe abrir um arquivo (fopen e fclose) e ler o que tem dentro (Ler Arquivos). Falta o caminho de volta: escrever. A boa notícia é que você já conhece quase tudo, porque escrever num arquivo é muito parecido com escrever na tela: o printf tem um irmão, o fprintf, que faz o mesmo num arquivo. Nesta aula você vai gerar a lista de compras e o relatório de vendas da lojinha da Ana, entender por que o texto às vezes demora para chegar ao disco (o buffer) e aprender a ter certeza de que o arquivo foi mesmo salvo.
- As três funções de escrita:
fputc(um caractere),fputs(um texto) efprintf(texto formatado). - Por que o
fputsnão pula linha sozinho. - Como gravar um relatório CSV a partir de um array de structs.
- Como acrescentar ao fim de um registro (log) com o modo
"a". - O que é o buffer, o que o
fflushfaz e o que se perde quando o programa cai. - Como conferir se a gravação deu certo, até o
fclose, e como salvar sem risco de perder o arquivo antigo.
Todos os programas desta aula foram compilados com o gcc 15.2 na máquina fictícia da Ana (usuária ana, computador notebook), na pasta ~/c, com o comando padrão do guia: gcc -std=c23 -Wall -Wextra arquivo.c -o programa.
A ideia: três jeitos de escrever no caderno
No caderno da lojinha, a Ana escreve de três jeitos:
- um sinal só, como um
*para marcar um item importante; - uma frase pronta, copiada como está: "arroz";
- um formulário com espaços para preencher: "___ pacotes de café, total R$ ___".
Em C, são três funções, todas do <stdio.h>, e todas recebem o FILE * do arquivo aberto:
| No caderno | Função | Exemplo | A versão "tela" |
|---|---|---|---|
| Um sinal | fputc ("file put character") | fputc('*', arq); | putchar('*'); |
| Uma frase pronta | fputs ("file put string") | fputs("arroz\n", arq); | fputs("arroz\n", stdout); |
| Um formulário | fprintf ("file print formatted") | fprintf(arq, "%d pacotes\n", 2); | printf("%d pacotes\n", 2); |
1. As três funções juntas
#include <stdio.h>
int main(void) {
FILE *arq = fopen("compras.txt", "w");
if (arq == NULL) {
perror("compras.txt");
return 1;
}
fputc('*', arq);
fputc('\n', arq);
fputs("arroz\n", arq);
fputs("feijão\n", arq);
fprintf(arq, "%d pacotes de café\n", 2);
fprintf(arq, "Total: R$ %.2f\n", 31.5);
fclose(arq);
printf("Lista gravada.\n");
return 0;
}
- fopen("compras.txt", "w")
- Cria o arquivo (ou apaga o que havia) para escrever. Conferir o
NULLé obrigatório, como sempre. - fputc('*', arq)
- Escreve um caractere, entre aspas simples. Depois,
fputc('\n', arq)escreve a quebra de linha, que também é um caractere. - fputs("arroz\n", arq)
- Escreve o texto exatamente como está, sem formatação. O texto vem primeiro e o arquivo depois.
- fprintf(arq, "%d pacotes de café\n", 2)
- Igual ao
printf, com um argumento a mais no começo: o arquivo. Todos os formatos (%d,%.2f,%s,%-10s…) funcionam (aula Formatos do printf). - printf("Lista gravada.\n")
- Este vai para a tela: é só um aviso para a Ana.
$ gcc -std=c23 -Wall -Wextra tres.c -o tres $ ./tres Lista gravada. $ cat compras.txt * arroz feijão 2 pacotes de café Total: R$ 31.50
fprintf, o arquivo vem no começo; no fputs e no fputc, vem no fim. É uma pegadinha antiga da biblioteca do C. Se trocar, o compilador reclama (veja "Quando dá errado").
2. O fputs não pula linha
O puts, que escreve na tela, acrescenta um \n no fim por conta própria. O fputs, não: ele escreve só o que você mandou. Esquecer o \n gruda tudo:
fputs("arroz", arq);
fputs("feijão", arq);
fputs("café", arq);
$ ./grudado $ cat compras.txt arrozfeijãocafé$ cat -A compras.txt arrozfeijM-CM-#ocafM-CM-)$
A lista virou uma palavra só, e até o $ do terminal veio parar na mesma linha, porque o arquivo não termina com \n. O cat -A mostra os caracteres invisíveis: se houvesse quebras de linha, apareceria um $ no fim de cada linha. Os M-CM-# e M-CM-) são os 2 bytes do ã e do é em UTF-8.
Missão: o programa grava três itens e depois lê o arquivo de volta, linha por linha. Rode: saiu uma linha só. Conserte a parte da gravação para cada item ficar na sua linha, e a saída ser - arroz, - feijão, - café e (fim da lista), cada um numa linha.
#include <stdio.h>
int main(void) {
FILE *arq = fopen("compras.txt", "w");
if (arq == NULL) {
perror("compras.txt");
return 1;
}
fputs("arroz", arq);
fputs("feijão", arq);
fputs("café", arq);
fclose(arq);
/* agora lê de volta, linha por linha */
arq = fopen("compras.txt", "r");
if (arq == NULL) {
perror("compras.txt");
return 1;
}
char linha[100];
while (fgets(linha, sizeof linha, arq) != NULL) {
printf("- %s", linha);
}
fclose(arq);
printf("(fim da lista)\n");
return 0;
}
#include <stdio.h>
int main(void) {
FILE *arq = fopen("compras.txt", "w");
if (arq == NULL) {
perror("compras.txt");
return 1;
}
fputs("arroz\n", arq);
fputs("feijão\n", arq);
fputs("café\n", arq);
fclose(arq);
/* agora lê de volta, linha por linha */
arq = fopen("compras.txt", "r");
if (arq == NULL) {
perror("compras.txt");
return 1;
}
char linha[100];
while (fgets(linha, sizeof linha, arq) != NULL) {
printf("- %s", linha);
}
fclose(arq);
printf("(fim da lista)\n");
return 0;
}
Boa prática: todo arquivo de texto termina com \n. Muitos programas do Linux (o wc -l, por exemplo) só contam uma linha quando ela termina com \n.
3. Um relatório com fprintf
O fprintf brilha quando os dados estão num array de structs. A Ana quer o relatório de vendas do dia num arquivo CSV, que abre em qualquer planilha:
#include <stdio.h>
struct Venda {
char produto[20];
int quantidade;
double preco;
};
int main(void) {
struct Venda vendas[] = {
{"Caderno", 3, 15.90},
{"Caneta", 10, 2.50},
{"Mochila", 1, 89.00},
};
int n = sizeof vendas / sizeof vendas[0];
FILE *arq = fopen("vendas.csv", "w");
if (arq == NULL) {
perror("vendas.csv");
return 1;
}
fprintf(arq, "produto;quantidade;preco;subtotal\n");
double total = 0.0;
for (int i = 0; i < n; i++) {
double sub = vendas[i].quantidade * vendas[i].preco;
fprintf(arq, "%s;%d;%.2f;%.2f\n",
vendas[i].produto, vendas[i].quantidade, vendas[i].preco, sub);
total += sub;
}
fprintf(arq, "TOTAL;;;%.2f\n", total);
if (fclose(arq) != 0) {
perror("vendas.csv");
return 1;
}
printf("Relatório com %d vendas gravado em vendas.csv\n", n);
return 0;
}
- fprintf(arq, "produto;quantidade;preco;subtotal\n")
- A primeira linha do CSV é o cabeçalho: o nome de cada coluna. Sem nenhum
%, ofprintfescreve o texto como está. - "%s;%d;%.2f;%.2f\n"
- Uma linha por venda: os valores separados por
;, o preço e o subtotal com 2 casas. É o formato que osscanfda aula Ler Arquivos sabe ler de volta. - "TOTAL;;;%.2f\n"
- Duas colunas vazias (
;;) para o total cair na coluna do subtotal. - if (fclose(arq) != 0)
- O
fclosedevolve 0 se deu tudo certo eEOFse falhou ao gravar o final. Num arquivo importante, confira (seção 6).
$ gcc -std=c23 -Wall -Wextra relatorio.c -o relatorio $ ./relatorio Relatório com 3 vendas gravado em vendas.csv $ cat vendas.csv produto;quantidade;preco;subtotal Caderno;3;15.90;47.70 Caneta;10;2.50;25.00 Mochila;1;89.00;89.00 TOTAL;;;161.70
O %.2f escreve o número com ponto (15.90), como o C faz por padrão. Uma planilha configurada para o Brasil pode esperar vírgula (15,90) e tratar 15.90 como texto. Na hora de abrir, escolha o ponto como separador decimal. (Um setlocale(LC_ALL, "") no começo do programa faz o fprintf usar vírgula no Linux da Ana; aí, use ; para separar as colunas, como aqui, e nunca vírgula.)
4. Acrescentar: um registro de pedidos
Muitos programas mantêm um registro (log): um arquivo em que cada acontecimento vira uma linha no fim, sem apagar as anteriores. É o modo "a" (append) da aula Arquivos:
#include <stdio.h>
int main(void) {
FILE *arq = fopen("pedidos.log", "a");
if (arq == NULL) {
perror("pedidos.log");
return 1;
}
fprintf(arq, "pedido recebido\n");
fclose(arq);
return 0;
}
$ gcc -std=c23 -Wall -Wextra registrar.c -o registrar $ ./registrar $ ./registrar $ ./registrar $ cat pedidos.log pedido recebido pedido recebido pedido recebido
Três execuções, três linhas. Com "w", sobraria uma. Num registro de verdade, cada linha leva também a data e a hora, que a aula Data e Hora ensina a obter.
5. O buffer: o texto ainda não chegou ao disco
Escrever no disco, um pedacinho por vez, seria muito lento. Por isso o fprintf não escreve no disco na hora: ele guarda o texto numa área de espera na memória, o buffer, e só manda tudo de uma vez quando o buffer enche, quando você chama fflush ou quando o arquivo é fechado. Para ver o buffer acontecendo, este programa escreve uma linha, "trabalha" por 3 segundos e escreve outra:
#include <stdio.h>
#include <unistd.h>
int main(void) {
FILE *arq = fopen("log.txt", "w");
if (arq == NULL) {
perror("log.txt");
return 1;
}
fprintf(arq, "Começou o fechamento do caixa\n");
sleep(3); /* finge um trabalho demorado */
fprintf(arq, "Terminou\n");
fclose(arq);
return 0;
}
A Ana roda o programa em segundo plano (o & no fim), espera 1 segundo e olha o arquivo enquanto ele ainda está rodando. Depois espera ele terminar (wait) e olha de novo:
$ gcc -std=c23 -Wall -Wextra lento.c -o lento $ ./lento & sleep 1; cat log.txt; echo "(fim do cat)"; wait; cat log.txt [1] 453249 (fim do cat) « no meio: o arquivo está vazio! [1]+ Concluído ./lento Começou o fechamento do caixa Terminou « depois do fclose, tudo lá
Um segundo depois do fprintf, o arquivo ainda estava vazio: a primeira linha estava no buffer, na memória do programa. Só o fclose, no fim, levou tudo para o disco. O fflush ("descarregar") manda o buffer para o disco na hora, sem fechar o arquivo:
fprintf(arq, "Começou o fechamento do caixa\n");
fflush(arq); /* manda para o disco agora */
sleep(3);
$ ./lento2 & sleep 1; cat log.txt; echo "(fim do cat)"; wait; cat log.txt [1] 453254 Começou o fechamento do caixa « agora já aparece no meio (fim do cat) [1]+ Concluído ./lento2 Começou o fechamento do caixa Terminou
Quando o programa cai, o buffer se perde
Se o programa termina normalmente (return no main ou exit), o C esvazia os buffers e fecha os arquivos sozinho. Mas se ele morre no meio, o que estava no buffer some:
fprintf(arq, "Venda 1: R$ 15.90\n");
fprintf(arq, "Venda 2: R$ 2.50\n");
int *p = NULL;
printf("%d\n", *p); /* um erro qualquer derruba o programa */
fclose(arq);
$ ./caiu Falha de segmentação (imagem do núcleo gravada) ./caiu $ ls -l log.txt -rw-rw-r-- 1 ana ana 0 out 17 11:43 log.txt « 0 bytes: as duas vendas se perderam
As duas vendas foram "escritas" com fprintf, mas nunca saíram do buffer. Num registro, é justamente a última linha antes da queda que você mais queria ler. Por isso, em registros importantes, chame fflush(arq) depois de cada linha. Não exagere em programas que escrevem muito: cada fflush é uma ida ao disco.
6. Conferir se gravou de verdade
Escrever num arquivo pode falhar: o disco enche, o pendrive é arrancado, a rede cai (num arquivo em outra máquina). Cada função avisa do seu jeito:
| Função | Deu certo | Deu errado |
|---|---|---|
fputc(c, arq) | o próprio caractere | EOF |
fputs(texto, arq) | um número que não é negativo | EOF |
fprintf(arq, …) | quantos bytes escreveu | um número negativo |
fflush(arq) / fclose(arq) | 0 | EOF |
Por causa do buffer, o erro muitas vezes só aparece no fim. O Linux tem um arquivo especial, /dev/full, que finge ser um disco sempre cheio. Perfeito para testar:
#include <stdio.h>
int main(void) {
FILE *arq = fopen("/dev/full", "w");
if (arq == NULL) {
perror("/dev/full");
return 1;
}
int r = fprintf(arq, "Dados importantes\n");
printf("fprintf devolveu %d\n", r);
if (fclose(arq) != 0) {
perror("fclose");
return 1;
}
printf("Salvo com sucesso!\n");
return 0;
}
$ gcc -std=c23 -Wall -Wextra cheio.c -o cheio $ ./cheio fprintf devolveu 18 fclose: No space left on device
O fprintf devolveu 18 (os 18 bytes de "Dados importantes\n") e parecia ter dado certo: o texto foi para o buffer, que tinha espaço. O erro só apareceu quando o fclose tentou levar o buffer para o "disco": No space left on device, "não há espaço no dispositivo". Um programa que não conferisse o fclose diria "Salvo com sucesso!" e perderia os dados calado. O /dev/full não existe no editor do guia: lá, este fopen falha com No such file or directory.
fclose. Para conferir no meio, sem esperar o fim, chame fflush e confira o retorno dele, ou use ferror(arq), que fica verdadeiro depois de qualquer falha de escrita.
7. Salvar sem arriscar o arquivo antigo
Há uma armadilha escondida no "w": ele apaga o arquivo na hora em que abre. Se a gravação falhar no meio (disco cheio, programa que cai), o arquivo novo fica pela metade e o antigo já foi embora. Os editores de texto e os jogos evitam isso com um truque em três passos:
- grave tudo num arquivo temporário (
config.txt.tmp); - confira que deu tudo certo, inclusive o
fclose; - só então troque o antigo pelo novo, com
rename("renomear", dostdio.h), que no Linux faz a troca de uma vez só: quem abrir o arquivo vê o antigo inteiro ou o novo inteiro, nunca a metade.
#include <stdio.h>
int salvar(const char *nome, const char *texto) {
char temp[100];
snprintf(temp, sizeof temp, "%s.tmp", nome);
FILE *arq = fopen(temp, "w");
if (arq == NULL) {
perror(temp);
return 0;
}
if (fputs(texto, arq) == EOF) {
perror(temp);
fclose(arq);
remove(temp);
return 0;
}
if (fclose(arq) != 0) {
perror(temp);
remove(temp);
return 0;
}
if (rename(temp, nome) != 0) {
perror(nome);
remove(temp);
return 0;
}
return 1;
}
int main(void) {
if (salvar("config.txt", "volume=7\ntema=escuro\n")) {
printf("Configuração salva.\n");
}
return 0;
}
| Linha | O que faz |
|---|---|
snprintf(temp, sizeof temp, "%s.tmp", nome) | Monta o nome do temporário: config.txt.tmp (aula Texto ↔ Número). |
fputs(…) == EOF | Confere a escrita. Se falhou, fecha, apaga o temporário e desiste; o config.txt antigo continua intacto. |
fclose(arq) != 0 | Confere o fim da gravação, onde aparecem os erros do buffer (seção 6). |
remove(temp) | Apaga um arquivo (do stdio.h). Aqui, o temporário que não deu certo. |
rename(temp, nome) | Troca o antigo pelo novo. Devolve 0 se deu certo. |
return 1; / return 0; | A função diz se salvou (1) ou não (0), para o main decidir o que mostrar. |
$ gcc -std=c23 -Wall -Wextra seguro.c -o seguro $ ./seguro Configuração salva. $ ls config* config.txt « o .tmp já virou config.txt $ cat config.txt volume=7 tema=escuro
Formas de escrever: qual usar
| Você quer escrever… | Use | Lembre |
|---|---|---|
| Um caractere por vez (copiar, transformar) | fputc(c, arq) | O arquivo vem por último. |
| Um texto pronto | fputs(texto, arq) | Não acrescenta \n. |
| Números, colunas, relatórios | fprintf(arq, formato, …) | O arquivo vem primeiro. |
| Uma linha que precisa sobreviver a uma queda | fprintf + fflush(arq) | Cada fflush é uma ida ao disco. |
| Um "salvar" do usuário | Temporário + fclose conferido + rename | Seção 7. |
| Os bytes de uma struct ou de um array | fwrite | Aula Arquivos Binários. |
| Uma mensagem de erro | fprintf(stderr, …) | Aula stdin, stdout e stderr. |
Quando dá errado
fprintf("arroz\n", arq);
fputs(arq, "feijão\n");
$ gcc -std=c23 -Wall -Wextra ordem.c -o ordem
ordem.c: In function ‘main’:
ordem.c:8:13: error: passing argument 1 of ‘fprintf’ from incompatible pointer type [-Wincompatible-pointer-types]
8 | fprintf("arroz\n", arq);
| ^~~~~~~~~
| |
| char *
In file included from ordem.c:1:
/usr/include/stdio.h:364:38: note: expected ‘FILE * restrict’ but argument is of type ‘char *’
364 | extern int fprintf (FILE *__restrict __stream,
| ~~~~~~~~~~~~~~~~~^~~~~~~~
«…»
ordem.c:9:11: error: passing argument 1 of ‘fputs’ from incompatible pointer type [-Wincompatible-pointer-types]
9 | fputs(arq, "feijão\n");
| ^~~
| |
| FILE *
/usr/include/stdio.h:711:42: note: expected ‘const char * restrict’ but argument is of type ‘FILE *’
711 | extern int fputs (const char *__restrict __s, FILE *__restrict __stream)
| ~~~~~~~~~~~~~~~~~~~~~~~^~~
«…»
O que aconteceu: "o argumento 1 tem um tipo de ponteiro incompatível". As notas mostram o que cada função esperava: o fprintf quer o FILE * primeiro, e o fputs quer o texto primeiro e o FILE * por último. O Clang do editor diz incompatible pointer types passing 'char[7]' to parameter of type 'FILE *'.
Como resolver: fprintf(arq, "arroz\n"); e fputs("feijão\n", arq);. Na dúvida, leia a linha note: expected …: ela mostra a declaração da função.
O que aconteceu: três causas comuns:
- o programa caiu antes do
fclose, e o buffer se perdeu (seção 5); - você olhou o arquivo enquanto o programa ainda rodava, antes do
fcloseou dofflush; - você usou
printfem vez defprintf(arq, …): o texto foi para a tela, e o arquivo continuou vazio.
Como resolver: feche o arquivo assim que terminar de escrever; para registros, fflush(arq) depois de cada linha; e confira se cada linha de escrita tem o arq.
fprintf devolveu -1 e "Bad file descriptor": o arquivo foi aberto para leitura
FILE *arq = fopen("compras.txt", "r");
/* … if (arq == NULL) … */
int r = fprintf(arq, "leite\n");
printf("fprintf devolveu %d\n", r);
if (ferror(arq)) {
perror("fprintf");
}
$ ./modoerrado fprintf devolveu -1 fprintf: Bad file descriptor
O que aconteceu: o arquivo foi aberto com "r", só para leitura, e o fprintf tentou escrever. Ele devolveu -1 (negativo = erro), o ferror ficou verdadeiro, e o motivo é "descritor de arquivo ruim": esse arquivo não aceita escrita. O compilador não tem como perceber. No editor do guia, o fprintf também devolve -1, mas o perror mostra Success, porque lá o motivo não é anotado: confie no retorno, e não só na mensagem.
Como resolver: abra com o modo de escrita certo ("w" ou "a"), ou com "r+" se precisa ler e escrever no mesmo arquivo (aula fseek).
O que aconteceu: o programa abriu com "w" para acrescentar uma linha, e o "w" apagou tudo o que havia antes (aula Arquivos, seção 3).
Como resolver: para acrescentar, use "a". Para reescrever o arquivo inteiro com segurança, use o temporário + rename da seção 7.
Referência rápida
| Escrita | O que faz |
|---|---|
fputc('*', arq) | Escreve um caractere; devolve EOF se falhar. |
fputs("texto\n", arq) | Escreve o texto como está, sem acrescentar \n. |
fprintf(arq, "%s;%d\n", nome, qtd) | Como o printf, no arquivo; devolve os bytes escritos ou um negativo. |
fopen(nome, "a") | Acrescenta no fim, sem apagar. |
fflush(arq) | Manda o buffer para o disco agora, sem fechar. |
if (fclose(arq) != 0) | Confere se o fim da gravação deu certo. |
ferror(arq) | Verdadeiro se alguma escrita (ou leitura) falhou. |
remove("velho.txt") | Apaga um arquivo; 0 se deu certo. |
rename("novo.tmp", "dados.txt") | Renomeia (e substitui) um arquivo; 0 se deu certo. |
Exercícios
1. O argumento que falta
Complete para escrever a nota no arquivo arq, com uma casa decimal:
2. A quebra de linha
Complete o texto para que leite fique sozinho na sua linha do arquivo:
3. Agora!
Qual função manda o buffer de arq para o disco sem fechar o arquivo?
4. O que o arquivo contém depois de fputs("Ana", arq); fputs("Bia", arq);?
5. Um programa faz fprintf(arq, …) e cai com falha de segmentação antes do fclose. O que é mais provável encontrar no arquivo?
6. No exemplo do /dev/full, por que o fprintf devolveu 18 se o "disco" estava cheio?
Missão final: o boletim da turma
Missão: grave o boletim em boletim.txt: dentro do for, use fprintf para escrever uma linha por aluno, com o formato "%-6s %4.1f %s\n" (nome, nota e a situação: aprovado se a nota for 6.0 ou mais, reprovado se não). A parte 2, que lê o arquivo e mostra na tela, já está pronta. A saída deve ser:
Ana 8.5 aprovado Bruno 5.5 reprovado Carla 9.0 aprovado Davi 6.0 aprovado
#include <stdio.h>
struct Aluno {
char nome[20];
double nota;
};
int main(void) {
struct Aluno turma[] = {
{"Ana", 8.5}, {"Bruno", 5.5}, {"Carla", 9.0}, {"Davi", 6.0},
};
int n = sizeof turma / sizeof turma[0];
/* 1) grava o boletim */
FILE *arq = fopen("boletim.txt", "w");
if (arq == NULL) {
perror("boletim.txt");
return 1;
}
for (int i = 0; i < n; i++) {
/* escreva a linha do aluno turma[i] com fprintf */
}
if (fclose(arq) != 0) {
perror("boletim.txt");
return 1;
}
/* 2) mostra o que ficou no arquivo */
arq = fopen("boletim.txt", "r");
if (arq == NULL) {
perror("boletim.txt");
return 1;
}
char linha[100];
while (fgets(linha, sizeof linha, arq) != NULL) {
printf("%s", linha);
}
fclose(arq);
return 0;
}
#include <stdio.h>
struct Aluno {
char nome[20];
double nota;
};
int main(void) {
struct Aluno turma[] = {
{"Ana", 8.5}, {"Bruno", 5.5}, {"Carla", 9.0}, {"Davi", 6.0},
};
int n = sizeof turma / sizeof turma[0];
/* 1) grava o boletim */
FILE *arq = fopen("boletim.txt", "w");
if (arq == NULL) {
perror("boletim.txt");
return 1;
}
for (int i = 0; i < n; i++) {
fprintf(arq, "%-6s %4.1f %s\n", turma[i].nome, turma[i].nota,
turma[i].nota >= 6.0 ? "aprovado" : "reprovado");
}
if (fclose(arq) != 0) {
perror("boletim.txt");
return 1;
}
/* 2) mostra o que ficou no arquivo */
arq = fopen("boletim.txt", "r");
if (arq == NULL) {
perror("boletim.txt");
return 1;
}
char linha[100];
while (fgets(linha, sizeof linha, arq) != NULL) {
printf("%s", linha);
}
fclose(arq);
return 0;
}
Dica: o operador ternário (condição ? "aprovado" : "reprovado", aula Operador Ternário) escolhe o texto sem precisar de um if.
Resumo
fputcescreve um caractere,fputsescreve um texto (sem acrescentar\n) efprintfé oprintfdos arquivos.- Ordem dos argumentos:
fprintf(arq, …)com o arquivo primeiro;fputs(texto, arq)efputc(c, arq)com o arquivo por último. "w"recomeça o arquivo;"a"acrescenta no fim, como num registro.- O texto passa por um buffer antes de ir ao disco.
fflushefcloseesvaziam o buffer; se o programa cai antes, o que estava nele se perde. - Confira os retornos, principalmente o do
fclose: é nele que aparece um "disco cheio". - Para salvar sem arriscar o arquivo antigo: grave num temporário, confira tudo e troque com
rename.