Ir ao conteúdo
  • Cadastre-se
giu_d

C Erro ao atualizar dados de arquivo

Recommended Posts

Olá. Estou começando a estudar p valer sobre atualizar dados em um arquivo. A dificuldade está em posicionar o cursor no local correto do arquivo para fazer a alteração em um dado do arquivo, sem modificar o restante do arquivo

Creio q passando o código e os dados do arquivo fique mais fácil de entender o problema

Obs: Da forma como está o código, está sendo considerado q o arquivo está em um "formato fixo".

Mas gostaria de saber se tem como fazer esse exercício usando para isso um arquivo no 'formato variável'. No "formato fixo"  o campo seq ocupa 2 caracteres, o campo nome 30 caracteres e os campos nota 1 e nota 2 ocupando 5 caracteres cada. Os campos são separados por espaços.

Segue o código:

 

#include <stdio.h>
#include <stdbool.h>

#define QTDE 31

char escolha_opcao();
void limpa_linha();

int main() {

    FILE *arq = fopen("alunos.txt", "r+");
    fpos_t pos;
    char nome[QTDE];
    double n1, n2;
    char opcao; int seq;

    while (true) {
        fgetpos(arq, &pos);
        if (fscanf(arq, "%d %30c %lf %lf%*c", &seq, nome, &n1, &n2) == EOF) {
            break;
        }
        printf("%2d %30s %5.2f %5.2f\n", seq, nome, n1, n2);
        opcao = escolha_opcao();
        if (opcao == '9') {
            break;
        }
        switch (opcao)
        {
        case '1':
            printf("\nnovo nome: ");
            scanf("%30[^\n]", nome);
            limpa_linha();

            printf("nova nota1: ");
            scanf("%lf", &n1);

            printf("nova nota2: ");
            scanf("%lf", &n2);

            limpa_linha();

            fsetpos(arq, &pos);

            fprintf(arq, "%2d %-30s %5.2f %5.2f\n", seq, nome, n1, n2);
            break;
        default:
            printf("\nmantem os dados\n\n");
            break;
        }
    }

    fclose(arq);

    return 0;
}

char escolha_opcao() {

    char op;

    do {
        printf("\n\nEscolha a opcao:\n");
        printf("1 - Atualiza\n");
        printf("2 - Mantem\n");
        printf("9 - Termina o programa\n");
        printf("Sua opcao: ");
        scanf("%c", &op);
        limpa_linha();

    } while ((op != '1') && (op != '2') && (op != '9'));

    return op;
}

void limpa_linha() {

    scanf("%*[^\n]");
    scanf("%*c");
}

Segue os dados do arquivo, em um "formato variável":

 

 1 Capitu 7.8 9.5
 2 Ines Pereira 8.3 6.15
 3 Marilia de Dirceu  5.18 8.4
 4 Padre Nando 6.6 5.9
 5 Policarpo Quaresma 2.3 9.15
 6 Virgilia da Souza 7.80 9.50
 7 Quincas Borba 5.7 7.32
 8 Dulcineia de Bodosa 7.90 9.00

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Com uns pequenos ajustes conseguimos fazer com que seu programa imprima a lista "corretamente" SI pulsamos qualquer outra opção que não ser nem 1 e nem 9.

 

 


#include <stdio.h> #include <stdbool.h> #define QTDE 31 char escolha_opcao(); void limpa_linha(); int main() { FILE *arq = fopen("alunos.txt", "r+"); fpos_t pos; char nome[QTDE]; double n1, n2; char opcao = 0; //int seq; int sair = 0; while ( sair != 1 && (fscanf(arq, "%[^0-9]%lf %lf%*c", nome, &n1, &n2) != EOF) ) { fgetpos(arq, &pos); printf("%s %5.2f %5.2f\n", nome, n1, n2); opcao = escolha_opcao(); switch (opcao) { case '1': printf("\nnovo nome: "); scanf("%30[^\n]", nome); limpa_linha(); printf("nova nota1: "); scanf("%lf", &n1); printf("nova nota2: "); scanf("%lf", &n2); limpa_linha(); fsetpos(arq, &pos); fprintf(arq, "%s %5.2f %5.2f\n", nome, n1, n2); break; case '9': sair = 1; break; default: printf("\nmantem os dados\n\n"); break; } } fclose(arq); return 0; } char escolha_opcao() { char op; do { printf("\n\nEscolha a opcao:\n"); printf("1 - Atualiza\n"); printf("2 - Mantem\n"); printf("9 - Termina o programa\n"); printf("Sua opcao: "); scanf("%c", &op); limpa_linha(); } while ((op != '1') && (op != '2') && (op != '9')); return op; } void limpa_linha() { scanf("%*[^\n]"); scanf("%*c"); }

 


