Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Beraldo

[Resolvido] Recuperar matriz salva em arquivo binário

Recommended Posts

Preciso recuperar uma matriz salva num arquivo binário. O problema é que essa matriz é alocada dinamicamente antes de ser salva no arquivo, então, ao ler o arquivo, não se sabe o tamanho dela. E, ao ler os seus valores, dá falha de segmentação.

 

A fim de testes, criei este código:

 

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

#define ARQ "teste.bin"

typedef struct
{
	int x;
	int m_linhas;
	int m_colunas;
	char **matriz;
} my_struct;


void salvar();
void recuperar();



int main()
{
	salvar();
	recuperar();
	
	return 0;
}



void salvar()
{
	my_struct st;
	int i = 0, j = 0, cont = 40;
	FILE *fp;
	
	st.x = 42;
	st.m_linhas = 5;
	st.m_colunas = 2;
	
	if ((st.matriz = (char **) malloc(st.m_linhas * sizeof(char *))) == NULL)
	{
		fprintf(stderr, "Erro\n");
		exit(1);
	}
	
	for (i = 0; i < st.m_linhas; i++)
	{
		if ((st.matriz[i] = (char *) malloc(2 * sizeof(char))) == NULL)
		{
			fprintf(stderr, "Erro\n");
			exit(1);
		}
	}
	
	printf("Definido...\n");
	for (i = 0; i < st.m_linhas; i++)
	{
		for (j = 0; j < st.m_colunas; j++)
		{
			st.matriz[i][j] = (char)cont;
			printf("matriz[%d][%d] = %c\n", i, j, st.matriz[i][j]);
			cont++;
		}
	}
	
	
	
	fp = fopen(ARQ, "wb");
	fwrite(&st, sizeof(my_struct), 1, fp);
	fclose(fp);
	
	
	for (i = 0; i < st.m_linhas; i++)
	{
		free(st.matriz[i]);
	}
	free(st.matriz);
	
}



void recuperar()
{
	FILE *fp;
	my_struct st;
	int i = 0, j = 0;
	
	fp = fopen(ARQ, "rb");
	fread(&st, sizeof(my_struct), 1, fp);
	fclose(fp);
	
	puts("\n\n\nRecuperando...");
	
	printf("st.x = %d\n", st.x);
	
	for ( i = 0; i < st.m_linhas; i++)
	{
		for (j = 0; j < st.m_colunas; j++)
		{
			printf("matriz[%d][%d] = %c\n", i, j, st.matriz[i][j]);
		}
	}
	
	
}

Tentei, após o fread(), alocar memória para a matriz e chamar fread() de novo, mas não deu certo.

void recuperar()
{
	FILE *fp;
	my_struct st;
	int i = 0, j = 0;
	
	fp = fopen(ARQ, "rb");
	fread(&st, sizeof(my_struct), 1, fp);
	
	if ((st.matriz = (char **) malloc(st.m_linhas * sizeof(char *))) == NULL)
	{
		fprintf(stderr, "Erro\n");
		exit(1);
	}
	
	for (i = 0; i < st.m_linhas; i++)
	{
		if ((st.matriz[i] = (char *) malloc(2 * sizeof(char))) == NULL)
		{
			fprintf(stderr, "Erro\n");
			exit(1);
		}
	}
	
	fread(&st, sizeof(my_struct), 1, fp);
	fclose(fp);
	
	puts("\n\n\nRecuperando...");
	
	printf("st.x = %d\n", st.x);
	
	for ( i = 0; i < st.m_linhas; i++)
	{
		for (j = 0; j < st.m_colunas; j++)
		{
			printf("matriz[%d][%d] = %c\n", i, j, st.matriz[i][j]);
		}
	}
}

Com essa modificação na função recuperar, ocorre o seguinte:

$ gcc teste.c -o teste && ./teste

Definido...

matriz[0][0] = (

matriz[0][1] = )

matriz[1][0] = *

matriz[1][1] = +

matriz[2][0] = ,

matriz[2][1] = -

