Hello i have a structure with couple of variables and i try to write and read them from/to binary files but when i read them i see only strange symbols and i dont know what i messed up,i have tried couple of variants but none of them works
typedef struct catalog
{
char num[20];
char name[80];
char author[50];
double price;
int year;
char publisher[80];
} Catalog;
Catalog* createCatalogData()
{
Catalog* c = malloc(sizeof(*c));
if (!c)
{
// Ups... add error handling
exit(0);
}
printf("Add num ");
getString(&c->num);
printf("Add name ");
getString(&c->name);
printf("add author ");
getString(&c->author);
printf("Add price ");
if (scanf("%lf", &c->price) != 1)
{
// Ups... add error handling
exit(0);
}
printf("Add publisher");
getString(&c->publisher);
printf("Add year");
if (scanf("%d", &c->year) != 1)
{
// Ups... add error handling
exit(0);
}
char *filePath = malloc(strlen(c->num) + 13);
char *folderName = "Catalogs\\";
strcpy(filePath, folderName);
strcat(filePath, c->num);
strcat(filePath, ".bin");
FILE *file = fopen(filePath, "wb");
if (file == NULL)
{
printf("Error opening file!\n");
exit(1);
}
fwrite(&c->num,1, strlen(c->num), file);
fwrite(&c->name,1, strlen(c->name), file);
fwrite(&c->author,1, strlen(c->author), file);
fwrite(&c->price, 1, sizeof(double), file);
fwrite(&c->publisher,1, strlen(c->publisher), file);
fwrite(&c->year,1, sizeof(int), file);
fclose(file);
return c;
}
Catalog* readCatalogData(char *filePath)
{
Catalog* c = malloc(sizeof(*c));
FILE* fh;
fopen_s(&fh, filePath, "rb");
//check if file exists
char *ptr;
//read line by line
const size_t line_size = 300;
char* line = malloc(line_size);
int counter = 0;
char* date;
fread(c->num, 1, 21, fh);
fread(c->name, 1, 80, fh);
fread(c->author, 1, 50, fh);
fread(&c->price, 1, sizeof(double), fh);
fread(c->publisher, 1, 80, fh);
fread(c->year, 1, sizeof(int), fh);
return c;
}
When you do e.g.
fwrite(&c->num,1, strlen(c->num), file);
you write a variable number of bytes without any terminator. When you read the file you have no idea how many bytes to actually read.
The above fwrite call actually contains another error, in that you write a pointer and not the actual data in c->num.
Instead of writing the data members one by one, write the whole structure in one single call:
fwrite(c, sizeof c, 1, file);
And when reading the file read the whole structure in a single fread call.
Important note (and as noted by Attie in a comment): If you intend to make this portable, then you should use serialization to read and write the data, as the size of the structure might not be the same on all platforms.
For simple code and just "experimenting" then it will work fine.
Related
I have a piece of code that is designed to replicate the creation of a .tar file albeit a simpler version of it. It will take in 2 .txt files and produce a .tar file of the 2 files. Below is my code for doing so. However when opening the .tar file it is corrupted and sure enough, by viewing the data in a hex editor the data is cut off.
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
#define RECORDSIZE 512
#define NAMSIZ 100
#define TUNMLEN 32
#define TGNMLEN 32
struct header {
char name[NAMSIZ];//needed
char size[12];//needed
};
int main(int argc, char** argv) {
//argv[1] file1 argv[2] file2
char* file1 = argv[1];
char* file2 = argv[2];
FILE* f;
int lSize;
char temp_length[10];
char* file1_data, * file2_data;
int result;
//char* output_str = (char*)malloc(sizeof)
f = fopen(file1, "rb");
if (f == NULL) {
printf("File error!");
return 1;
}
fseek(f, 0, SEEK_END);
lSize = ftell(f);
fseek(f, 0, SEEK_SET);
file1_data = (char*)malloc(sizeof(char) * lSize);
if (file1_data == NULL) {
printf("Memory error!");
return 1;
}
result = fread(file1_data, 1, lSize, f);
file1_data[result] = '\0';
fclose(f);
sprintf(temp_length, "%d", lSize);
struct header* h1 = malloc(sizeof(struct header));
strcpy(h1->name, file1);
strcpy(h1->size, temp_length);
printf("Name:%s Value:%s\n", h1->name, h1->size);
printf("File 1 data:%s\n", file1_data);
f = fopen(file2, "rb");
if (f == NULL) {
printf("File error!");
return 1;
}
fseek(f, 0, SEEK_END);
lSize = ftell(f);
fseek(f, 0, SEEK_SET);
file2_data = (char*)malloc(sizeof(char) * lSize);
if (file2_data == NULL) {
printf("Memory error!");
return 1;
}
result = fread(file2_data, 1, lSize, f);
file2_data[result] = '\0';
fclose(f);
sprintf(temp_length, "%d", lSize);
struct header* h2 = malloc(sizeof(struct header));
strcpy(h2->name, file1);
strcpy(h2->size, temp_length);
printf("Name:%s Value:%s\n", h2->name, h2->size);
printf("File 2 data:%s\n", file2_data);
//allocate mem for output buffer
int total = sizeof(struct header) + sizeof(struct header) + sizeof(file1_data) + sizeof(file2_data);
printf("total length %d\n", total);
f = fopen("Result.tar", "wb");
//fwrite(input,length,no of ele,output buffer)
fwrite(h1, sizeof(struct header), 1, f);
fwrite(file1_data, sizeof(file1_data), 1, f);
fwrite(h2, sizeof(struct header), 1, f);
fwrite(file2_data, sizeof(file2_data), 1, f);
if (fwrite != 0)
printf("Contents to file written successfully !\n");
else
printf("Error writing file !\n");
fclose(f);
}
First file name: File1.txt
Data within:
This is File 1.
Second file name: File2.txt
Data within:
This is File 2.
File 2 has more data inside.
Decoded text output:
File1.txt�ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ31�ÍÍÍÍÍÍÍÍÍThis is File1.txt�ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ63�ÍÍÍÍÍÍÍÍÍThis is
As observed, File 2's name as well as the subsequent data has been cut off. I've been debugging but I'm unsure where am I going wrong as fwrite does not return a 0 hence, I'm assuming it's successful.
As the print statements are exactly what I expected, I don't think the reading of the data is the issue but rather the fwrite function. Hence I would like to seek advice on it.
The error resids in the way you calculate the total length. Indeed, you do not want to use sizeof(file1_data) as the length of the file. Instead you want to use the value returned when reading in result.
Create two variables file1_length and file2_length. Then populate them with the size of their respective file:
...
size_t f1_len, f2_len;
...
f = fopen(file1, "rb");
if (f == NULL) {
printf("File error!");
return 1;
}
fseek(f, 0, SEEK_END);
f1_size = ftell(f);
fseek(f, 0, SEEK_SET);
file1_data = (char*)malloc(sizeof(char) * f1_len);
...
int total = sizeof(struct header) + sizeof(struct header) + f1_len + f2_len;
...
fwrite(h1, sizeof(struct header), 1, f);
fwrite(file1_data, f1_len, 1, f);
fwrite(h2, sizeof(struct header), 1, f);
fwrite(file2_data, f2_len, 1, f);
...
Finally, use these variable as the length of the files' content.
NOTE: The value obtained by sizeof(file1_data) indicate the size of the type. Here, since file1_data is of type char * you get 4.
In this code snippet I'm trying to add a string in a binary file. If the string already exists, I return error, otherwise I add it at EOF. I creataed two functions, void AddNewTeam(FILE* fp, char* newTeam), and int SearchTeam(FILE* fp, char* newTeam) as shown below, but that didn't work. Everytime I enter a string, it is added at EOF, even if is in the binary file.
I think the problem is the fread function, I tried to print the value returned by the fread but it is always 0. Can someone help me trying to understand what's wrong with this code and why it's not working?
void AddNewTeam(FILE* fp, const char* newTeam){
int found;
if((fp = fopen("File.dat", "rb")) == NULL){
fp = fopen("File.dat", "ab");
fclose(fp);
}
printf("\tEnter New Team: ");
scanf("%s", newTeam);
found = SearchTeam(fp, newTeam);
if(found == 0){
if((fp = fopen("File.dat", "ab"))){
fwrite(newTeam, sizeof(newTeam), 1, fp);
printf("\tThe following team has been successfully loaded\n");
fclose(fp);
}
}else if(found == 1){
printf("\tThis team already exists\n");
exit(EXIT_FAILURE);
}
}
int SearchTeam(FILE* fp, const char* newTeam){
char string[MAX][MAX_LENGTH];
int counter, result, found = 0;
if((fp = fopen("File.dat", "rb"))){
fseek(fp, 0, SEEK_SET);
for(counter = 0; !feof(fp); counter++){
if(fread(string[counter], sizeof(string[counter]), 1, fp) == 1){
result = strcmp(newTeam, string[counter]);
if(result == 0){
found = 1; break;
}else if(result != 0){
found = 0;
}
}
}
fclose(fp);
return found;
}else if(fp == NULL){
printf("\tError opening binary file\n");
exit(EXIT_FAILURE);
}
}
This is the main function and the function prototypes
int SearchTeam(FILE* fp, const char* newTeam);
void AddNewTeam(FILE* fp, const char* newTeam);
int main(void){
FILE* fp;
char newTeam[MAX_LENGTH];
AddNewTeam(fp, newTeam);
return 0;
}
You're not appending to the file correctly. The search code expects each team name to be in a block of MAX_LENGTH bytes. But when you write, you do:
fwrite(newTeam, sizeof(newTeam), 1, fp);
newTeam is a pointer, not an array, so sizeof will be the size of a pointer, typically 4 or 8 bytes. You need to write MAX_LENGTH bytes so this will match the way you read from the file.
fwrite(newTeam, MAX_SIZE, 1, fp);
I'm currently trying to use a CMP decompressor: https://web.archive.org/web/20070113004119/http://rewiki.regengedanken.de:80/wiki/.CMP
It does in fact decompress the cmp, but it does not write it into a file.
So i tried myself.
int main(int argc, char** argv)
{
int length, dstLength;
unsigned char* fileInMem; //compressed data
unsigned char* dstFile; //decompressed data
if (argc < 2) {
fprintf(stderr, "give filename.cmp as parameter\n");
return 1;
}
printf("%s", argv[1]);
fileInMem = loadFile(argv[1], &length); //compressed data read
if (fileInMem == NULL) {
return 1;
}
dstFile = parseCmp(fileInMem, length, &dstLength); //decompress and assign data to dstFile
if (dstFile) {
/* Now we can save the file from dstFile, dstLength bytes */
printf("%d bytes depacked\n", dstLength);
for (int i = 0; i < 16; i++) {
dataArray[i] = fileInMem[i];
}
FILE *writer = fopen(argv[2], "r+");
//fputs(fileInMem, writer);
//fputs(dstFile, writer);
fclose(writer);
free(dstFile);
}
free(fileInMem);
return 0;
}
As you can see the decompressed data is a pointer to an unsigned char (according to the website a bitstream) and I tried fputs() from stdio.h, but the resulting file contains only 4 Bytes when viewed in a hex-editor.
If you need more information, please comment.
Thank you in advance.
Edit: This is what I was able to change thanks to your help, but when I open the file, it is still empty:
FILE* writer = fopen(argv[2], "wb");
fwrite(dstFile, 192, 192, writer);
192, because the length of the first decompressed Image is 192 Bytes large.
This is a common issue.
First, you need to open the output file writer for writing in binary mode ("wb").
FILE *writer = fopen(argv[2], "wb");
Second, you can't use fputs to write arbitrary data to a file, since it expects a string. Use fwrite instead: (assuming writer is the output file, dstFile the decompressed data and dstLength the amount of bytes to write)
fwrite(dstFile, 1, dstLength, writer);
If you examine the resulting file with an hex editor, you will see it is identical to the decompressed data.
Test-update
I wrote some test-code to see what is wrong, share your results so we can help you.
Add these functions to your code:
void printDataToScreen(unsigned char *dataptr, int datalen)
{
if (dataptr == NULL)
{
printf("[!] ERROR, NULL POINTER PROVIDED!\n");
return;
}
printf("> Dumping %d bytes of data into the terminal...\n", datalen);
for (int i = 0; i < datalen; i++)
{
if (i % 16 == 0)
printf("\n ");
printf("%02X ", dataptr[i]);
}
printf("\n\n");
}
void writeDataToFile(char *fileName, unsigned char *dataptr, int datalen)
{
FILE *file = fopen(fileName, "wb");
if (dataptr == NULL)
{
printf("[!] ERROR, NULL POINTER PROVIDED!\n");
return;
} else if (file == NULL)
{
printf("[!] ERROR WHILE OPENING FILE '%s'!\n", fileName);
return;
}
printf("> Writting %d bytes of data to '%s'...\n", datalen, fileName);
int writtenBytes = fwrite(dataptr, 1, datalen, file);
printf(" Done, %d bytes written!\n\n", writtenBytes);
fclose(file);
}
void runTest(char *fileName, unsigned char *dataptr, int datalen)
{
printf("Running tests... [0/2 done]\n");
printDataToScreen(dataptr, datalen);
printf("Running tests... [1/2 done]\n");
writeDataToFile(fileName, dataptr, datalen);
printf("Finished! [2/2 done]\n");
}
Call it like this:
runTest(argv[2], dstFile, dstLength);
Add the call to this place in your code (comment this code, also the line where you close writer):
FILE *writer = fopen(argv[2], "r+");
//fputs(fileInMem, writer);
//fputs(dstFile, writer);
Please share your results.
could you help me?
Here is the whole program: http://knking.com/books/c2/programs/inventory2.c
I need to add dump and restore functions that will write (using fwrite) the linked list into the file or load it (using fread) from the file.
My take:
void dump(void)
{
FILE *fp;
char output_file[30];
int i;
printf("Enter name of input file: ");
scanf("%s", &output_file);
if ((fp = fopen(output_file, "wb")) == NULL) {
fprintf(stderr, "Can't open %s\n", output_file);
exit(EXIT_FAILURE);
}
while (inventory != NULL) {
fwrite(&inventory->number, sizeof(inventory->number), 1, fp);
fwrite(inventory->name, strlen(inventory->name) + 1, 1, fp);
fwrite(&inventory->on_hand, sizeof(inventory->on_hand), 1, fp);
inventory = inventory->next;
}
fclose(fp);
}
void restore(void)
{
FILE *fp;
char input_file[30];
int i = 0;
printf("Enter name of output file: ");
scanf("%s", &input_file);
if ((fp = fopen(input_file, "rb")) == NULL) {
fprintf(stderr, "Can't open %s\n", input_file);
exit(EXIT_FAILURE);
}
inventory = malloc(sizeof(struct part));
fread(&inventory->number, sizeof(inventory->number), 1, fp);
fread(&inventory->name[i], sizeof(inventory->name), 1, fp);
fread(&inventory->on_hand, sizeof(inventory->on_hand), 1, fp);
fclose(fp);
}
Restore function keeps crashing. It also keeps reading 26 characters to inventory->name causing inventory->on_hand showing wrong values. I also have no idea how to load the rest of linked list.
I need to build a program that reads each record, and according to that record information would update some other records on the same file. For that, I was thinking in this approach:
int main(int argc, char *argv[]) {
FILE *my_file;
int files_read;
struct my_struct an_struct;
my_file = fopen("myfile.dat", "rb");
files_read = fread(&an_struct, sizeof(struct my_struct), 1, my_file);
printf("main->files_read: %d \n", files_read); //This prints one
while (files_read == 1) {
do_update();
files_read = fread(&an_struct, sizeof(struct my_struct), 1, my_file);
printf("main->files_read: %d \n", files_read); //This prints one
}
fclose(archivo_paises);
return 0;
}
In the main function I'm reading the contents of the file, and every time I call read I get one as a response until I reach the end of the file. The problem is in the do_update function:
void do_update() {
FILE *my_file;
int files_read;
struct my_struct an_struct;
struct my_struct another_struct;
my_files = fopen("myfile.dat", "wb+"); //Using rb+ solves it
files_read = fread(&an_struct, sizeof(struct my_struct), 1, my_file);
printf("do_update->files_read: %d \n", files_read);
//This printed zero!. Prints one using rb+
while (files_read == 1) { //This never gets executed. Unless you use rb+
if(something){
fwrite(&another_struct, sizeof(struct my_struct), 1, my_file);
// Using rb+, this returns zero and didn't update
}
files_read = fread(&an_struct, sizeof(struct my_struct), 1, my_file);
printf("do_update->files_read: %d \n", files_read);
}
fclose(my_file);
}
What's happening is that the files_read variable gets the value of zero after the read call, so the logic to update the file is never executed.
Why is read returning zero when opening a file for wb+?
Update:
Using rb+ as file mode on do_update() works, but now the call to fwrite() always returns zero, and it didn't update the file. Is is related to the mode?
fwrite is moving the position in the file to the end of the file. The fread then has nothing to read.
Use fgetpos to save the file position before the fwrite, and fsetpos to set the position back after the fwrite.
The meaning of the flag "w+" (from http://www.cplusplus.com/reference/cstdio/fopen/):
write/update: Create an empty file and open it for update (both for input and output). If a file with the same name already exists its contents are discarded and the file is treated as a new empty file.
When you open a file with "w+", you will need to write to it first before you can read from it.
Update
Example program to demonstrate use of "rb+".
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void createFile(char const* filename, int num)
{
int i = 0;
int data = 0;
FILE* out = fopen(filename, "wb");
if ( out == NULL )
{
return;
}
for (i = 0; i < num; ++i )
{
data = rand()/10000;
fwrite(&data, sizeof(data), 1, out);
}
fclose(out);
}
void displayFileContents(char const* filename, int num)
{
int i = 0;
int data = 0;
FILE* in = fopen(filename, "rb");
if ( in == NULL )
{
return;
}
for (i = 0; i < num; ++i )
{
fread(&data, sizeof(data), 1, in);
printf("%d\n", data);
}
fclose(in);
}
void testReadAndWrite(char const* filename, int num)
{
int i = 0;
int data = 0;
long int pos = 0;
FILE* in = fopen(filename, "rb+");
if ( in == NULL )
{
return;
}
for ( i = 0; i < num; ++i )
{
pos = ftell(in);
fread(&data, sizeof(data), 1, in);
printf("%d\n", data);
// Rewind to previos position.
fseek(in, pos, SEEK_SET);
// Write at the previus position.
data = rand();
printf("%d\n", data);
if ( fwrite(&data, sizeof(data), 1, in) != 1 )
{
printf("Unable to write using fwrite.\n");
}
// Rewind to previos position.
fseek(in, pos, SEEK_SET);
// Read from the previus position.
if ( fread(&data, sizeof(data), 1, in) != 1 )
{
printf("Unable to read using fread.\n");
}
printf("%d\n\n", data);
}
fclose(in);
}
int main()
{
char const* filename = "test.txt";
int num = 10;
// See the random number generator.
srand(time(NULL));
// Create a file with some random data.
createFile(filename, num);
// Display the contents of the file.
displayFileContents(filename, num);
printf("\n");
// Test read and write using a single FILE*
testReadAndWrite(filename, num);
}
Sample output:
51830
169074
141071
61921
145333
101195
139074
9535
164668
49552
51830
1030292590
1030292590
169074
1003635396
1003635396
141071
1060541073
1060541073
61921
474399692
474399692
145333
1467401071
1467401071
101195
830521014
830521014
139074
1186142943
1186142943
9535
1759682963
1759682963
164668
848798825
848798825
49552
60932215
60932215