Deveria Funcionar, não? Linguagem C 1

31 respostas
D

Pessoal, isso tá me dando nos nervos. Estou implementando um simples banco de dados em C. Ele permite criar e abrir tabelas e adicionar, ler, editar e apagar registros. A função de leitura é muito parecida com a de edição. A diferença é que após posicionar o cursor do arquivo na posição onde está o registro, a função de leitura lê um registro e a de edição sobrescreve esse registro. Ou deveria. Acontece que a edição não funciona, e acho que a culpa é do fwrite! A seguir o código:

As estruturas que representam a tabela na memória

Um campo da tabela
struct ltfield {
	char name[LT_ID_LEN + 1];
	int size;
	char type;
};

typedef struct ltfield LTField;

struct ltable {
	int version[3];
	int fieldsLen;
	int registerLen; /* Esse é o comprimento de um registro de acordo com o vetor de campos */
	int count;
	long data; /* Esse é a posição no arquivo onde os registro começam a ser gravados */
	long insert;
	char match;
	int pos;
	int cursor; /* Esse é o registro sobre o qual a operação será realiza */
	LTField *fields; /* Esse é o vetor de campos fornecido na criação da tabela */
	FILE *file;
	char **buffer; /* Este buffer serve para a leitura e gravação de registros */
	unsigned opened :1;
	unsigned error :7;
};

typedef struct ltable LTable;

A função de leitura

#include "ltables.h"
#include "lterr.h"
#include <stdio.h>
#include <string.h>

int lt_read(LTable *table) {
	register int F;
	char active = 0;

	/* Checa a tabela */
	if (table->error)
		return LTERR_TABLE_HAS_ERROR;
	if (!table->opened)
		return LTERR_TABLE_IS_CLOSED;

	/* Corrige cursores atrevidos :-) */
	if (table->cursor >= table->count)
		table->cursor = table->count - 1;
	else if (table->cursor < 0)
		table->cursor = 0;

	/* Acha a posição de leitura */
	fseek(table->file,
			table->data + table->cursor * (sizeof(char) + table->registerLen),
			SEEK_SET);

	/* Checa se o registro está ativo */
	active = getc(table->file);
	if (!active)
		return LTERR_INACTIVE_REGISTER;

	/* Limpa o buffer, para que dados anteriores não deixem fragmentos */
	for (F = 0; F < table->fieldsLen; F++)
		memset(table->buffer[F], '\0', table->fields[F].size);

	/* Lê o registro para o buffer AQUI FUNCIONA*/
	for (F = 0; F < table->fieldsLen; F++)
		fread(table->buffer[F], sizeof(char), table->fields[F].size,
				table->file);

	return 0;
}

A função de Edição

#include "ltables.h"
#include "lterr.h"
#include <stdio.h>
#include <string.h>

int lt_edit(LTable *table) {
	register int F;
	char active = 0;
	long int offset=0;

	/* Checa a tabela */
	if (table->error)
		return LTERR_TABLE_HAS_ERROR;
	if (!table->opened)
		return LTERR_TABLE_IS_CLOSED;

	/* Corrige cursores atrevidos :-) */
	if (table->cursor >= table->count)
		table->cursor = table->count - 1;
	else if (table->cursor < 0)
		table->cursor = 0;

	/* Acha a posição de edição */
	offset=table->data + table->cursor * (sizeof(char) + table->registerLen);
	fseek(table->file, offset, SEEK_SET);

	/* Checa se o registro está ativo */
	active = getc(table->file);
	if (!active)
		return LTERR_INACTIVE_REGISTER;


	// Grava o buffer AQUI !NAO! FUNCIONA!
	for (F = 0; F < table->fieldsLen; F++)
		fwrite(table->buffer[F], sizeof(char), table->fields[F].size, table->file);
	fflush(table->file);

	// Limpa o buffer, para que dados anteriores não deixem fragmentos
	for (F = 0; F < table->fieldsLen; F++)
		memset(table->buffer[F], '\0', table->fields[F].size);

	return 0;
}

O link para o codigo completo vai aqui? http://www.4shared.com/zip/rRuN9OLB/ltables.html
Meu email: [email removido]
Obrigado.

31 Respostas

J

Cara, que bagunça. Misturou c++ com c.