Explicação:
Traz fazer uns ajustes e retirar de todos os fscanf, fprintf, etc, a variável seq, que ao meu ver não está contida no arquivo E parece não fazer uso desta variável para nada, o programa passou a imprimir a primeira linha do arquivo de forma correta, e isso já é uma boa sinal.

 

Agora, aparentemente, o problema é de lógica. Se o que você quer é mostrar a lista do arquivo para que o usuário saiba o que está fazendo ao alterar o arquivo, você ta errando... você deve imprimir o arquivo completo  primeiro dentro de um laço e depois alterar o arquivo. Veja esse código:

 

 


#include <stdio.h> #include <stdbool.h> #define QTDE 31 char escolha_opcao(); void limpa_linha(); int main() { FILE *arq = fopen("alunos.txt", "r+"); fpos_t pos; char nome[QTDE]; double n1, n2; char opcao = 0; //int seq; int sair = 0; while ( sair != 1 ) { //fgetpos(arq, &pos); rewind(arq); //voltamos ao inicio do arquivo while (fscanf(arq, "%[^0-9]%lf %lf%*c", nome, &n1, &n2) != EOF){ printf("%s %5.2f %5.2f\n", nome, n1, n2); } //traz sair do while que imprime estamos no final do arquivo precisamos voltar ao inicio novamente rewind(arq); //voltamos ao inicio do arquivo opcao = escolha_opcao(); switch (opcao) { case '1': printf("\nnovo nome: "); scanf("%30[^\n]", nome); limpa_linha(); printf("nova nota1: "); scanf("%lf", &n1); printf("nova nota2: "); scanf("%lf", &n2); limpa_linha(); //fsetpos(arq, &pos); fprintf(arq, "%s %5.2f %5.2f\n", nome, n1, n2); break; case '9': sair = 1; break; default: printf("\nmantem os dados\n\n"); break; } } fclose(arq); return 0; } char escolha_opcao() { char op; do { printf("\n\nEscolha a opcao:\n"); printf("1 - Atualiza\n"); printf("2 - Mantem\n"); printf("9 - Termina o programa\n"); printf("Sua opcao: "); scanf("%c", &op); limpa_linha(); } while ((op != '1') && (op != '2') && (op != '9')); return op; } void limpa_linha() { scanf("%*[^\n]"); scanf("%*c"); }

 



Aparentemente não faz nada mas posso lhe assegurar que seu programa ja funciona só que ta faltando algo.
Preste atenção nisso é importante. Quando alteramos um programa em um arquivo de testo em um editor de código qualquer, o fato de escrever nele não o salva, pois na realidade estamos editando um buffer muitas vezes. É justo al apertar o botão "guardar" ou "salvar" que o programa leva a cabo uma serie de rotinas para enviar os dados ao arquivo e salva-lo. Mas como funciona isso realmente? O primeiro que vou lhe dizer é que se você abrir um arquivo e escrever nele você vai perceber que isso não o alterará. Por quê? fácil porque você não "pulsou" guardar, e para isso você precisa FECHAR ELE TRAZ CADA ALTERAÇÃO(fclose(arq)). É chato mesmo... mas se você não fazer isso, de fechar ele depois de cada alteração, a alteração não ocorrerá.
Então visto o visto que falta? Por um fclose no final do case '1'. Faça isso e comprove, ponha um fclose no final do case 1 que altera o arquivo, e veja a magia.
Importante!!! Comente as linhas que tenham fgetpos(arq, &pos) e fsetpos(arq, &pos), de momento não servem para nada, só atrapalhar.
 

 

 


