paulo1205
(usa Ubuntu)
Enviado em 25/11/2015 - 10:06h
Alguns comentários:
1) Você repete as mesmas constantes strings várias vezes no programa, e são constantes longas, excelentes vítimas de erros de digitação (para quem escreve) e falta de atenção (para quem escreve e quem lê o código), além de dificultar a manutenção do código, se houver necessidade de alterar os nomes dos arquivos, que implica ter de mudar a mesma coisa em vários lugares diferentes. Minha sugestão é que você declare símbolos constantes ou macros com esses nomes, e use tais símbolos quando for operar com os arquivos.
/* Exemplo com macros */
#define DATA_FILENAME "C:\\Users\\fabricio\\Desktop\\Projeto C\\Dados\\Funcionarios.txt"
#define TMPDATA_FILENAME "C:\\Users\\fabricio\\Desktop\\Projeto C\\Dados\\FuncionariosTemporarios.txt"
/* ... */
FILE *fp=fopen(DATA_FILENAME, "r");
FILE *tfp=fopen(TMPDATA_FILENAME, "w");
/* ... */
remove(DATA_FILENAME);
rename(TMPDATA_FILENAME, DATA_FILENAME);
2) Se você não vai alterar o arquivo através do ponteiro
fp , não precisa abri-lo com modo
"r+" ; basta usar
"r" . Já o arquivo temporário, que não será lido, pode ser aberto em modo de escrita com remoção de qualquer conteúdo anterior, o que sugere que
"w" deve ser usado, em lugar de
"a+" .
3) Por outro lado, como você parece estar usando Windows e seus arquivos contêm dados binários, provavelmente você deveria usar sempre o flag binário na especificação de modo (e.g.
"wb" em lugar de
"w" ,
"r+b" em lugar de
"r+" etc.). Caso contrário, se o modo default de abertura de arquivos for texto (geralmente o é, no Windows), você pode se deparar com algumas traduções inesperadas na hora de escrever nos ou ler dos arquivos certos bytes ou sequências de bytes.
4) O teste de se
pf é nulo deveria ser movido para um ponto do programa anterior à realização de operações sobre ele, ou mesmo de abrir o arquivo temporário. Aliás, por que não testar também se o arquivo temporário foi aberto com sucesso?
5) Além disso, a resposta ao teste acima não deveria ser apenas imprimir uma mensagenzinha. Ela deveria impedir a execução do que vem depois -- especialmente porque o que vem depois inclui o apagamento do arquivo original e sua substituição por outra coisa.
6) O loop de cópia dos registros parece estar essencialmente correto (desde que o campo com a matrícula seja de um tipo de dados que se possa comparar com o operador
!= ; se ele for um campo com uma string, você deveria usar a função
strcmp () em vez do operador). No entanto, eu sugiro que você seja mais cauteloso contra perda de dados, tomando os cuidados abaixo.
a) Lembre-se de que algumas linhas abaixo você vai substituir o arquivo original pelo arquivo novo. Por isso, verifique se cada uma das chamadas a
fwrite () executou com sucesso. Qualquer falha de escrita deve provocar a interrupção da execução e preservação do arquivo original.
b) Não convém imprimir a mensagem de que o registro foi excluído durante um loop que ainda está construindo o arquivo substituto. Eu a guardaria até o momento em que se tiver certeza de que o arquivo substituto, depois de construído plenamente e com sucesso, entrou no lugar do original.
c) Você usa as chamadas a
fread () como condição de controle do loop. Isso está OK, mas você então terá de checar, após o fim do loop, se a última chamada retornou valor zero porque realmente chegou ao fim de arquivo ou se houve erro de leitura. Teste primeiro com
ferror (), e depois confirme com
feof (). Se o primeiro teste der verdadeiro, ocorreu um erro e o arquivo original deveria ser preservado. Se o segundo der falso, então o arquivo original tem grandes chances de estar corrompido (o tamanho total não é múltiplo do tamanho de cada registro, situação que pode acontecer em decorrência de não especificar modo de operação binário na hora de abrir o arquivo), e você terá de decidir como proceder nessa situação.
7) Pode parecer surpreendente a princípio, mas a cópia de registros só termina realmente depois que você chama
fclose () sobre o arquivo temporário, porque é nesse momento que os buffers são esvaziados e a escrita em disco é realmente encaminhada para o sistema operacional. Por isso, você também deveria testar o valor de retorno dessa chamada.
8) Ainda para se proteger contra perda de dados, além de implementar as recomendações acima, eu cercaria de cuidados e avisos ao usuário a parte final, que efetua a troca dos arquivos. Se o seu programa fosse exclusivamente para o mundo UNIX, eu eliminaria a chamada a
remove (), pois a de
rename () seria suficiente para fazer atomicamente a substituição de um arquivo pelo outro, e sua eventual falha preservaria o arquivo original. No caso geral (no qual o Windows infelizmente se enquadra), porém, é necessário tirar o arquivo velho do caminho primeiro, e isso, por si só, já inevitavelmente abre espaço para perda de dados (imagine a situação em que falta luz ou computador trava entre a conclusão de
remove () e a execução de
rename ()).