Se for usar c++ as estruturas de dados estão prontas e os arquivos pode ser lidos e gravados com fstream. C++ e C são coisas diferentes.

Olha aqui:

Stl Io
http://www.cplusplus.com/reference/iolibrary/

Stl containers
http://www.cplusplus.com/reference/stl/

E

Mas onde é que está misturado? É código C mesmo, mas tem uma coisinha chata - não estou conseguindo imaginar corretamente o layout do arquivo.

E

Você vai ter de debugar seu programa com cuidado. Não vi, nesse programa, onde é que você acerta o valor de “registerLen”, por exemplo.

J

Mas onde é que está misturado? É código C mesmo, mas tem uma coisinha chata - não estou conseguindo imaginar corretamente o layout do arquivo.

string é c++

D

Juliocbq? string é c++ string.h é c mesmo. registerLen é calculado quando se cria a tabela com lt_open. Voce fornece um vetor de campos (LTField[]) e a funcao calcula o tamanho do registro.

D

entanglement:
Você vai ter de debugar seu programa com cuidado. Não vi, nesse programa, onde é que você acerta o valor de “registerLen”, por exemplo.
Ja debuguei. Quando chega na hora do fwrite em lt_edit nao ha nada de errado. Tanto e que se eu usar fread no lugar funciona. Le-se um registro para o buffer. So nao funciona gravar o buffer para o arquivo.

J

Olha, buffer é uma sequência de strings

Não tenho certeza, mas você não deveria alocar assim?

D

juliocbq:
Cara, que bagunça. Misturou c++ com c.

Se for usar c++ as estruturas de dados estão prontas e os arquivos pode ser lidos e gravados com fstream. C++ e C são coisas diferentes.

Olha aqui:

Stl Io
http://www.cplusplus.com/reference/iolibrary/

Stl containers
http://www.cplusplus.com/reference/stl/

Nao e bagunca. É código C puro. O link do 4shared fornece o codigo na integra para baixar. A estrutura ltables fornece toda a informacao que as funcoe precisarao para fazer leituras e escritas, etc.

D

juliocbq:
Olha, buffer é uma sequência de strings

Não tenho certeza, mas você não deveria alocar assim?

Funciona. buffer e uma matriz bidimendional mas buffer[F] é uma string. PAra ler uma string com fread voce multiplica o tamanho de um char pelo numero de caracteres que vc quer ler, nesse caso o tamanho da string.

J

é verdade, essa aí não é a da stl não…

J

daniloMPA:
juliocbq:
Olha, buffer é uma sequência de strings

Não tenho certeza, mas você não deveria alocar assim?

Funciona. buffer e uma matriz bidimendional mas buffer[F] é uma string. PAra ler uma string com fread voce multiplica o tamanho de um char pelo numero de caracteres que vc quer ler, nesse caso o tamanho da string.

:slight_smile:

D

O problema se concentra na lt_edit na hora de gravar o vetor de strings, o buffer. Nao ha problemas com os ponteiros, e o buffer sempre tem o conteudo que eu escrevi. Mas O fwrite nao grava!

E

Danilo-san, não tenho acesso ao 4Shared. De qualquer forma, é sempre aconselhável usar a macro offsetof (quando disponível) porque, devido a problemas de “packing” e “alignment”.
Quando você tem uma estrutura do tipo:

struct X {
    char x;
    int y;
    unsigned char[10] z;
};

você tem os seguintes problemas:
a) Qual é o tamanho da struct acima? Dica: não é 1 + 4 + 10 = 15. Provavelmente é 4 + 4 + 16 ou então 8 + 8 + 16, devido ao que o compilador C faz com o layout de structs em em memória (para que dados fiquem alinhados em endereços múltiplos do tamanho da palavra do processador e que ocupem um número inteiro de palavras.
b) Se você quer usar o valor de um determinado campo, devido ao “alignment” e ao “packing”, ele fica em uma posição que não é imediatamente fácil de você achar. Nesse caso, y provavelmente está em um endereço múltiplo de 4 ou de 8, não no deslocamento 1 byte a partir do início da struct.

E

só uma perguntinha. Não li seu código inteiro (até porque não estou com acesso ao 4shared, e tenho uma preguiça terrível de ler código dos outros).