matriz[3][0] = .

matriz[3][1] = /

matriz[4][0] = 0

matriz[4][1] = 1

 

 

 

Recuperando...

st.x = 42

matriz[0][0] =

matriz[0][1] =

matriz[1][0] = p

matriz[1][1] =

matriz[2][0] = P

matriz[2][1] =

matriz[3][0] = 0

matriz[3][1] =

matriz[4][0] =

matriz[4][1] =

 

 

 

Como posso solucionar esse problema?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Sugestão:

 

Antes de cada matriz salva no arquivo, grave a ordem dela.

 

typedef struct __ordem
{
  int linhas;
  int colunas;
} Ordem;

Leia uma vez a ordem com fread

 

Ordem ordem_matriz;
fread(&ordem_matriz, sizeof(Ordem), 1, fp);

Crie uma nova matriz de char como você quiser fazer. Agora não sei qual seria a maneira correta de criar uma matriz dinâmica multidimensional, se seria primeiro mallocar as linhas e então passar um for mallocando as colunas (kkkkk), ou mallocar direto o tamanho normal real, acho que o certo é a primeira opção não tenho certeza. Vou esperar a isis me corrigir :P

 

Leia a quantidade de dados necessária de acordo com o que leu na Ordem.

ordem_matriz.linhas * ordem_matriz.colunas * sizeof(char)

Leia isso na nova matriz.

 

Se você vai ler mais matrizes de um único arquivo, sugiro criar um header no seu arquivo, contendo a quantidade de matrizes e alguma outra informação que seja importante, e ler esse header antes de mais nada (header é uma struct que tu grava no arquivo antes de tudo mais, e é a primeira coisa que você lê do arquivo, geralmente contém informações importantes como por exemplo, a versão do arquivo, a quantidade de dados, onde começam os dados reais, quantas estruturas tem que ler, entre outras coisas). Você pode gazer algumas complicações com as funções de arquivo e algumas gambiarras pra saber se tem mais matrizes no arquivo, talvez até ficasse mais flexível mas só se o usuário vai poder e saber editar o arquivo diretamente com um editor de texto, aí no caso ele não tem obrigação de saber sobre os headers utilizados no seu programa, mas se é só seu programa que vai ler e escrever esses arquivos, escrever um header no início do arquivo seria a forma mais tri pra fazer isso.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Quanto ao malloc, uso sempre a primeira opção citada por você. :D

 

São várias matriz, em estrturas diferentes. Eu pensei em fazer o que você sugeriu, mas imaginei que poderia haver uma forma diferente. Vou fazer isso amanhã cedo (agora estou começando a ficar com sono :P ) aí digo se deu certo. http://forum.imasters.com.br/public/style_emoticons/default/thumbsup.gif

 

Obrigado

Compartilhar este post


Link para o post
Compartilhar em outros sites

