Ir para conteúdo

POWERED BY:

Arquivado

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

Beraldo

Apagar um determinado número de bytes de um arquiv binário

Recommended Posts

Montei um programinha que salva e lista corretamente um arquivo binário.

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

#define NOME_ARQUIVO "teste.bin"


typedef struct
{
	int x;
	float y;
	double z;
	char str[20];
} ESTRUTURA;



FILE * abrir_arquivo(char *nome, char *modo);
int fechar_arquivo(FILE *fp);
ESTRUTURA salvar(int x, float y, double z, char *str);
void imprimir();


int main()
{
	salvar(1, 42.59987, 7.889751410232548, "Beraldo1");
	salvar(2, 43.59987, 8.889751410232548, "Beraldo2");
	salvar(3, 44.59987, 9.889751410232548, "Beraldo3");
	imprimir();
	
	
		
	return 0;
}



FILE * abrir_arquivo(char *nome, char *modo)
{
	FILE *fp;
	
	if ((fp = fopen(nome, modo)) == NULL)
	{
		fprintf(stderr, "Erro ao abrir arquivo\n");
		exit(1);
	}
	
	return fp;
}
/*****************************************/


int fechar_arquivo(FILE *fp)
{
	return fclose(fp);
}
/********************************************/


ESTRUTURA salvar(int x, float y, double z, char *str)
{
	FILE *fp;
	ESTRUTURA e;
	
	e.x = x;
	e.y = y;
	e.z = z;
	strcpy(e.str, str);
	
	fp = abrir_arquivo(NOME_ARQUIVO, "ab");
	fwrite(&e, sizeof(ESTRUTURA), 1, fp);
	fechar_arquivo(fp);
}
/*********************************************/




void imprimir()
{
	FILE *fp;
	ESTRUTURA *e;
	int i, total;
	struct stat st;
	
	stat(NOME_ARQUIVO, &st);
	
	total = st.st_size / sizeof(ESTRUTURA);
	
	e = (ESTRUTURA *) malloc(total * sizeof(ESTRUTURA));
	
	fp = abrir_arquivo(NOME_ARQUIVO, "rb");
	fread(e, sizeof(ESTRUTURA), total, fp);
	fechar_arquivo(fp);
	
	for (i = 0; i < total; i++)
	{
		printf("x=%d | y=%f | z=%f | str=%s\n", e[i].x, e[i].y, e[i].z, e[i].str);
	}
}

O que quero fazer agora é uma função de remoção, que apague uma das estruturas salvas no arquivo. Mas nem tenho idéia de como fazer isso sem precisar reescrever todo o arquivo (reescrevendo fica fácil :P )

 

Alguém sabe?

Compartilhar este post


Link para o post
Compartilhar em outros sites

nunca fiz algo assim..

+ sem reescrever o arquivo acho q vai ser meio complicado... bom acho eu neh :lol:

sera q o comando fseek te ajuda um pouco??

 

[]'s

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pensei no fseek(). Ele vai ajudar se houver uma forma de remover todo o conteúdo que está após o ponteiro do arquivo.

 

Mas ainda não descobri como fazer isso... http://forum.imasters.com.br/public/style_emoticons/default/no.gif

 

 

Quero evitar a reescrita do arquivo pois meu professor falou para tentarmos manipular o arquivo em disco, não em memória.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Se você quer mudar o conteúdo do arquivo sem reescrevê-lo, ajoelhe e reze.

O jeito mais fácil é criar um arquivo e ir copiando os bytes que se quer manter, remover o antigo (man 3 remove), e renomear o novo arquivo (man 2 rename).

Compartilhar este post


Link para o post
Compartilhar em outros sites

Consegui! :D

Usei truncate(). Assim não é necessário reescrever o arquivo.

 

A função para truncar ficou assim:

void truncar()
{
	int total, tamanho;
	struct stat st;
	
	stat(NOME_ARQUIVO, &st);
	
	total = st.st_size / sizeof(ESTRUTURA);
	tamanho = (total - 1) * sizeof(ESTRUTURA);
	
	if (truncate(NOME_ARQUIVO, tamanho) == 0)
	{
		printf("Truncado com sucesso. Novo tamanho: %d\n", tamanho);
	}
}

 