Mas você quer gravar com um único fwrite todos os buffers de uma vez? Eu gravaria um de cada vez, cada qual com seu fwrite.

E

Desculpe, é isso que você fez. Só mais uma coisa - você, debugando, está vendo que ele está realmente pegando os buffers do jeito que você quer?

D

Entendo entanglement. Mas se houvesse um problema dessa natureza que interferisse no acesso ao membro buffer entao a lt_read tambem na funcionaria. Foi por isso que a coloquei lado a lado com a lt_edit para comparacao. Se fread funciona, por fwrite nao. O arquivo foi aberto em modo “r+b” e a lt_insert funciona inserindo um novo dado justamente com fwrite. Porque fwrite funciona para inserir ao fim do arquivo, mas nao sobrescrever um registro existente? Esse e meu problema…

D

Ao debugar, o buffer chega do jeito esperado ate o momento de chamar fwrite. So nao e gravado no arquivo apos a chamada.

D

for (F = 0; F < table->fieldsLen; F++) fwrite(table->buffer[F], sizeof(char), table->fields[F].size, table->file);

table->fieldsLen e o numero de campos e tambem de strings no buffer. Esse codigo grava um campo do buffer, ou seja, uma string do buffer por vez.

|campo1|campo2|campo3| e assim ate finalizar o registro.

J
daniloMPA:
for (F = 0; F < table->fieldsLen; F++)  
        fwrite(table->buffer[F], sizeof(char), table->fields[F].size, table->file);

table->fieldsLen e o numero de campos e tambem de strings no buffer. Esse codigo grava um campo do buffer, ou seja, uma string do buffer por vez.

|campo1|campo2|campo3| e assim ate finalizar o registro.

na documentação passam 1 na largura da data. Pode ser isso aí.

http://www.cplusplus.com/reference/cstdio/fwrite/

/* fwrite example : write buffer */
#include <stdio.h>

int main ()
{
  FILE * pFile;
  char buffer[] = { 'x' , 'y' , 'z' };
  pFile = fopen ( "myfile.bin" , "wb" );
  fwrite (buffer , 1 , sizeof(buffer) , pFile );
  fclose (pFile);
  return 0;
}
D

O livro c completo e total poe assim:

"size_t fwrite(void *buffer, size_t num_bytes, size_t count, FILE *fp)

O argumento count determina quantos itens (cada um de comprimento num_bytes) serao … escritos"

J

daniloMPA:
O livro c completo e total poe assim:

"size_t fwrite(void *buffer, size_t num_bytes, size_t count, FILE *fp)

O argumento count determina quantos itens (cada um de comprimento num_bytes) serao … escritos"

tudo bem danilo, mas segue a documentação pra ver se funciona. Imagino que o pecado esteja no tamanho dos bytes.

D

Entao num_bytes e o tamanho da unidade, nesse caso o char em bytes. Pode ser que a versao de c++ seja diferente. Mas aqui estou usando c. De qualquer forma nao descartei sua ideia :slight_smile:

J

Justamente. A gente não leu direito a documentação. Como eu que não li seu código direito.

D

The GNU C Library Reference Manual:

size_t fwrite (const void *data, size t size, size t count, FILE [Function]
*stream)
This function writes up to count objects of size size from the array data, to the
stream stream. The return value is normally count, if the call succeeds. Any other
value indicates some sort of error, such as running out of space.

Esta funcao escreve ate count objetos de tamanho size a partir do vetor data para a stream stream. Ou seja o mesmo conceito de c completo e total.

Este e a documentacao oficial da libc usada com o gcc.

J

daniloMPA:
The GNU C Library Reference Manual:

size_t fwrite (const void *data, size t size, size t count, FILE [Function]
*stream)
This function writes up to count objects of size size from the array data, to the
stream stream. The return value is normally count, if the call succeeds. Any other
value indicates some sort of error, such as running out of space.

Esta funcao escreve ate count objetos de tamanho size a partir do vetor data para a stream stream. Ou seja o mesmo conceito de c completo e total.

Este e a documentacao oficial da libc usada com o gcc.

tenta com 1 em t_size pra gente ver o resultado

G

Só para ter um pouco de informação a mais poderia testar o valor retornado pelo fwrite, veja se tem alguma coisa estranha. Se a escrita teve sucesso deverá estar igual ao parâmetro count.