#include <stdio.h> #include <stdbool.h> #define QTDE 31 char escolha_opcao(); void limpa_linha(); int main() { FILE *arq = fopen("alunos.txt", "r+"); fpos_t pos; char nome[QTDE]; double n1, n2; char opcao = 0; //int seq; int sair = 0; while ( sair != 1 ) { //fgetpos(arq, &pos); rewind(arq); //voltamos ao inicio do arquivo while (fscanf(arq, "%[^0-9]%lf %lf%*c", nome, &n1, &n2) != EOF){ printf("%s %5.2f %5.2f\n", nome, n1, n2); } //traz sair do while que imprime estamos no final do arquivo precisamos voltar ao inicio novamente rewind(arq); //voltamos ao inicio do arquivo opcao = escolha_opcao(); switch (opcao) { case '1': printf("\nnovo nome: "); scanf("%30[^\n]", nome); limpa_linha(); printf("nova nota1: "); scanf("%lf", &n1); printf("nova nota2: "); scanf("%lf", &n2); limpa_linha(); //fsetpos(arq, &pos); fprintf(arq, "%s %5.2f %5.2f\n", nome, n1, n2); fclose(arq); break; case '9': sair = 1; break; default: printf("\nmantem os dados\n\n"); break; } } fclose(arq); return 0; } char escolha_opcao() { char op; do { printf("\n\nEscolha a opcao:\n"); printf("1 - Atualiza\n"); printf("2 - Mantem\n"); printf("9 - Termina o programa\n"); printf("Sua opcao: "); scanf("%c", &op); limpa_linha(); } while ((op != '1') && (op != '2') && (op != '9')); return op; } void limpa_linha() { scanf("%*[^\n]"); scanf("%*c"); }

 



agora podemos perceber si vamos no arquivo que ele está assim:

 

 


mi sato 1.00 2.00 Ines Pereira 8.3 6.15 Marilia de Dirceu 5.18 8.4 Padre Nando 6.6 5.9 Policarpo Quaresma 2.3 9.15 Virgilia da Souza 7.80 9.50 Quincas Borba 5.7 7.32 Dulcineia de Bodosa 7.90 9.00

 



Mas já podemos ver que altera "corretamente", bom... que altera vamos >_<. O problema agora é que ao fechar o arquivo já não é mostrado a lista :oops:, mas isso tem fácil solução... podemos abrir novamente shuesuheus. Depois do ultimo fclose que lhe mandei inserir(dentro do case'1') ponha outra vez o fopen hehe.

 

 


#include <stdio.h> #include <stdbool.h> #define QTDE 31 char escolha_opcao(); void limpa_linha(); int main() { FILE *arq = fopen("alunos.txt", "r+"); fpos_t pos; char nome[QTDE]; double n1, n2; char opcao = 0; //int seq; int sair = 0; while ( sair != 1 ) { //fgetpos(arq, &pos); rewind(arq); //voltamos ao inicio do arquivo while (fscanf(arq, "%[^0-9]%lf %lf%*c", nome, &n1, &n2) != EOF){ printf("%s %5.2f %5.2f\n", nome, n1, n2); } //traz sair do while que imprime estamos no final do arquivo precisamos voltar ao inicio novamente rewind(arq); //voltamos ao inicio do arquivo opcao = escolha_opcao(); switch (opcao) { case '1': printf("\nnovo nome: "); scanf("%30[^\n]", nome); limpa_linha(); printf("nova nota1: "); scanf("%lf", &n1); printf("nova nota2: "); scanf("%lf", &n2); limpa_linha(); //fsetpos(arq, &pos); fprintf(arq, "%s %5.2f %5.2f\n", nome, n1, n2); fclose(arq); arq = fopen("alunos.txt", "r+"); break; case '9': sair = 1; break; default: printf("\nmantem os dados\n\n"); break; } } fclose(arq); return 0; } char escolha_opcao() { char op; do { printf("\n\nEscolha a opcao:\n"); printf("1 - Atualiza\n"); printf("2 - Mantem\n"); printf("9 - Termina o programa\n"); printf("Sua opcao: "); scanf("%c", &op); limpa_linha(); } while ((op != '1') && (op != '2') && (op != '9')); return op; } void limpa_linha() { scanf("%*[^\n]"); scanf("%*c"); }

 