Ainda não deu certo... :(

 

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

#define ARQ "teste.bin"

typedef struct
{
	int x;
	int m_linhas;
	int m_colunas;
	char **matriz;
} my_struct;


typedef struct
{
	int m_linhas;
	int m_colunas;
} DIMENSOES;


void salvar();
void recuperar();



int main()
{
	salvar();
	recuperar();
	
	return 0;
}



void salvar()
{
	my_struct st;
	DIMENSOES dims;
	int i = 0, j = 0, cont = 40;
	FILE *fp;
	
	st.x = 42;
	st.m_linhas = 5;
	st.m_colunas = 2;
	
	if ((st.matriz = (char **) malloc(st.m_linhas * sizeof(char *))) == NULL)
	{
		fprintf(stderr, "Erro\n");
		exit(1);
	}
	
	for (i = 0; i < st.m_linhas; i++)
	{
		if ((st.matriz[i] = (char *) malloc(st.m_colunas * sizeof(char))) == NULL)
		{
			fprintf(stderr, "Erro\n");
			exit(1);
		}
	}
	
	printf("Definido...\n");
	for (i = 0; i < st.m_linhas; i++)
	{
		for (j = 0; j < st.m_colunas; j++)
		{
			st.matriz[i][j] = (char)cont;
			printf("matriz[%d][%d] = %c\n", i, j, st.matriz[i][j]);
			cont++;
		}
	}
	
	
	dims.m_linhas = st.m_linhas;
	dims.m_colunas = st.m_colunas;
	
	
	fp = fopen(ARQ, "wb");
	fwrite(&dims, sizeof(DIMENSOES), 1, fp);
	fwrite(&st, sizeof(my_struct), 1, fp);
	fclose(fp);
	
	
	for (i = 0; i < st.m_linhas; i++)
	{
		free(st.matriz[i]);
	}
	free(st.matriz);
	
}



void recuperar()
{
	FILE *fp;
	my_struct st;
	DIMENSOES dims;
	int i = 0, j = 0;
	
	fp = fopen(ARQ, "rb");
	fread(&dims, sizeof(DIMENSOES), 1, fp);
	
	printf("\n\nDimensões: %d x %d\n\n", dims.m_linhas, dims.m_colunas);
	
	if ((st.matriz = (char **) malloc(dims.m_linhas * sizeof(char *))) == NULL)
	{
		fprintf(stderr, "Erro\n");
		exit(1);
	}
	
	for (i = 0; i < dims.m_linhas; i++)
	{
		if ((st.matriz[i] = (char *) malloc(dims.m_colunas * sizeof(char))) == NULL)
		{
			fprintf(stderr, "Erro\n");
			exit(1);
		}
	}
	
	fread(&st, sizeof(my_struct), 1, fp);
	fclose(fp);
	
	
	puts("\n\n\nRecuperando...");
	
	printf("st.x = %d\n", st.x);
	
	for ( i = 0; i < st.m_linhas; i++)
	{
		for (j = 0; j < st.m_colunas; j++)
		{
			printf("matriz[%d][%d] = %c\n", i, j, st.matriz[i][j]);
		}
	}
	
	
}

Os valores das dimensões são recuperados corretamente. Faço a alocação da matriz, mas o resultado é o mesmo que postei anteriormente.

Compartilhar este post


Link para o post
Compartilhar em outros sites

[...] Agora não sei qual seria a maneira correta de criar uma matriz dinâmica multidimensional, se seria primeiro mallocar as linhas e então passar um for mallocando as colunas (kkkkk), ou mallocar direto o tamanho normal real, acho que o certo é a primeira opção não tenho certeza. Vou esperar a isis me corrigir :P [...]

Então, eu sei que é meio chato isso, mas estou entrando de gaiato porque estava com essa mesma dúvida nesse post. Pelo que vi na solução do Beraldo, fiz do jeito dele. Tava com dúvida se precisava criar apontador do tipo ** mesmo. Se achar outra coisa nos diga, José.

 

E Beraldo, o problema com a sua aplicação é que o programa está lendo os endereços de memória dos ponteiros, e não o conteúdo dos mesmos. Quando você passa o ponteiro da **matriz, o programa lerá a matriz, na verdade o vetor, de ponteiros mas não se redirecionará para ler o conteúdo a que eles apontam. E pior, após ler esse vetor ele continua lendo a memória logo após o fim do vetor, que não é, necessariamente, o começo da próxima linha (já que a matriz foi alocada dinamicamente).

 

Pensei agora numa solução: alocar o primeiro vetor (**matriz) normalmente. Depois fazer uma alocação de todas as 10 (linhas*colunas) posições de uma vez, e só então redirecionar os ponteiros da **matriz para o começo de cada linha. Vou tentar agora e já posto.

 

Abraço

 

----------- Edit ----------

 

Eita, quanta besteira falei nesse post... esquece tudo! :P

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bom, depois debulhar as funções de fwrite e fread para entender como funcionam, consegui resolver o problema:

 

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

#define ARQ "teste.bin"

typedef struct
{
	int m_linhas;
	int m_colunas;
} DIMENSOES;


void salvar();
void recuperar();



int main()
{
	salvar();
	recuperar();
	
	getchar();
	return 0;
}



void salvar()
{
	char **matriz;
	DIMENSOES dims;
	int i = 0, j = 0, cont = 40;
	FILE *fp;
	
	dims.m_linhas = 5;
	dims.m_colunas = 2;
	
	if ((matriz = (char **) malloc(dims.m_linhas * sizeof(char *))) == NULL)
	{
		fprintf(stderr, "Erro\n");
		exit(1);
	}
	
	for (i = 0; i < dims.m_linhas; i++)
	{
		if ((matriz[i] = (char *) malloc(dims.m_colunas * sizeof(char))) == NULL)
		{
			fprintf(stderr, "Erro\n");
			exit(1);
		}
	}
	
	printf("Definido...\n");
	for (i = 0; i < dims.m_linhas; i++)
	{
		for (j = 0; j < dims.m_colunas; j++)
		{
			matriz[i][j] = (char)cont;
			printf("matriz[%d][%d] = %c\n", i, j, matriz[i][j]);
			cont++;
		}
	}
	
	
	fp = fopen(ARQ, "wb");
	fwrite(&dims, sizeof(DIMENSOES), 1, fp);
	for (i = 0; i < dims.m_linhas; i++)
	{
		fwrite(matriz[i], sizeof(char), dims.m_colunas, fp);
	}
	fclose(fp);
	
	
	for (i = 0; i < dims.m_linhas; i++)
	{
		free(matriz[i]);
	}
	free(matriz);
	
}



void recuperar()
{
	FILE *fp;
	char **matriz;
	DIMENSOES dims;
	int i = 0, j = 0;
	
	fp = fopen(ARQ, "rb");
	fread(&dims, sizeof(DIMENSOES), 1, fp);
	
	printf("\n\nDimensões: %d x %d\n\n", dims.m_linhas, dims.m_colunas);
	
	if ((matriz = (char **) malloc(dims.m_linhas * sizeof(char *))) == NULL)
	{
		fprintf(stderr, "Erro\n");
		exit(1);
	}
	
	for (i = 0; i < dims.m_linhas; i++)
	{
		if ((matriz[i] = (char *) malloc(dims.m_colunas * sizeof(char))) == NULL)
		{
			fprintf(stderr, "Erro\n");
			exit(1);
		}
	}
	
	for (i = 0; i < dims.m_linhas; i++)
	{
		fread(matriz[i], sizeof(char), dims.m_colunas, fp);
	}
	
	fclose(fp);
	
	
	puts("\n\n\nRecuperando...");
	
	
	for ( i = 0; i < dims.m_linhas; i++)
	{
		for (j = 0; j < dims.m_colunas; j++)
		{
			printf("matriz[%d][%d] = %c\n", i, j, matriz[i][j]);
		}
	}
	
}

Bom, a minha primeira idéia de alocar um "salsichão" de chars e depois redirecionar os ponteiros da **matriz para ele foi descartada. Não faria diferença nenhuma. Deixei a alocação como estava.

 

Não mantive aquela primeira estrutura my_struct porque ela não fazia muita coisa. Além de gravar aquele x (de teste, não era?) ela gravava de novo as dimensões da matriz e ainda gravava apenas o endereço do primeiro ponteiro da **matriz (não poderíamos aproveitá-lo para nada). Resolvi então declarar a **matriz na própria função mesmo, bele?

 

Aí veio a solução: na hora de gravar, temos que gravar linha por linha e ainda dizer que cada linha tem o tamanho dims.m_colunas, sendo todos os elementos do tipo char. E a recuperação dos dados segue na mesma idéia: ler linha por linha e armazenar cada linha na matriz.

 

Acho que ficou com um resultado satisfatório no final. :)

 