O código completo:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

#define NOME_ARQUIVO "teste.bin"


typedef struct
{
	int x;
	float y;
	double z;
	char str[20];
} ESTRUTURA;



FILE * abrir_arquivo(char *nome, char *modo);
int fechar_arquivo(FILE *fp);
ESTRUTURA salvar(int x, float y, double z, char *str);
void imprimir();
void truncar();


int main()
{
	
	salvar(1, 42.59987, 7.889751410232548, "Beraldo1");
	salvar(2, 43.59987, 8.889751410232548, "Beraldo2");
	salvar(3, 44.59987, 8.889751410232548, "Beraldo3");
	truncar();
	salvar(4, 45.59987, 8.889751410232548, "Beraldo4");
	
	
	imprimir();
		
	return 0;
}
/******************************************/



FILE * abrir_arquivo(char *nome, char *modo)
{
	FILE *fp;
	
	if ((fp = fopen(nome, modo)) == NULL)
	{
		fprintf(stderr, "Erro ao abrir arquivo\n");
		exit(1);
	}
	
	return fp;
}
/*****************************************/


int fechar_arquivo(FILE *fp)
{
	return fclose(fp);
}
/********************************************/


ESTRUTURA salvar(int x, float y, double z, char *str)
{
	FILE *fp;
	ESTRUTURA e;
	
	e.x = x;
	e.y = y;
	e.z = z;
	strcpy(e.str, str);
	
	fp = abrir_arquivo(NOME_ARQUIVO, "ab");
	fwrite(&e, sizeof(ESTRUTURA), 1, fp);
	fechar_arquivo(fp);
}
/*********************************************/




void imprimir()
{
	FILE *fp;
	ESTRUTURA *e;
	int i, total;
	struct stat st;
	
	stat(NOME_ARQUIVO, &st);
	
	total = st.st_size / sizeof(ESTRUTURA);
	
	e = (ESTRUTURA *) malloc(total * sizeof(ESTRUTURA));
	
	fp = abrir_arquivo(NOME_ARQUIVO, "rb");
	fread(e, sizeof(ESTRUTURA), total, fp);
	fechar_arquivo(fp);
	
	for (i = 0; i < total; i++)
	{
		printf("[%d] => x=%d | y=%f | z=%f | str=%s\n", i, e[i].x, e[i].y, e[i].z, e[i].str);
	}

}


void truncar()
{
	int total, tamanho, tr;
	struct stat st;
	
	stat(NOME_ARQUIVO, &st);
	
	total = st.st_size / sizeof(ESTRUTURA);
	tamanho = (total - 1) * sizeof(ESTRUTURA);
	
	if (truncate(NOME_ARQUIVO, tamanho) == 0)
	{
		printf("Truncado com sucesso. Novo tamanho: %d\n", tamanho);
	}
	
}

 

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

Compartilhar este post


Link para o post
Compartilhar em outros sites

Retire bytes do meio do arquivo usando truncate.

Mesmo usando truncate você está reescrevendo o arquivo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Retire bytes do meio do arquivo usando truncate.

Nesse caso, substituo os X bytes do meio do arquivo pelos últimos X bytes do fim do arquivo. Depois chamo truncate() para apagar os últimos X bytes do arquivo.

 

Mesmo usando truncate você está reescrevendo o arquivo.

Fiz uma função para reescrever o arquivo manualmente para comparar

Usando truncate foi mais rápido.

 

Usei este código:

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

#define NOME_ARQUIVO "teste.bin"
#define LINHAS 20


typedef struct
{
	int x;
	char str[20];
} ESTRUTURA;



FILE * abrir_arquivo(char *nome, char *modo);
int fechar_arquivo(FILE *fp);
void salvar();
void imprimir(char *filename);
void remover(int linha);
void remover2(int linha);


