I've been trying to write a binary file with some information in a program but I can't make it work. I write it and try to read it to see if it worked. This is the struct I'm trying to write inside the file:
typedef struct{
int puntuacio;
int posicio_x;
int posicio_y;
int vides;
int direccio;
}Jugador;
I've got a variable called player of type Jugador. In the function I work with the binary file I recieve player as a pointer (so Jugador *player). This is the code I've written (I only give the relevant parts):
f=fopen("whatever.bin","wb+");
fwrite(nom,sizeof(char),strlen(nom),f); //nom is a string containing the player's name
fwrite(&player,sizeof(Jugador*),1,f);
auxint=player->direccio; //just doing this to see if I pass the info correctly
fwrite(&auxint,sizeof(int),1,f);
//auxp, auxjug and auxint are auxiliar variables I declared inside the function
fseek(f,0,SEEK_SET); //go to the start of the file before reading
fread(auxp,sizeof(char),20,f);
fread(&auxjug,sizeof(Jugador),1,f);
fread(&auxint,sizeof(int),1,f);
printf("auxp:%s--\n",auxp);
printf("puntuacio:%d--\n",auxjug.puntuacio);
printf("dir:%d--\n",auxjug.direccio);
printf("posx:%d--\n",auxjug.posicio_x);
printf("posy:%d--\n",auxjug.posicio_y);
printf("vids:%d--\n",auxjug.vides);
printf("auxint:%d--",auxint);
auxp prints the name correctly but I get an additional garbage character in the last position of the string, but that's easy to solve. auxint prints perfect. But I get what I guess are memory adresses when I print the parameters of auxjug.
fwrite(&player,sizeof(Jugador*),1,f);
is only writing a pointer size element (4 or 8 bytes) to the file. You need to have:
fwrite(player,sizeof(Jugador),1,f);
without the extra & and the extra *.
Another problem is that you only output strlen(nom) bytes to the file. But when you read the file, you read exactly 20 bytes. So you should probably pad your nom string to 20 bytes and fwrite exactly 20 bytes to the file:
fwrite(nom,sizeof(char),20,f);
...
fread(auxp,sizeof(char),20,f);
Related
Here is what I want to do:
Read my 2 text files into an "array of structs" (this is how it is worded on my assignment).
Dynamically create sufficient memory for each entry read from the file
Here is one of the structs I am working with:
typedef struct {
int eventid;
char eventdate[20];
char venuename[20];
char country[20];
int rockid;
} Venue;
Within my main function I have the array setup to receive the text as:
Venue *(places[20]);
Now comes the more complex part. I need to open the file for reading (I got this to work perfectly) and then dynamically allocate the memory for each entry. I know I need to use malloc to do this but I have never used it before and am sort of at a loss. Here is what I have so far:
void load_data(void)
{
char buffer[20]; //stating that each line can't be longer than 20 chars
int i = 0,len; //declaring 2 int variables
FILE * venuePtr=fopen("venueinfo.txt", "r");
if (venuePtr != NULL)
printf("\n**Venue info file has been opened!**\n");
else{
printf("\nPlease create a file named venueinfo.txt and restart!\n");
} //so far so good...
while (!feof(venuePtr)){ //while we have not found the eof key...
fscanf(venuePtr,"%s",buffer); //we scan each line of text
len = strlen(buffer); //find the length (len) of the string
places[i]=(char*)malloc(len+1); //allocate memory space for the word here
strcpy(places[i],buffer); //copy a word into our array
++i; //finally we move on to the next element in the array
} //end while
The problem resides in the while loop and I have been working this for 2 days straight. I have 5 members in my struct and am thinking that strcpy may not work. This is only part of the problem though I am sure. I just can't wrap my head around reading in everything. The file itself is a super simple txt file and looks like this:
1 Jan10 Citadel Belgium 8
4 May05 Sunrise Belize 6
3 Jun17 Footloose Brazil 4
you are trying to copy string into the venue structure, how do you expect that to work ?
strcpy(venue[i],buffer);
please give an example of your file, you probably need to parse each element and write it to structure members
Stuff you can skip but should eventually do:
use function(void), not just empty() for functions. visual studio, which you are using allows it, but it's bad format.
declaring global variables is likewise bad form. You want to declare them in main and pass them in.
finally, you want to return. return 0 from a successful run of main. If you have a void function, still, return; before the final closing '}' character of the function.
oh and fscan_s isn't portable, it's a Microsoft function.
stuff you actually asked about:
Now, onto your problem. Don't allocate memory and assign it. You have statically allocated memory for your structures, and for your strings, by making them arrays with a given number of characters. If you want to statically allocate memory, you need to use a pointer.
if you scan the first number, the rock id into id, you would assign the first venue's rock id as,
venue[0].rockid = id;
for arrays, you do have to string copy. You already allocated memory for them, so you just have to use strcpy.
but you can't just copy strings into the structure and have it all end up in the right place. You need to get each part and add it seperately
That means you either need to read in each element separately, like "%d%s" to read an int then a string, or whatever, or you need to split up your string after reading in the whole thing. NOTE
That %s won't read the whole line!!! It will stop at the first white-space character (new line, tab, even a space) so if you %s "hi there" you get "hi". You may want to use %[^\n] which while characters don't match \n.
My suggestion is to use fscanf with multiple items, but if you need to split the string up, you want to use sscanf, which lets you scan a string again.
Finally, you don't need to test for feof, and that's actually problematic. It's far better to use while (fscanf(parameters go in here) > 0) since EOF is generally -1 and 0 means no items were scanned. Either way, you are done reading.
I suggest you start small. It looks like you are trying to jump ahead without understanding fundamentals, and C is kinda brutal about needing to know those.
Good luck.
P.S. It's very possible I made a small mistake here just now, writing this, because I'm not a C expert, but I'm sure somebody will come along and help me. Finding mistakes is how we learn, so don't get discouraged. :)
I have, as usual, been reading quite a few posts on here. I found a particular useful posts on bus errors in general, see here. My problem is that I cannot understand why my particular code is giving me an error.
My code is an attempt to teach myself C. It's a modification of a game I made when I learned Java. The goal in my game is to take a huge 5049 x 1 text file of words. Randomly pick a word, jumble it and try to guess it. I know how to do all of that. So anyway, each line of the text file contains a word like:
5049
must
lean
better
program
now
...
So, I created an string array in C, tried to read this string array and put it into C. I didn't do anything else. Once I get the file into C, the rest should be easy. Weirder yet is that it complies. My problem comes when I run it with ./blah command.
The error I get is simple. It says:
zsh: bus error ./blah
My code is below. I suspect it might have to do with memory or overflowing the buffer, but that's completely unscientific and a gut feeling. So my question is simple, why is this C code giving me this bus error msg?
#include<stdio.h>
#include<stdlib.h>
//Preprocessed Functions
void jumblegame();
void readFile(char* [], int);
int main(int argc, char* argv[])
{
jumblegame();
}
void jumblegame()
{
//Load File
int x = 5049; //Rows
int y = 256; //Colums
char* words[x];
readFile(words,x);
//Define score variables
int totalScore = 0;
int currentScore = 0;
//Repeatedly pick a random work, randomly jumble it, and let the user guess what it is
}
void readFile(char* array[5049], int x)
{
char line[256]; //This is to to grab each string in the file and put it in a line.
FILE *file;
file = fopen("words.txt","r");
//Check to make sure file can open
if(file == NULL)
{
printf("Error: File does not open.");
exit(1);
}
//Otherwise, read file into array
else
{
while(!feof(file))//The file will loop until end of file
{
if((fgets(line,256,file))!= NULL)//If the line isn't empty
{
array[x] = fgets(line,256,file);//store string in line x of array
x++; //Increment to the next line
}
}
}
}
This line has a few problems:
array[x] = fgets(line,256,file);//store string in line x of array
You've already read the line in the condition of the immediately preceding if statement: the current line that you want to operate on is already in the buffer and now you use fgets to get the next line.
You're trying to assign to the same array slot each time: instead you'll want to keep a separate variable for the array index that increments each time through the loop.
Finally, you're trying to copy the strings using =. This will only copy references, it won't make a new copy of the string. So each element of the array will point to the same buffer: line, which will go out of scope and become invalid when your function exits. To populate your array with the strings, you need to make a copy of each one for the array: allocate space for each new string using malloc, then use strncpy to copy each line into your new string. Alternately, if you can use strdup, it will take care of allocating the space for you.
But I suspect that this is the cause of your bus error: you're passing in the array size as x, and in your loop, you're assigning to array[x]. The problem with this is that array[x] doesn't belong to the array, the array only has useable indices of 0 to (x - 1).
You are passing the value 5049 for x. The first time that the line
array[x] = ...
executes, it's accessing an array location that does not exist.
It looks like you are learning C. Great! A skill you need to master early is basic debugger use. In this case, if you compile your program with
gcc -g myprogram.c -o myprogram
and then run it with
gdb ./myprogram
(I am assuming Linux), you will get a stack dump that shows the line where bus error occurred. This should be enough to help you figure out the error yourself, which in the long run is much better than asking others.
There are many other ways a debugger is useful, but this is high on the list. It gives you a window into your running program.
You are storing the lines in the line buffer, which is defined inside the readFile function, and storing pointers to it in the arary. There are two problems with that: you are overwriting the value everytime a new string is read and the buffer is in the stack, and is invalid once the function returns.
You have at least a few problems:
array[x] = fgets(line,256,file)
This stores the address of line into each array element. line in no longer valid when readFile() returns, so you'll have an array of of useless pointers. Even if line had a longer lifetime, it wouldn't be useful to have all your array elements having the same pointer (they'd each just point to whatever happened to be written in the buffer last)
while(!feof(file))
This is an antipattern for reading a file. See http://c-faq.com/stdio/feof.html and "Using feof() incorrectly". This antipattern is likely responsible for your program looping more than you might expect when reading the file.
you allocate the array to hold 5049 pointers, but you simply read however much is in the file - there's no checking for whether or not you read the expected number or to prevent reading too many. You should think about allocating the array dynamically as you read the file or have a mechanism to ensure you read the right amount of data (not too little and not too much) and handle the error when it's not right.
I suspect the problem is with (fgets(line,256,file))!=NULL). A better way to read a file is with fread() (see http://www.cplusplus.com/reference/clibrary/cstdio/fread/). Specify the FILE* (a file stream in C), the size of the buffer, and the buffer. The routine returns the number of bytes read. If the return value is zero, then the EOF has been reached.
char buff [256];
fread (file, sizeof(char), 256, buff);
Hi I have to write the contents of a structure variable into a file. I have a working program but the output looks distorted, you can understand when you look at the output. The simple version of the code is as below and the outputs follows.
Code:
#include<stdio.h>
#include <stdlib.h>
struct mystruct
{
char xx[20];
char yy[20];
int zz;
};
void filewrite(struct mystruct *myvar)
{
FILE *f;
f = fopen("trace.bin","wb");
if (f == NULL)
{
printf("\nUnable to create the file");
exit(0);
}
fwrite(myvar, sizeof(struct mystruct), 1, f);
fclose(f);
}
void main()
{
struct mystruct myvar = {"Rambo 1", "Rambo 2", 1234};
filewrite(&myvar);
}
Output:
(1. Where is the integer '1234'? I need that intact.)
(2. Why does some random character appear here?)
trace.bin
Rambo 1Rambo 2Ò
Your program is correct and the output too...
Your are writing a binary file containing the raw data from memory.
The integer zz gets written to disk as 4 bytes (or 2 depending on the size of an int on your system), with the least significant byte first (Intel machine I guess).
1234 (decimal) gets written as 0xD2, 0x04, 0x00, 0x00 to disk.
0xD2 is a Ò when you look at in text form. The 0x04 and the 0x's are non-printable characters so they don't show.
First, in general it's not a good practice to copy non-packed struct-types to files since the compiler can add padding to the struct in order to align it in memory. Thus you will end up with either a non-portable implementation, or some garbled output where someone else tries to read your file, and the bits/bytes are not placed at the correct offset because of the compiler's padding bytes.
Second, I'm not sure how you are reading your file back (it appears you just copied it into a buffer and tried to print that), but the last set of bytes is an int type at the end ... it's not going to be a null-terminated string, so the manner in which it prints will not look "correct" ... printing non-null-terminated strings as strings can also lead to buffer overflows resulting in segmentation faults, etc.
In order to read back the contents of the file in a human-readable format, you would need to open the file and read contents back into the correct data-structures/types, and then appropriately call printf or some other means of converting the binary data to ASCII data for a print-out.
I don't recommend dumping memory directly into file, you should use some serialization method (e.x if you have pointer in the struct, you are doomed). I recommend Google Buffers Protocol if data will be shared between multiple applications.
Having few issues with my copy program which creates a copy of a file user enteres. I decided not to use (size_t) structure instead just assigned (int) and (char) types variables so I know exact value of bytes to read() out. ie I know start at beggining of file and read 4 bytes(int) to get value of lenght of filename, which I use as size in next read()
So, when I am writing (copying file exactly with same name) users inputted file to the output file (copied file) I writing it in long string, without spaces obviously just to make it readable here,
filenamesize filename filecontentsize filecontent
ie 10 myfile.txt 5 hello
So when come to reading that data out I start at begining of file using lseek() and I know the first 4 bytes are (int) which is lenght of filename so I put that into value int namelen using the read function.
My problem is I want to use that value read for the filenamesize(first 4 bytes) to declare my array to store filename with the right lenght. How do I put this array into read() so the read stores value inside that char array specified, see below please
int namelen; //value read from first 4 bytes of file lenght of filename to go in nxt read()
char filename[namelen];
read(fd, filename[namelen], namelen);//filename should have 'myfile.txt' if user entered that filename
So my question is once I read that first 4 bytes from file giving me lenght of filename stored in namelen, I then want to read namelen amount of bytes to give me the filename of originally file so I can create copied file inside directory?
Thanks
int namelen; //value read from first 4 bytes of file lenght of filename to go in nxt read()
char* filename = new char[namelen+1];
read(fd, filename, namelen);
filename[namelen]=0; // Just to keep readed buffer c-string compatible
do something with filename
delete[] filename;
In your previous question here, you did not upvote a single answer or accept any of them. You do appear to have used those answers though.
People who answered that earlier question might be inclined to help you here if you could be bothered to show a little gratitude for their earlier help by upvoting their answers and accepting the one that you found most helpful.
The user should input some file names in the command line and the program will read each file name from argv[] array. I have to perform error checking etc.
I want to read each filename. For example, if argv[2] is 'myfile.txt', the program should read the content of 'myfile.txt' and store value in char buffer[BUFSIZ] and then write the content of buffer into another file.
However before the content is written, the program should also write the name of the file and the size. Such that the file can be easily extracted later. A bit like the tar function.
The file I write the content of buffer, depending on the number of files added by user, should be a string like:
myfile.txt256Thisisfilecontentmyfile2.txt156Thisisfile2content..............
My question is
1) How do I write value of argv[2] into file using write() statement, as having problems writing char array, what should I put as (sizeof(?)) inside write(). see below as I don't know the length of the file name entered by the user.
2) Do I use the '&' to write an integer value into file after name, for example write 4 bytes after file name for the size of file
Here is the code I have written,
char buffer[BUFSIZ];
int numfiles=5; //say this is no of files user entered at command
open(file.....
lseek(fdout, 0, SEEK_SET); //start begging of file and move along each file some for loop
for(i=0-; ......
//for each file write filename,filesize,data....filename,filesize,data......
int bytesread=read(argv[i],buffer,sizeof(buffer));
write(outputfile, argv[i], sizeof(argv)); //write filename size of enough to store value of filename
write(outputfile, &bytesread, sizeof(bytesread));
write(outputfile, buffer, sizeof(buffer));
But the code is not working as I expected.
Any suggestions?
Since argv consists of null-terminated arrays, the length you can write is strlen(argv[2])+1 to write both the argument and null terminator:
size_t sz = strlen (argv[2]);
write (fd, argv[2], sz + 1);
Alternatively, if you want the length followed by the characters, you can write the size_t itself returned from strlen followed by that many characters.
size_t sz = strlen (argv[2]);
write (fd, &sz, sizeof (size_t));
write (fd, argv[2], sz);
You probably also need to write the length of the file as well so that you can locate the next file when reading it back.
1., You can write the string the following way:
size_t size = strlen(string);
write(fd, string, size);
However, most of the time it's not this simple: you will need the size of the string so you'll know how much you need to read. So you should write the string size too.
2., An integer can be written the following way:
write(fd, &integer, sizeof(integer));
This is simple, but if you plan to use the file on different architectures, you'll need to deal with endianness too.
It sounds like your best bet is to use a binary format. In your example, is the file called myfile.txt with a content length of 256, or myfile.txt2 with a content length of 56, or myfile.txt25 with a content length of 6? There's no way to distinguish between the end of the filename and the start of the content length field. Similarly there is no way to distinguish between the end of the content length and the start of the content. If you must use a text format, fixed width fields will help with this. I.e. 32 characters of filename followed by 6 digits of content length. But binary format is more efficient.
You get the filename length using strlen(), don't use sizeof(argv) as you will get completely the wrong result. sizeof(argv[i]) will also give the wrong result.
So write 4 bytes of filename length followed by the filename then 4 bytes of content length followed by the content.
If you want the format to be portable you need to be aware of byte order issues.
Lastly, if the file won't all fit in your buffer then you are stuffed. You need to get the size of the file you are reading to write it to your output file first, and then make sure you read that number of bytes from the first file into the second file. There are various techniques to do this.
thanks for replies guys,
I decided not to use (size_t) structure instead just assigned (int) and (char) types so I know exact value of bytes to read() out. ie I know start at beggining of file and read 4 bytes(int) to get value of lenght of filename, which I use as size in next read()
So, when I am writing (copying file exactly with same name) users inputted file to the output file (copied file) I writing it in long string, without spaces obviously just to make it readable here,
filenamesize filename filecontentsize filecontent
ie 10 myfile.txt 5 hello
So when come to reading that data out I start at begining of file using lseek() and I know the first 4 bytes are (int) which is lenght of filename so I put that into value int namelen using the read function.
My problem is I want to use that value read for the filenamesize(first 4 bytes) to declare my array to store filename with the right lenght. How do I put this array into read() so the read stores value inside that char array specified, see below please
int namelen; //value read from first 4 bytes of file lenght of filename to go in nxt read()
char filename[namelen];
read(fd, filename[namelen], namelen);//filename should have 'myfile.txt' if user entered that filename
So my question is once I read that first 4 bytes from file giving me lenght of filename stored in namelen, I then want to read namelen amount of bytes to give me the filename of originally file so I can create copied file inside directory?
Thanks