Abraço!

Compartilhar este post


Link para o post
Compartilhar em outros sites

A estrutura my_struct é inútil nesse exemplo, de fato.

Mas esse código é um teste somente. Estou fazendo outra aplicação, que requer essa estrututura (há diversas outras informações nela). Por isso preciso salvar a estrutura e não somente a matriz.

 

Aliás, não é apenas uma estrutura.São três. Cada uma delas possui uma, ou até mais matrizes desse tipo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Problema resolvido.

 

Como a matriz é dinâmica, não há como salvá-la no arquivo por meio da estrutura. É necessário salvá-la separadamente.

 

O código final ficou assim:

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

#define ARQ "teste.bin"

typedef struct
{
	int x;
	int m_linhas;
	int m_colunas;
	char *matriz;
} my_struct;



void salvar();
void recuperar();



int main()
{
	salvar();
	recuperar();
	
	return 0;
}
/****************************/



void salvar()
{
	my_struct st;
	int i = 0, j = 0, cont = 42;
	FILE *fp;
	
	st.x = 42;
	st.m_linhas = 5;
	st.m_colunas = 2;
	
	if ((st.matriz = (char *) malloc(st.m_linhas * st.m_colunas * sizeof(char))) == NULL)
	{
		fprintf(stderr, "Erro\n");
		exit(1);
	}
	
	
	
	printf("Definido...\n");
	for (i = 0; i < st.m_linhas; i++)
	{
		for (j = 0; j < st.m_colunas; j++)
		{
			*(st.matriz + i * st.m_colunas + j) = (char)cont;
			printf("matriz[%d][%d] = %c\n", i, j, *(st.matriz + i * st.m_colunas + j));
			cont++;
		}
	}
	
	
	fp = fopen(ARQ, "wb");
	fwrite(&st, sizeof(my_struct), 1, fp);
	fwrite(st.matriz, sizeof(char), st.m_linhas * st.m_colunas, fp);
	fclose(fp);
	
	
	free(st.matriz);
	
}
/************************************/