int main()
{
	double tempo1 = 0, tempo2 = 0;
	struct timeval antes, depois;
	
	gettimeofday(&antes, NULL);
	
	salvar();
	remover(5);
	remover(2);
	remover(7);
	remover(10);
	
	gettimeofday(&depois, NULL);
	
	tempo1 = (depois.tv_sec - antes.tv_sec) + (depois.tv_usec - antes.tv_usec) / 1.e6;
	
	
	
	gettimeofday(&antes, NULL);
	
	salvar();
	remover2(5);
	remover2(2);
	remover2(7);
	remover2(10);
	
	gettimeofday(&depois, NULL);
	
	tempo2 = (depois.tv_sec - antes.tv_sec) + (depois.tv_usec - antes.tv_usec) / 1.e6;
	
	/*imprimir("saida.bin");*/
		
		
	printf("\nTempo:\nCom remover(): %.10lf\nCom remover2(): %.10lf\n\n", tempo1, tempo2);
	
	return 0;
}
/******************************************/



FILE * abrir_arquivo(char *nome, char *modo)
{
	FILE *fp;
	
	if ((fp = fopen(nome, modo)) == NULL)
	{
		fprintf(stderr, "Erro ao abrir arquivo '%s' no modo '%s'\n", nome, modo);
		exit(1);
	}
	
	return fp;
}
/*****************************************/


int fechar_arquivo(FILE *fp)
{
	return fclose(fp);
}
/********************************************/


void salvar()
{
	FILE *fp;
	ESTRUTURA e;
	int i;
	
	fp = abrir_arquivo(NOME_ARQUIVO, "wb");
	
	for (i = 0; i < LINHAS; i++)
	{
		e.x = i;
		sprintf(e.str, "Beraldo%d", i);
		fwrite(&e, sizeof(ESTRUTURA), 1, fp);
	}
	
	fechar_arquivo(fp);
}
/*********************************************/




void imprimir(char *filename)
{
	FILE *fp;
	ESTRUTURA *e;
	int i, total;
	struct stat st;
	
	stat(filename, &st);
	
	total = st.st_size / sizeof(ESTRUTURA);
	
	e = (ESTRUTURA *) malloc(total * sizeof(ESTRUTURA));
	
	fp = abrir_arquivo(filename, "rb");
	fread(e, sizeof(ESTRUTURA), total, fp);
	fechar_arquivo(fp);
	
	for (i = 0; i < total; i++)
	{
		printf("[%d] => x=%d | str=%s\n", i, e[i].x, e[i].str);
	}

}


void remover(int linha)
{
	int total, tamanho, tr;
	ESTRUTURA e;
	struct stat st;
	FILE *fp;
	
	stat(NOME_ARQUIVO, &st);
	
	total = st.st_size / sizeof(ESTRUTURA);
	tamanho = (total - 1) * sizeof(ESTRUTURA);
	
	fp = abrir_arquivo(NOME_ARQUIVO, "r+b");
	fseek(fp, tamanho, SEEK_SET);
	fread(&e, sizeof(ESTRUTURA), 1, fp);
	fseek(fp, linha * sizeof(ESTRUTURA), SEEK_SET);
	fwrite(&e, sizeof(ESTRUTURA), 1, fp);
	fechar_arquivo(fp);
	
	
	if (truncate(NOME_ARQUIVO, tamanho) != 0)
	{
		fprintf(stderr, "Erro ao truncararquivo");
	}
	
}


void remover2(int linha)
{
	int total, tamanho, i = 0;
	ESTRUTURA e;
	struct stat st;
	FILE *fp, *fp2;
	
	stat(NOME_ARQUIVO, &st);
	
	total = st.st_size / sizeof(ESTRUTURA);
	tamanho = (total - 1) * sizeof(ESTRUTURA);
	
	fp = abrir_arquivo(NOME_ARQUIVO, "r+b");
	fseek(fp, tamanho, SEEK_SET);
	fread(&e, sizeof(ESTRUTURA), 1, fp);
	fseek(fp, linha * sizeof(ESTRUTURA), SEEK_SET);
	fwrite(&e, sizeof(ESTRUTURA), 1, fp);
	fechar_arquivo(fp);
	
	
	fp = abrir_arquivo(NOME_ARQUIVO, "rb");
	fp2 = abrir_arquivo("saida.bin", "wb");
	
	while (!feof(fp))
	{
		/*printf("i=%d\n", i++);*/
		fread(&e, sizeof(ESTRUTURA), 1, fp);
		fwrite(&e, sizeof(ESTRUTURA), 1, fp2);
	}
	
	fechar_arquivo(fp);
	fechar_arquivo(fp2);
}

 