E

Uma coisa que sempre recomendo a todos que vão ler e escrever arquivos binários é aprender a usar uma ferramenta como o “hexdump” do Linux ou o modo hexadecimal de alguns editores de texto (acho que o Ultraedit e o Notepad++ têm esse modo) para poder ver se você não está fazendo alguma coisa errada. Um exemplo de hexdump sendo rodado sobre um arquivo executável:

0000    4D 5A 90 00 03 00 00 00  04 00 00 00 FF FF 00 00   MZ..............
0010    B8 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00   ........@.......
0020    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
0030    00 00 00 00 00 00 00 00  00 00 00 00 80 00 00 00   ................
0040    0E 1F BA 0E 00 B4 09 CD  21 B8 01 4C CD 21 54 68   ........!..L.!Th
0050    69 73 20 70 72 6F 67 72  61 6D 20 63 61 6E 6E 6F   is program canno
0060    74 20 62 65 20 72 75 6E  20 69 6E 20 44 4F 53 20   t be run in DOS
0070    6D 6F 64 65 2E 0D 0D 0A  24 00 00 00 00 00 00 00   mode....$.......
0080    50 45 00 00 4C 01 05 00  6D 8E 36 33 00 00 00 00   PE..L...m.63....

Você acaba aprendendo muita coisa …

J

entanglement:
Uma coisa que sempre recomendo a todos que vão ler e escrever arquivos binários é aprender a usar uma ferramenta como o “hexdump” do Linux ou o modo hexadecimal de alguns editores de texto (acho que o Ultraedit e o Notepad++ têm esse modo) para poder ver se você não está fazendo alguma coisa errada. Um exemplo de hexdump sendo rodado sobre um arquivo executável:

0000    4D 5A 90 00 03 00 00 00  04 00 00 00 FF FF 00 00   MZ..............
0010    B8 00 00 00 00 00 00 00  40 00 00 00 00 00 00 00   ........@.......
0020    00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00   ................
0030    00 00 00 00 00 00 00 00  00 00 00 00 80 00 00 00   ................
0040    0E 1F BA 0E 00 B4 09 CD  21 B8 01 4C CD 21 54 68   ........!..L.!Th
0050    69 73 20 70 72 6F 67 72  61 6D 20 63 61 6E 6E 6F   is program canno
0060    74 20 62 65 20 72 75 6E  20 69 6E 20 44 4F 53 20   t be run in DOS
0070    6D 6F 64 65 2E 0D 0D 0A  24 00 00 00 00 00 00 00   mode....$.......
0080    50 45 00 00 4C 01 05 00  6D 8E 36 33 00 00 00 00   PE..L...m.63....

Você acaba aprendendo muita coisa …

Nossa, mas dos mode não acabou no win xp?

E

Todo arquivo executável do Windows está no formato PE (“Portable Executable Format”) e esse formato é assim: existe um antigo cabeçalho DOS que começa por MZ ( Mark Zbikowski ) que simplesmente, se alguém tentar executar esse arquivo em Windows 3.1 ou DOS, irá imprimir essa mensagem “This program cannot be executed in DOS mode” e sair.
A parte útil começa a partir do ponto em que está escrito “PE” (Portable Executable).

J

Eu me lembro que o winxp mantinha um modo de compatibilidade com o dos. O win95 e 98 rodavam em cima dele.

D

Pessoal, descobri o que ocorria - embora não o porquê - mas é o seguinte: quando eu chamo uma função de escrita como fwrite ou putc logo após ter chamado uma de leitura como fread ou getc - isso no modo “w+b” - a função de gravação simplesmente não funcina. E o retorno de fwrite, por exemplo, informa que foram escritas as count unidades. Como resolvi? Usei fseek só para reposicionar logo após a leitura e depois a função de escrita funcionou! Isso acontece com o mingw atual e com o openwatcom. Parece que chamar a função de leitura coloca a stream num modo interno que espera sómente por outra leitura ou simplesmente e inesperadamente impede efetivamente a escrita. Mas, isso deveria ocorrer no modo leitura E escrita? Não sei. Mas fica a experiência. E obrigado pelas mensagens. Até.

Criado 26 de março de 2013
Ultima resposta 3 de abr. de 2013
Respostas 31
Participantes 4