void recuperar()
{
	FILE *fp;
	my_struct st;
	int i = 0, j = 0;
	
	fp = fopen(ARQ, "rb");
	
	fread(&st, sizeof(my_struct), 1, fp);
	
	if ((st.matriz = (char *) malloc(st.m_linhas * st.m_colunas * sizeof(char))) == NULL)
	{
		fprintf(stderr, "Erro\n");
		exit(1);
	}
	
	
	fread(st.matriz, sizeof(char), st.m_linhas * st.m_colunas, fp);
	fclose(fp);
	
	
	puts("\n\n\nRecuperando...");
	
	printf("st.x = %d\n", st.x);
	
	for ( i = 0; i < st.m_linhas; i++)
	{
		for (j = 0; j < st.m_colunas; j++)
		{
			printf("matriz[%d][%d] = %c\n", i, j, *(st.matriz + i * st.m_colunas + j));
		}
	}
	
	free(st.matriz);
}
/**********************************/

 

Não coloquei comentários no código. Mas se alguém tiver dúvidas sobre ele, pode postá-las. :)

)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Ô Beraldo, conseguiu sozinho? Valeu pela consideração, viu?

Não pude usar sua solução pelo motivo que apresentei acima: a aplicação que fiz precisava daquela estrutura.

Mas a gravação da matriz no arquivo é praticamente igual à maneira apresentada por você. http://forum.imasters.com.br/public/style_emoticons/default/thumbsup.gif

Compartilhar este post


Link para o post
Compartilhar em outros sites

É que nem pareceu que você tinha lido minha solução...

 

Mas então, alocando com malloc(st.m_linhas * st.m_colunas * sizeof(char)) a matriz não fica como uma matriz "normal", né? Tipo o seu ponteiro de matriz é do tipo *, e não do **. Isso não muda muita coisa?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Li sua solução, sim. Testei o código também.

 

Alocando como fiz, não dá para usar a notação de matriz. Tem que manipular a memória. É mais fácil de salvar num arquivo, já que não precisa de um loop.

Aloca-se um vetor, mas o trata como se fosse uma matriz.

 

http://forum.imasters.com.br/public/style_emoticons/default/thumbsup.gif

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.