$ gcc teste.c -o teste

$ ./teste

 

Tempo:

Com remover(): 0.0002450000

Com remover2(): 0.0002660000

 

 

 

[edit]

A função remover2() está errada.

Eu a refiz:

 

void remover2(int linha)
{
	int total, tamanho, i = 0, pos = 0;
	ESTRUTURA e;
	struct stat st;
	FILE *fp, *fp2;
	
	stat(NOME_ARQUIVO, &st);
	
	total = st.st_size / sizeof(ESTRUTURA);
	tamanho = (total - 1) * sizeof(ESTRUTURA);
	
	fp = abrir_arquivo(NOME_ARQUIVO, "r+b");
	fseek(fp, tamanho, SEEK_SET);
	fread(&e, sizeof(ESTRUTURA), 1, fp);
	fseek(fp, linha * sizeof(ESTRUTURA), SEEK_SET);
	fwrite(&e, sizeof(ESTRUTURA), 1, fp);
	rewind(fp);
	
	fp2 = abrir_arquivo("saida.bin", "wb");
	
	while ((pos = i * sizeof(ESTRUTURA)) < tamanho)
	{
		
		fread(&e, sizeof(ESTRUTURA), 1, fp);
		fwrite(&e, sizeof(ESTRUTURA), 1, fp2);
		
		i++;
		
	}
	
	fechar_arquivo(fp);
	fechar_arquivo(fp2);
	
	remove(NOME_ARQUIVO);
	rename("saida.bin", NOME_ARQUIVO);
}

$ ./teste

 

Tempo:

Com remover(): 0.0002500000

Com remover2(): 0.0003270000

 

Com truncate() continua mais rápido

Compartilhar este post


Link para o post
Compartilhar em outros sites
Fiz uma função para reescrever o arquivo manualmente para comparar

Usando truncate foi mais rápido.

 

Nesse caso, substituo os X bytes do meio do arquivo pelos últimos X bytes do fim do arquivo. Depois chamo truncate() para apagar os últimos X bytes do arquivo.

 

 

você ainda não entendeu ou não está sabendo se explicar.

 

Suponha que o arquivo seja assim:

 

R1,R2,R3

R4,R5,R6

R7,R8,R9

R10,R11,R12

R13,R14,R15

 

 

Substituindo os bytes da 3a linha do arquivo pela mesma quantidade de bytes do final do arquivo, você tem

 

 

R1,R2,R3

R4,R5,R6

R13,R14,R15

R10,R11,R12

R13,R14,R15

 

Apagando os mesmos bytes do final do arquivo, você some com a ordem entre os registros. Se isso for um arquivo com dados meteorológicos, uma seqüência numérica p/ plotar, ou na pior das hipóteses, dados "marcadores" (como se fossem tags de início e final de seção, como as dos arquivos de configuração no Linux) você dança.

 

+1,5%

+ 5%

-3%

-0,9%

+1,2%

 

 

 

A reescrita do arquivo acontece de qualquer modo e é isso que você não está entendendo. O que você faz com o truncate é modificar o campo que contém o tamanho do arquivo na estrutura (além das datas de modificação e talvez outras coisas mais) que contém os dados do arquivo. Aparentemente, quando você especifica um tamanho menor, o marcador de final do arquivo é colocado no último byte que você especifica. No caso de você expandir o arquivo, dados são adicionados no final dele. Então, dizer que você vai truncar um arquivo sem reescrevê-lo é meio nonsense.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu entendi quando você falou que truncate() faz uma reescrita de arquivo. Também me expliquei bem (pelo menos você entendeu exatamente o que eu tentei dizer). :D

Fiz a função própria de rescrita só para comparar o tempo de execução.

 

Para a aplicação que estou desenvolvendo, a ordem dos registros no arquivo não importa, por isso posso deslocar os bytes, simplesmente..

 

Mas se a ordem interferir, aí, deveras, será necessário reescrever manualmente.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Cara, tempo de execução não diz nada.

Teu processo pode sofrer um renice pra prioridade mais baixa possível e nunca ser executado ou é executado sempre e mais rápido porque tem prioridade mais alta do que os outros processos do sistema.

E ainda depende do algoritmo de escalonamento...

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.