Erros muito estranhos... Linguagem C e arquivos

3 respostas
A

Com a ajuda do pessoal do GUJ, consegui fazer um programa para calcular o índice de massa corpórea e gravar os dados. Porém quando tento colocar em ordem alfabética acontecem algumas coisas estranhas.
Ao gravar o arquivo imc2.txt ele perde o imc e um dos nomes (estou testando então são apenas quatro pessoas).

veja como ele calcula corretamente o imc e lista na tela.

Agora veja o arquivo que deveria conter os nomes das quatro pessoas em ordem alfabética, mas tem erros e o imc se perdeu, o que aconteceu?

Nome: Airton Senna
IMC: 25.319
Nome: Airton Senna
IMC: 0.000
Nome: Juan Pablo Montoia
IMC: 0.000
Nome: Lewwis Hamilton
IMC: 0.000

Veja também o primeiro arquivo imc.txt, com os dados gravados corretamente:

Ronaldo Rodrigues Godoi
48 82.500 1.700
Juan Pablo Montoia
45 80.321 1.720
Lewwis Hamilton
32 78.394 1.740
Airton Senna
57 68.932 1.650

O programa grava em uma linha idade, peso e altura e na primeira linha o nome. O código do programa está abaixo:

/*

   Programa de manipulação de arquivo
   Ronaldo Rodrigues Godoi
   
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NUM_LISTA 4

void limpa(FILE *fp) {
	int c;
	while((c=fgetc(fp)) != EOF && c != '\n') ;	
}

struct registro {
	char nome[81];
	int idade;
	double peso;
	double altura;
	double imc;
};

struct registro pessoas[NUM_LISTA];

void ordena_nome() {
	
	char aux_nome[81];
	int aux_idade = 0;
	double aux_peso = 0;
	double aux_altura = 0;
	double aux_imc = 0;
	
	for(int i = 0; i < NUM_LISTA; i++) {
		printf("\n\nNome: %s\n i: %d", pessoas[i].nome, i);
	}
	
	scanf("%d", &aux_idade);
	
	for(int i = 0; i < NUM_LISTA; i++) {
		for(int j = i + 1; j < NUM_LISTA; j++) {
			int r = strcmp(pessoas[i].nome, pessoas[j].nome);
			
			printf("\n\nAAAA Nomes: \n %-80s\n %-80s\n %d %d", pessoas[i].nome, pessoas[j].nome, i, j);
			
			if(r > 0) {
				
				printf("\n\nNome: %-80s\nIMC: %.3f", pessoas[i].nome, pessoas[i].imc);
				
				strcpy(aux_nome, pessoas[i].nome);
				strcpy(pessoas[i].nome, pessoas[j].nome);
				strcpy(pessoas[j].nome, aux_nome);
				
				aux_idade = pessoas[i].idade;
				pessoas[i].idade = pessoas[j].idade;
				pessoas[j].idade = aux_idade;
				
				aux_peso = pessoas[i].peso;
				pessoas[i].peso = pessoas[j].peso;
				pessoas[j].peso = aux_peso;
				
				aux_altura = pessoas[i].altura;
				pessoas[i].altura = pessoas[j].altura;
				pessoas[j].altura = aux_altura;
				
				aux_imc = pessoas[i].imc;
				pessoas[i].imc = pessoas[j].imc;
				pessoas[j].imc = aux_imc;
			}
		}
	}
}

main() {
	
	printf("\n\nCalculo do IMC");
	char entrada;
	do {
		printf("\n\nFaz entrada de dados? (S/N)");
		scanf("%c", &entrada);
	} while(entrada != 'S' && entrada != 's' && entrada != 'N' && entrada != 'n');
	
	if(entrada == 's' || entrada == 'S') {
		printf("Dados para o arquivo: \n");
		for(int i = 0; i < NUM_LISTA; i++) {
			limpa(stdin);
			printf("\n\n Nome: ");
			scanf("%80[^\n]", pessoas[i].nome);
			limpa(stdin);
			printf("\n Idade: ");
			scanf("%d", &pessoas[i].idade);
			limpa(stdin);
			printf("\n Peso: ");
			scanf("%lf", &pessoas[i].peso);
			limpa(stdin);
			printf("\n Altura: ");
			scanf("%lf", &pessoas[i].altura);
		}
	}
	
	FILE *ponteiro;
	ponteiro = fopen("imc.txt", "a");
	if(!ponteiro) {
		printf("\nErro ao abrir arquivo!\n");
		fclose(ponteiro);
		exit(0);
	}
	else
		printf("\nArquivo aberto com sucesso\n");
		
	if(entrada == 's' || entrada == 'S') {
		for(int i = 0; i < NUM_LISTA; i++) {
			fprintf(ponteiro, "%s\n%d %.3f %.3f\n", pessoas[i].nome, pessoas[i].idade, pessoas[i].peso, pessoas[i].altura);
		}
	}
	
	int registrosLidos = 0;
	char buffer[81];
	fclose(ponteiro);
	ponteiro = fopen("imc.txt", "r");
	if(!ponteiro) {
		printf("\nErro ao abrir arquivo!\n");
		fclose(ponteiro);
		exit(0);
	}
	else
		printf("\nArquivo aberto com sucesso, segunda vez.\n");
	
	while (1) {
		
		if(! fgets(buffer, 80, ponteiro)) {
			printf("\n\nNão consegui ler o nome...2");
			break;
		}
		
		buffer[strcspn(buffer, "\n")] = '\0';
		strcpy(pessoas[registrosLidos].nome,buffer);
		
		if(! fgets(buffer, 80, ponteiro)) {
			printf("\n\nNão consegui ler os numeros...3");
			break;
		}
		
		if(sscanf(buffer, "%d %lf %lf", &pessoas[registrosLidos].idade,
									 	&pessoas[registrosLidos].peso,
									 	&pessoas[registrosLidos].altura) !=3) {
	 		break;
											 }
											 
		pessoas[registrosLidos].imc = pessoas[registrosLidos].peso / (pessoas[registrosLidos].altura * pessoas[registrosLidos].altura);
		
		printf("\n\nNome: %-80s\nIdade: %d\nPeso: %.3f\nAltura: %.3f\n\nIMC: %.3f\n", 
		pessoas[registrosLidos].nome,
		pessoas[registrosLidos].idade,
		pessoas[registrosLidos].peso,
		pessoas[registrosLidos].altura,
		pessoas[registrosLidos].imc);
		
	}
	
	ordena_nome();
	
	FILE *ponteiro2;
	ponteiro2 = fopen("imc2.txt", "w");
	if(!ponteiro2) {
		printf("\nErro ao abrir/criar segundo arquivo!\n");
		fclose(ponteiro);
		exit(0);
	}
	else
		printf("\nSegundo Arquivo aberto/criado com sucesso\n");
	for(int i = 0; i < NUM_LISTA; i++) {
		fprintf(ponteiro2, "Nome: %s\nIMC: %.3f\n", pessoas[i].nome, pessoas[i].imc);		
	}
	
	fclose(ponteiro);
	fclose(ponteiro2);
	return(0);
	
}

Bom, não entendo porque os dados ficam embaralhados. Pode ser erro em alguma máscara ou de ordenação. Se alguém puder me ajudar eu agradeço.

Atenciosamente,
Ronaldo

3 Respostas

H

Ao ler do arquivo, faltou incrementar a variável registrosLidos:

while (1) {
    // fgets, sscanf, print, etc...

    // no final, faltou isso aqui:
    registrosLidos++;
}

Sem isso, todos os registros eram colocados na posição zero.


Dito isso, tem um jeito mais simples de ordenar, que é usar a função nativa qsort:

int compararPessoasPorNome(const void *a, const void *b) {
    return strcmp(((struct registro *) a)->nome, ((struct registro *) b)->nome);
}

void ordena_nome(struct registro *pessoas) {
    qsort(pessoas, NUM_LISTA, sizeof(struct registro), compararPessoasPorNome);
}

Para isso você precisa criar outra função que faz a comparação entre dois registros. No caso, é a função compararPessoasPorNome, que compara duas pessoas pelo nome (usando a já existente strcmp), que deve retornar -1 se o primeiro valor é “menor” (ou seja, se deve vir antes na ordenação), 1 se o primeiro valor é “maior” (se deve vir depois) ou zero se tanto faz. Como strcmp já faz isso para strings, aproveitei ela, passando os nomes como parâmetros.

A sintaxe é meio chata porque os argumentos devem ser void *, então precisa do cast para a sua struct.

E repare que agora eu passo o array como parâmetro (melhor, porque senão a função só funciona para o struct global que você declarou fora do main - já passando como parâmetro ela funciona para qualquer outro array). Para chamar a função, basta passar o array para ela, assim:

ordena_nome(pessoas);
A

Muito obrigado!!! Está funcionando.

Atenciosamente,
Ronaldo

H

Outro detalhe: usar qsort como sugerido acima é mais eficiente porque ela trabalha com ponteiros em vez da struct em si. Ou seja, em vez de copiar todos os dados da struct (o que é bem ineficiente, ainda mais se você precisa fazer várias vezes), ela só troca os ponteiros (os endereços de memória), muito mais eficiente.


Claro que se quiser implementar na mão a função de ordenação, pode. Mas em vez de copiar todos os dados da struct na mão, pode ser assim:

void ordena_nome(struct registro *pessoas) {
    for (int i = 0; i < NUM_LISTA - 1; i++)
        for (int j = 0; j < NUM_LISTA - i - 1; j++)
            if (strcmp(pessoas[j].nome, pessoas[j + 1].nome) > 0) {
                struct registro tmp = pessoas[j];
                pessoas[j] = pessoas[j + 1];
                pessoas[j + 1] = tmp;
            }
}
Criado 20 de fevereiro de 2022
Ultima resposta 21 de fev. de 2022
Respostas 3
Participantes 2