I got some weird characters into the file... $0# ϊ ?0#
what did i do wrong in writing the struct?
Code:
int main (){
struct books {
char name[30];
int npages;
char author[30];
} book1;
book1.name = "1000 leagues under the sea";
book1.npages = 250;
book1.author = "Jules Verne";
FILE *book;
book = fopen("book.txt", "wb");
/* trying to write the struct books into a file called book.txt */
fwrite( &book1, sizeof(book1), 1, book);
fclose(book);
return 0;
}
i changed some things now i get a file written. but i dont' get the npages right in the file.... it's like "Jules Verne 0# Πώ" ϊ 1000 leagues under the sea ”" "
You are storing binary representation of your struct data in the file. The strange characters that you see in the file is exactly that: the binary representation of the npages field. Yes, it will look like a set of strange characters, just like it is supposed to.
If you want to see the number of pages stored as a human-readable (text) representation of the number, you have to convert it from binary to text representation manually or use I/O functions that will do that for you.
In fact, if you want to see everything represented in human-readable format, you need a text file, not a binary file. I.e. you need to open it as a text file and use formatted-output functions to write the data.
FILE *book = fopen("book.txt", "wt");
fprintf(book, "%s %d %s\n", book1.name, book1.npages, book1.author);
fclose(book);
sizeof(struct books)
The number of bytes being copied is struct books and you never care about the number of bytes needed to store the strings. sizeof(struct books) will just have sizeof(pointers) included and not the number of bytes held by the pointer.
You can have a char array like
char name[20]; /* some size */
char author[40];
Now sizeof(struct books) includes the sizeof(name) + sizeof(author)
Related
I'd like to add an array of type 'struct classes' (definition included below) to a file. For instance, if allClasses[0].title is equal to "Math" and allClasses[0].class_id is equal to 1, I'd like the file to have the following input:
1Math/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0
If another class is added with a title of Science, then the file should now read
1Math/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/02Science/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0
What appears to happen is that, although the code will catch the char array part of the struct (math / science in the example), it will have trouble with the int and instead print out junk in its place (and the junk is often times longer than 1 character / 1 int long).
From experience, the code format (with a few adjustments, of course) works just fine when using a struct with variables that are only char arrays. However, it glitches out when using ints. Is this because of ASCII conversions, or something similar? How can I adjust the code so that I get the input with both the int and the char array?
void addClasses(char *given_title) {
FILE *fp;
fp = fopen("classes.db", "r");
if (numClasses == 0 && fp != NULL) {
findClasses();
}
strcpy(allClasses[numClasses].title, given_title);
allClasses[numClasses].class_id = numClasses + 1;
numClasses++;
fclose(fp);
fp = fopen("classes.db", "w");
for (int i = 0; i < numClasses; i++) {
struct classes *object = malloc(sizeof(struct classes) - 1);
memset(object, 0, sizeof( struct classes ));
object->class_id = allClasses[i].class_id;
strcpy(object->title, allClasses[i].title);
fseek(fp, numClasses * (sizeof(struct classes) - 1), SEEK_END);
fwrite(object, sizeof(struct classes) - 1, 1, fp);
}
fclose( fp );
}
The struct:
struct classes {
int class_id;
char title[30];
};
A bit of extra (possibly unnecessary) background on some of the components in the code: the bit at the beginning of the method tries to read the file and start to fill the array with any structs that were already put into the file before starting the program. I'm not including the code for that, since the aforementioned glitch happens even when I have a fresh classes.db file (and thus, even when findClasses() never runs).
Small note, by the way: I can't change the class_id into a char / char array. It needs to be an int.
If you want to add it in the text form:
fprintf(fp, "%d,\"%s\"\n", object -> class_id, object -> title);
when you open the file with "w" you create new empty file. When you write to the file you do need to fseek.
If you want to append to existing file use "a" or "a+" instead.
Similar with this. But what if MAX_BOOKS would be unknown as well?
I want to get number of structures from a file.
My structure:
typedef struct material {
int mat_cislo;
char oznaceni[MAX_TEXT];
char mat_dodavatel[MAX_TEXT];
char dodavatel[MAX_TEXT];
float cena;
int mat_kusovnik;
} MATERIAL;
My code:
void nacist_material() {
FILE* pSoubor;
MATERIAL materialy_pocitadlo;
int i;
int b;
if((pSoubor = fopen(SOUBOR_MATERIAL, "rb")) == NULL ) {
printf("\nChyba při čtení souboru");
return;
}
pocet_zaznamu_materialu = 3;
printf("\n\n===>%d", pocet_zaznamu_materialu);
if(pocet_zaznamu_materialu > 0) {
printf("\nExistuje %d materialu", pocet_zaznamu_materialu);
free(pMaterialy);
pMaterialy = (MATERIAL *) malloc(pocet_zaznamu_materialu * sizeof(MATERIAL));
for(i = 0; i < pocet_zaznamu_materialu; i++) {
b = fread(&pMaterialy[i], sizeof(MATERIAL), 1, pSoubor);
}
printf("\n otrava %d", b);
}
else {
printf("\nNeexistuje předchozí záznam materialu");
}
fclose(pSoubor);
return;
}
Right now pocet_zaznamu_materialu is hard code to 3, because there are 3 structures in a file and it all works correctly. But what if number of structures in file changes?
Problem: I need to know - number of structures in file, how to a do it?
Thanks, sorry for eng
If the file is composed of nothing but a list of your desired struct stored contiguously, then the file's size, in bytes, will be a multiple of the size of your struct, and you can obtain the file size and then the number of structs in the file like so:
size_t len_file, num_structs;
fseek(fp, 0, SEEK_END);
len_file = ftell(fp);
rewind(fp);
num_structs = len_file/sizeof(MYSTRUCT);
This can be a real problem when you read from a dynamic file (another program writes at the end of file while you read it), a pipe or a network socket. In that case, you really have no way to guess the number of structs.
In that case, a common idiom is to use a dynamicaly allocated array of structs of an arbitrary size and then make it grow with realloc each time the currently allocated array is full. You could for example make the new size be twice the previous one.
That is the way C++ vectors manage their underlying array under the hood.
Have you considered adding a header to the file?
That is, place a special structure at the start of the file that tells you some information about the file. Something like ...
struct file_header {
char id[32]; /* Let this contain a special identifying string */
uint32_t version; /* version number in case the file structure changes */
uint32_t num_material; /* number of material structures in file */
};
Not only does this give you a relatively quick way to determine how many material structures you have in your file, it is also extensible. Perhaps you will want to store other structures in this file, and you want to know how many of each are in there--just add a new field and update the version.
If you want, you can even throw in some error checking.
student programmer here. I am having trouble reading input from a binary file in C. The data stored in the file are structs that look like this:
typedef struct reserve{
char *name;
char *ic;
int room_number;
} Reserve;
I get no problem when I write the struct to a binary file. Here is the code I did for writing to the file:
void reg_new() //Make a new reservation
{
Reserve newReserve = {"", "", 0};
char name[NAMEFIELD], ic[ICFIELD];
int room_number;
FILE *resvptr;
scanf_s("%s", name, NAMEFIELD);
scanf_s("%s", ic, ICFIELD);
scanf_s("%d", &room_number);
newReserve.name = name;
newReserve.ic = ic;
newReserve.room_number=room_number;
resvptr = fopen("reservations.dat", "wb");
fwrite(&newReserve, sizeof(Reserve), 1, resvptr);
fclose(resvptr);
}
And the code for reading from the file:
void reg_view() //view a reservation.
{
FILE *seekptr;
Reserve viewReserve = {"", "", 0};
int read;
if ( (seekptr = fopen("reservations.dat", "rb")) == NULL)
{
puts("Error: file could not be found.");
}
else
{
while ( !feof(seekptr))
{
read = fread(&viewReserve, sizeof(Reserve), 1, seekptr);
if(read != 0)
{
printf("Name: %s\nIC number: %s\nRoom Number: %d\n", viewReserve.name, viewReserve.ic, viewReserve.room_number);
}
}
fclose(seekptr);
}
Immediately after writing to the binary file and accessing the function for reading, the room number (of int value) reads fine, but the two strings (name and IC number) read out as garbage characters. Subsequently, closing and restarting the program, then attempting to read from the file will give me an access violation and bad pointer error.
The trouble is with reading the strings from the file. I'm suspecting a memory leak but I can't say for sure...can anyone help? Thanks.
You are writing pointers to the file, not the actual data. When you read the pointer from the file, it's just a memory address that you probably don't have access to, even if you do it in the same process that wrote the file (because it was actually a pointer to a function-scoped stack variable).
If you want to store the records in that way, make your function store the actual string data rather than the pointer:
typedef struct reserve{
char name[NAMEFIELD];
char ic[ICFIELD];
int room_number;
} Reserve;
And just do this:
scanf_s("%s", newReserve.name, NAMEFIELD);
scanf_s("%s", newReserve.ic, ICFIELD);
scanf_s("%d", &newReserve.room_number);
The structure consists of the integer and the addresses of two arrays (pointers). When you write it out and read it back, you are reading and writing the addresses of the character arrays - not their contents. You need to modify your routine to write and read the contents (including lengths) and also to allocate the storage to hold the contents when you read them back in.
I need to build an array of pointers to dynamically allocated structures (DBrecord) and fill that array with input from another file. Not sure how to approach this.
The data file will have the number of entries first, followed by entries in a specific order.
numOfEntries
lastName firstName studentID year gpa expGradYear
example:
1
Doe John 12345678 senior 3.14159 2015
Here's the code I have so far:
class.h
typedef enum {firstYear, sophomore, junior, senior, grad} class;
main.c
#include <stdio.h>
#include <stdlib.h>
#include "class.h"
int main(){
//DBrecord is name for structure
struct DBrecord{
int DBrecordID; //ID for each entry, range 0-319
char *last; //student last name
char *first; //student first name
char studentID[8]; //student ID
int age; //student age
class year; //year in school
float gpa; //GPA
int expGradYear; //expected graduation year
};
int numEntries; //total number of entries, first num in data file
struct DBrecord **ptrToDB;
//scan first int in data file and assign to numEntries
scanf("%d", &numEntries);
//allocate memory for structures, each is 36 bytes
*ptrToDB = malloc (sizeof(struct DBrecord) * numEntries);
//free allocated memory
free(ptrToDB);
//build an array of pointers to dynamically allocated structures
//fill that array with input from data file
//build 7 arrays of pointers to DBrecords, one for each field except DB ID
//sort each a different way
//note the 7 arrays are pointers, no copying
//print each of the 7 sorted arrays
return 0;
}
I can give you some snippets on how to look at this problem.
First - I would avoid using class name for any variable, because in many object-oriented programming languages (including C++) it is a keyword and can't be a name of variable.
Structure DBrecord
It might be a good idea to use typedef. Then you could declare a struct variable without using "struct DBrecord", just "DBrecord". But that's optional. This is how it would look:
typedef struct {
int DBrecordID; // ID for each entry
char *lastName;
char *firstName;
char studentID[8];
...
} DBrecord;
Loading from file
In this homework you have the number of records at the beginning of the file, so you don't need to take "extra" care about it. Just load it.
Let's assume the file is like this:
2
Doe John 12345678 senior 3.14159 2015
Carl Boss 32315484 junior 2.71 2013
Therefore the first thing you do with your file is to open it.
Portable way of working with files is by using FILE pointer. Let me show it (stdio.h must be included):
FILE *filePtr; // Define pointer to file
if((filePtr = fopen("records.txt", "r")) == NULL) // If couldn't open the file
{
printf("Error: Couldn't open records.txt file.\n"); // Printf error message
exit(1); // Exit the program
}
Then you can read from your file by line using fgets() to read by lines or fgetc() to read by characters. This is how you can read number of records (remember that it's on the first line and we've just opened the file - we are at the beginning of the file):
char buffer[100]; // Define the buffer
fgets(buffer, 100 /* size of buffer */, filePtr);
Now buffer contains the first line (without \n character) - number of records. Continue with converting the num's characters into integer (here stdlib.h also has to be included):
int numOfRecords = atoi(buffer);
Allocating enough DBrecords
Now you know the number of records, you can allocate enough space for them. We will use array of pointers.
DBrecord **recs;
recs = (DBrecord **) malloc(sizeof(DBrecord *) * numOfRecords);
Now we have created array of pointers, so now we need to allocate every individual pointer as a DBrecord. Using cycle:
int i;
for(i = 0; i < numOfRecords; i++)
{
recs[i] = (DBRecord *) malloc(sizeof(DBrecord));
}
Now you can acces array elements (= individual records) like this:
recs[0]->lastname /* two possibilities */
*(recs[0]).lastname
an so on.
Filling array with values from file
Now you know everything to get the homework done. This way you fill the array:
int i;
for(i = 0; i < numOfRecords; i++)
{
// Content of cycle reads one line of a file and parses the values into recs[i]->type...
/* I give you small advice - you can use fgetc(filePtr); to obtain character by character from the file. As a 'deliminer' character you use space, when you hit newline, then the for cycle continues.
You know the order of stored values in the file, so it shouldn't be hard for you.
If you don't get it, let me now in comments */
}
Is it somehow clearer now?
EDIT: File name as main's argument
There are usually two ways of 'passing' arguments (values) to a program. They are:
./program < records.txt // Here the file's name is passed to program on stdin
./program records.txt // Here the file's name is passed as value in `argv`.
If you can choose, I strongly recommend you the second one. Therefore you need to have main defined as this:
int main(int argc, char *argv[]) // this is important!
{
// code
return 0;
}
argc is integer which says, how much arguments were passed to the program. argv is array storing them. Remember, that the first argument is name of the program. Therefore if you need to check for it, do it:
if(argc != 2)
{
printf("Number of arguments is invalid\n");
exit(1); // exit program
}
Then you only put argv[1] into fopen function, instead of the string "records.txt".
EDIT 2: Reading file's name from stdin
Another approach must be done, if the name of the records file is passed to the program via ./program < records.txt, which means that "records.txt" (without quotes) will be passed (redirected) to program's standard input.
Therefore to handle that, you can do this:
char filename[50]; // buffer for file's name
scanf("%s", &filename); // reads standard input into 'filename' string until white character appears (new line, blank, tabulator).
Then you have your desired file's name in filename string.
Where to start, where to start.....
//allocate memory for structures, each is 36 bytes
mem = (double *)malloc(36*numEntries);
malloc should be malloc (sizeof (struct DBRecord) * numEntries);
don't cast the result of malloc
2a. you forgot stdlib.h
Why include class.h?
your array of pointers are not double, they are instead
struct DBRecord **ptrToDB;
*ptrToDB = malloc (sizeof (struct DBRecord) * numEntries);
This should get you started.
Next, free() should be the last thing you do before leaving your function (and yes, main is a function)
You'll have to insert some code for the next part, I can't do the homework for you.
I have a structure with the following definition:
typedef struct myStruct{
int a;
char* c;
int f;
} OBJECT;
I am able to populate this object and write it to a file. However I am not able to read the char* c value in it...while trying to read it, it gives me a segmentation fault error. Is there anything wrong with my code:
//writensave.c
#include "mystruct.h"
#include <stdio.h>
#include <string.h>
#define p(x) printf(x)
int main()
{
p("Creating file to write...\n");
FILE* file = fopen("struct.dat", "w");
if(file == NULL)
{
printf("Error opening file\n");
return -1;
}
p("creating structure\n");
OBJECT* myObj = (OBJECT*)malloc(sizeof(OBJECT));
myObj->a = 20;
myObj->f = 45;
myObj->c = (char*)calloc(30, sizeof(char));
strcpy(myObj->c,
"This is a test");
p("Writing object to file...\n");
fwrite(myObj, sizeof(OBJECT), 1, file);
p("Close file\n");
fclose(file);
p("End of program\n");
return 0;
}
Here is how I am trying to read it:
//readnprint.c
#include "mystruct.h"
#include <stdio.h>
#define p(x) printf(x)
int main()
{
FILE* file = fopen("struct.dat", "r");
char* buffer;
buffer = (char*) malloc(sizeof(OBJECT));
if(file == NULL)
{
p("Error opening file");
return -1;
}
fread((void *)buffer, sizeof(OBJECT), 1, file);
OBJECT* obj = (OBJECT*)buffer;
printf("obj->a = %d\nobj->f = %d \nobj->c = %s",
obj->a,
obj->f,
obj->c);
fclose(file);
return 0;
}
When you write your object, you're writing the pointer value to the file instead of the pointed-to information.
What you need to do is not just fwrite/fread your whole structure, but rather do it a field at a time. fwrite the a and the f as you're doing with the object, but then you need to do something special with the string. Try fwrite/fread of the length (not represented in your data structure, that's fine) and then fwrite/fread the character buffer. On read you'll need to allocate that, of course.
Your first code sample seems to assume that the strings are going to be no larger than 30 characters. If this is the case, then the easiest fix is probably to re-define your structure like this:
typedef struct myStruct{
int a;
char c[30];
int f;
} OBJECT;
Otherwise, you're just storing a pointer to dynamically-allocated memory that will be destroyed when your program exits (so when you retrieve this pointer later, the address is worthless and most likely illegal to access).
You're saving a pointer to a char, not the string itself. When you try to reload the file you're running in a new process with a different address space and that pointer is no longer valid. You need to save the string by value instead.
I would like to add a note about a potential portability issue, which may or may not exist depending upon the planned use of the data file.
If the data file is to be shared between computers of different endian-ness, you will need to configure file-to-host and host-to-file converters for non-char types (int, short, long, long long, ...). Furthermore, it could be prudent to use the types from stdint.h (int16_t, int32_t, ...) instead to guarantee the size you want.
However, if the data file will not be moving around anywhere, then ignore these two points.
The char * field of your structure is known as a variable length field. When you write this field, you will need a method for determining the length of the text. Two popular methods are:
1. Writing Size First
2. Writing terminal character
Writing Size First
In this method, the size of the text data is written first, followed immediately by the data.
Advantages: Text can load quicker by block reads.
Disadvantages: Two reads required, extra space required for the length data.
Example code fragment:
struct My_Struct
{
char * text_field;
};
void Write_Text_Field(struct My_Struct * p_struct, FILE * output)
{
size_t text_length = strlen(p_struct->text_field);
fprintf(output, "%d\n", text_length);
fprintf(output, "%s", p_struct->text_field);
return;
}
void Read_Text_Field(struct My_STruct * p_struct, FILE * input)
{
size_t text_length = 0;
char * p_text = NULL;
fscanf(input, "%d", &text_length);
p_text = (char *) malloc(text_length + sizeof('\0'));
if (p_text)
{
fread(p_text, 1, text_length, input);
p_text[text_length] = '\0';
}
}
Writing terminal character
In this method the text data is written followed by a "terminal" character. Very similar to a C language string.
Advantages: Requires less space than Size First.
Disadvantages: Text must be read one byte at a time so terminal character is not missed.
Fixed size field
Instead of using a char* as a member, use a char [N], where N is the maximum size of the field.
Advantages: Fixed sized records can be read as blocks.
Makes random access in files easier.
Disadvantages: Waste of space if all the field space is not used.
Problems when the field size is too small.
When writing data structures to a file, you should consider using a database. There are small ones such as SQLite and bigger ones such as MySQL. Don't waste time writing and debugging permanent storage routines for your data when they have already been written and tested.