O problema agora é o seguinte... o programa altera a primeira posição do arquivo sempre, e você seguramente quer alterar uma posição em concreto ou inclusive inserir os dados ao final do arquivo como você diz. No primeiro caso(caso busca) você vai precisar usar as funções fseek, fsetpos, ftell, rewind, etc, para mover o ponteiro de forma correta ao inicio da linha que você quer alterar. No segundo caso, de querer inserir al final do arquivo basta você por antes do fprintf que altera o arquivo o comando fseek ( arq, 0, SEEK_END ), isso move o cursor ao fim do arquivo antes de escrever nele.

 

 


#include <stdio.h> #include <stdbool.h> #define QTDE 31 char escolha_opcao(); void limpa_linha(); int main() { FILE *arq = fopen("alunos.txt", "r+"); fpos_t pos; char nome[QTDE]; double n1, n2; char opcao = 0; //int seq; int sair = 0; while ( sair != 1 ) { //fgetpos(arq, &pos); rewind(arq); //voltamos ao inicio do arquivo while (fscanf(arq, "%[^0-9]%lf %lf%*c", nome, &n1, &n2) != EOF){ printf("%s %5.2f %5.2f\n", nome, n1, n2); } //traz sair do while que imprime estamos no final do arquivo precisamos voltar ao inicio novamente rewind(arq); //voltamos ao inicio do arquivo opcao = escolha_opcao(); switch (opcao) { case '1': printf("\nnovo nome: "); scanf("%30[^\n]", nome); limpa_linha(); printf("nova nota1: "); scanf("%lf", &n1); printf("nova nota2: "); scanf("%lf", &n2); limpa_linha(); //fsetpos(arq, &pos); fseek ( arq, 0, SEEK_END ); fprintf(arq, "%s %5.2f %5.2f\n", nome, n1, n2); fclose(arq); arq = fopen("alunos.txt", "r+"); break; case '9': sair = 1; break; default: printf("\nmantem os dados\n\n"); break; } } fclose(arq); return 0; } char escolha_opcao() { char op; do { printf("\n\nEscolha a opcao:\n"); printf("1 - Atualiza\n"); printf("2 - Mantem\n"); printf("9 - Termina o programa\n"); printf("Sua opcao: "); scanf("%c", &op); limpa_linha(); } while ((op != '1') && (op != '2') && (op != '9')); return op; } void limpa_linha() { scanf("%*[^\n]"); scanf("%*c"); }

 



Acho que seu programa já inseri corretamente ao final com essas alterações.

Sobre isso de tamanho fixo ou formato variável não há problemas, o problema é que você n insira uma string maior que a que continha nessa linha anteriormente, ou você sobrescreverá a seguinte linha, e se isso acontecer na ultima linha nem sei te dizer o que acontece, mas boa coisa não será sheuseus. Por isso é preferível você ter um tamanho fixo, porque você sabe que não pode passar isso(mais fácil).

Lembre de sempre fechar o programa depois de cada alteração.

outra coisa a destacar é que talvez seja melhor usar "a+" em vez de "r+" pois ele lhe permitirá ler, mas não permitirá que os dados sejam inseridos em qualquer lugar, se não que no final do arquivo. De uma olhada nesse pdf:Modos de Abertura Função fopen.pdf

 

E vou lhe deixar um programa que maneja arquivos. algo simples que fiz uma vez, para você dar uma olhada no conceito:
arquivos.txt

 

Espero que sirva.

Ah!!! e uma pergunta...
para que serve isso?
scanf("%*[^\n]");

 nunca vi isso shueshueshsue

adicionado 6 minutos depois

mano foi mal... ao inserir o tag spoiler para não ocupar muitas linhas o programa perdeu o formato. Certamente porque esse fórum seja guardado em um servidor linux, e o caracter \n seja diferente. tente arrumar o programa, se tiver duvida amanhã tento arrumar mas agora vou dormir que é tarde e tenho que trabalhar daqui a pouco. Deixo o ultimo que tenho pois fui alterando o mesmo:

