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.
Related
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.
I made a simple dictionary code, and surfed for an hour, and I found a file I/O code. But my compiler(I use Microsoft visual C++) says my code(unfortunately, the core part of the code) is wrong. but I can't get it. What is actually wrong and why???
/*
DosDic ver 1.0.0 2015-07-03
*/
#include<stdio.h>
#include<string.h>
char key = 0;
FILE *fp; //set a file pointer var
fp = fopen("dicdata.dat","r"); //open a file
int b = 0;
int trial = 0;
char result[];
char searchfor[] = fp; //save a whole list of dictionary in a var
int i;
char sb[]; //var for search in a list
int getsearchtrial(char sb[]){
for(i=0;i=strlen(sb);i++){ //how much I tried to reach to the word
switch((int)searchfor[b]-(int)sb[i]){ //are two alphabets same?
case 0 :
default : i=0;
}
b++; //keep finding in a list
trial++; //try again
}
return trial;
}
int adress;
int mainpage(){
printf("Type what you want to search : ");
scanf("%c",sb[ ]);
getsearchtrial(sb[ ]) - strlen(sb[ ]) = adress; //where the word is located in the list
for(i = adress;i = adress + 30; i++){ //print
printf("%c",searchfor[i]);
}
printf("Thank you for using DosDic ver.1.0!"); //thank you!
}
void main(){ //call all those functions
mainpage();
fclose(fp); //close list
}
//and whats wrong with this? i cant even get it, but it's not working
Multiple issues.
First of all, you can't assign the result of fopen to fp outside the body of a function; you must move fp = fopen("dicdata.dat", "r"); to within the body of one of your functions (getsearchtrial most likely).
Secondly, you don't read from a file by simply assigning a file pointer to an object; you must use a library function like fscanf or fread or fgets. Assuming your file contains a single string of length 80, you'd need to write something like
char searchfor[81] = {0}; // make sure string is initially empty,
// extra space for string terminator
if ( !fgets( searchfor, sizeof searchfor, fp) )
{
// error reading from file
}
Of course, this depends on how your input file is structured. If it contains a list of strings, then you'll need to use a multidimensional array (or some other structure).
Third, when you declare an array, must specify its size before you can use it. result and searchfor are incomplete array definitions.
Finally, this line
getsearchtrial(sb[ ]) - strlen(sb[ ]) = adress;
needs to be reversed; the target of an assignment must be on the left of the assignment operator.
You need to step back and learn how to write C code from the ground up.
There is so much wrong I'm not even going to itemise it all here - most of it seems to stem from your lack of understanding of arrays in C.
Most notably...
You can't declare an array and not initialise it or specify a size.
You can't assign a FILE * to a char array (and expect decent
results).
You can't execute a statement like fp = fopen at the
global scope like you are.
Try this tutorial and you may fix 95% of your problems, then go from there.
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)
I'm working on creating a student database in C. The final thing I need to be able to do read and write the database I create to a file. So I've already got an array full of pointers to student structures, and I need to write it to a file. Once I have it written, I need to be able to read it back into my array as well.
I'm really not sure how to do it though. This is my struct:
typedef struct student Student;
struct student
{
char name[300];
int age;
char course1[300];
char course2[300];
char remarks[300];
};
Student *Data[30];
And these are the functions I've written to work with file:
void writeDisk()
{
FILE *file = fopen("disk.dat", "ab");
fwrite(&Data, sizeof(Student), count, file);
fclose(file);
}
void loadDisk()
{
FILE *file = fopen("disk.dat", "rb");
if (file != NULL)
{
fread(&Data, sizeof(Student), count, file);
fclose(file);
}
}
void emptyDisk()
{
FILE *file = fopen("disk.dat", "rw");
fopen("disk.dat", "wb");
}
I can see that the size of my disk.dat file changes when I write to it, and it goes to zero when I call empty, but loading does not work at all. If the student array in my program is zero, it just stays at zero when I call load and try to display it, but if there is something in the array, I get a segmentation fault when I call load and then try to display it.
I may be doing this entirely wrong. I'm really not sure what I'm doing. I was thinking that I could just write the whole array to a file, but I'm not sure that's true. I was just wondering if someone could look at this and let me know where I'm going wrong.
EDIT
I've edited my code to look like this:
void writeDisk()
{
int i = 0;
FILE *file = fopen("disk.dat", "ab");
for(i; i <count; i++)
{
fwrite(Data[i], sizeof(Student), 1, file);
}
fclose(file);
}
void loadDisk()
{
char buffer[300];
int i = 0;
clearData();
FILE *file = fopen("disk.dat", "rb");
while(Data[i] != NULL)
{
Data[i] = malloc(sizeof(Student));
fread(Data[i], sizeof(Student), 1, file);
i++;
}
fclose(file);
}
This still doesn't work though. The write seems to work better,but I don't see it writing the age of the student over to the file. The clearData() function in the load file just clears anything that's in the Data array to begin with, so that we can have a clean slate to read the file into.
I believe instead of
Student *Data[30];
You want
Student Data[30];
Because the first one is an array of pointers, while the second one is an array of the struct you want.
When you write
fread(&Data, sizeof(Student), count, file);
It reads the data from the file right into the location of Data. It looks like you want to read and write the actual structs, so you have to put them directly into the array, as opposed to using pointers.
I think this is the culprit:
Student *Data[30];
That's an array of pointers to Student structures. There is no storage allocated for actual Students.
Remove the *, throughout the rest of the code you seem to properly use Data as if it was a plain array, so it should need no modification.
EDIT on an unrelated note, you can declare a structure and its alias on the same statement, like this:
typedef struct {
...
} Student;
If Data is indeed an array of pointers to your structures, then what you are saving is just the pointers and not your actual data. In fact, you should never save actual pointers as the next time you run, malloc may return different pointers for storing your data.
What you want to do, for saving, is to iterate over your array and write the actual structure data to the file, something like:
for (i = 0; i < numberOfStudents; i++) {
fwrite(Data[i], sizeof (Student), 1, file);
}
For restoring, you'll need to loop over the students, malloc storage and read in the structure, and then set the Data pointer, something like:
for (i = 0; i < numberOfStudents; i++) {
Student *p = malloc(sizeof (Student));
fread(p, sizeof (Student), 1, file);
Data[i] = p;
}
Also, in general, you should check the return values from fopen, fread, fwrite, and fclose to detect errors.
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.