#include <stdio.h>
#include <stdbool.h>

#define QTDE 31

char escolha_opcao();
void limpa_linha();

int main() {

    FILE *arq = fopen("alunos.txt", "r+");
    fpos_t pos;
    char nome[QTDE];
    double n1, n2;
    char opcao = 0;
    //int seq;
    int sair = 0;

    while ( sair != 1 ) {
        //fgetpos(arq, &pos);


        rewind(arq); //voltamos ao inicio do arquivo
        while (fscanf(arq, "%[^0-9]%lf %lf%*c", nome, &n1, &n2) != EOF){
            printf("%s %5.2f %5.2f\n", nome, n1, n2);
        }

        //traz sair do while que imprime estamos no final do arquivo precisamos voltar ao inicio novamente
        rewind(arq); //voltamos ao inicio do arquivo

        opcao = escolha_opcao();

        switch (opcao) {
        case '1':
            printf("\nnovo nome: ");
            scanf("%30[^\n]", nome);
            limpa_linha();

            printf("nova nota1: ");
            scanf("%lf", &n1);

            printf("nova nota2: ");
            scanf("%lf", &n2);

            limpa_linha();

            //fsetpos(arq, &pos);

            fseek ( arq, 0, SEEK_END );
            fprintf(arq, "%s %5.2f %5.2f\n", nome, n1, n2);

            fclose(arq);
            arq = fopen("alunos.txt", "r+");

            break;

         case '9':
             sair = 1;
            break;

        default:
            printf("\nmantem os dados\n\n");
            break;
        }

    }
    fclose(arq);



    return 0;
}

char escolha_opcao() {

    char op;

    do {
        printf("\n\nEscolha a opcao:\n");
        printf("1 - Atualiza\n");
        printf("2 - Mantem\n");
        printf("9 - Termina o programa\n");
        printf("Sua opcao: ");
        scanf("%c", &op);
        limpa_linha();

    } while ((op != '1') && (op != '2') && (op != '9'));

    return op;
}

void limpa_linha() {

    scanf("%*[^\n]");
    scanf("%*c");
}


Sorte!.
 

adicionado 10 minutos depois

maldito tag spoiler que n funciona bem
flat,1000x1000,075,f.u1.jpg

  • Curtir 1
  • Obrigado 1

Compartilhar este post


Link para o post
Compartilhar em outros sites
4 minutos atrás, vangodp disse:

Ah!!! e uma pergunta...
para que serve isso?
scanf("%*[^\n]");

 nunca vi isso shueshueshsue

No scanf o asterisco * após o % indica que vai ler e descartar o que for lido, ou seja não vai guardar em nenhuma variável.

 

Nesse caso %*[^\n] Lê todos os caracteres digitados exceto o caractere nova linha, e os descarta. Assim limpa tudo que estiver no stdin, exceto o caractere nova linha.

 

Aí em seguida usa outro scanf("%*c") só para descartar o caractere nova linha '\n', assim limpa todo o stdin.

 

(Acho que eu também postei essa alternativa ao fflush(stdin) aqui no fórum, e por algum motivo não funcionou corretamente combinando os 2 em um único scanf por isso tem 2 scanfs.)

  • Curtir 2
  • Triste 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

@vangodp Rapaz...q dizer! Vai ser preciso pensar um pouco sobre o exercício rs...

Compartilhar este post


Link para o post
Compartilhar em outros sites
scanf("%[^\n]", vetordecaracteres);

@vangodp Isso captura tudo que é digitado no terminal (exceto o caractere nova linha '\n') e guarda em vetordecaracteres certo?

 

 

Então isso:

scanf("%*[^\n]");

Com o * incluso também captura tudo que é digitado (exceto o '\n'), mas não guarda em lugar algum, apenas descarta.

 

Assim, limpa a stdin, mas fica sobrando o '\n'. Então usa o outro scanf para ler apenas o '\n' e também descartar.

scanf("%*c");

 

Logo, juntos esses 2 comandos limpam todo o stdin.

  • Amei 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora





Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas publicações sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×