In C I am reading binary data from a file into a var data like this:
unsigned char *data;
data = malloc(size);
int read_size = fread(data, 1, size, fp);
I want to prepend the var data with <filename><size> of the file. How can I achieve this?
It's not a legal C string because it's binary data with null bytes potentially all over the place.
I know to make sure I allocate it with enough memory, I just can't figure out how to actually prepend it.
Allocate enough memory to data.
Copy the prefix into it.
Get a reference to just behind what had been copied in 2..
Pass this reference to fread().
Define your own data format for storage:
<uint64_t datalength><string name><char[datalength] contents>
Or for easier in-app use:
struct named_file {
char* contents;
uint64_t datasize;
char name[]; // contents begin directly after the name.
}
Allocate the struct with enough space: sizeof(named_file)+strlen(_name)+1+_datasize
strcpy(name, _name)
contents = name+strlen(name)+1
save data to contents-pointer. memcpy(), direct reading, whatever.
Related
I would like to ask how to add one char to a buffer. For example:
char buffer[50];
char one_symbol;
How to add one_symbol to buffer? I don't know how long the buffer is at the time, so I cant just write, for example buffer[5] = one_symbol;
Thanks.
You need to do something to keep track of the length of the data in the buffer.
You have a couple of choices about how to do that. Strings store data in the buffer (a NUL byte) to signal where there data ends. Another possibility is to store the length externally:
typedef struct {
char data[50];
size_t len;
} buffer;
This latter is particularly preferable when/if you want to allow for data that itself might include NUL bytes. If you don't want your buffer size fixed at 50, you can go a step further:
typedef struct {
size_t allocated;
size_t in_use;
char data[];
};
Note that this uses a flexible array member, which was added in C99, so some older compilers don't support it.
Keep track or the buffers current size. You can do it by adding a new variable for that.
Something like:
char buffer[50];
size_t current_size = 0; /* Buffer is of size zero from the size */
/* ... */
/* Add one character to the buffer */
buffer[current_size++] = 'a';
I have a program that is reading a file, but not saving into the structure. Once the data is read, it should be saved within the structure in order for the program to be able to use said data later. I'm having a heck of a time figuring out how to get this done.
structure
typedef struct friends_contact {
char *First_Name;
char *Last_Name;
char *home_phone;
char *cell_phone;
} fr;
Reading of the file
void ReadFile(fr *friends, int *counter, char buffer[], FILE *read) {
fseek(read, 0, SEEK_SET);
while (fscanf(read, "%s", buffer) != EOF) {
friends[*counter].First_Name = malloc(BUFFSIZE * strlen(buffer));
strcpy(friends[*counter].First_Name, buffer);
}
}
More information can be provided as needed. I just want to figure out why the information isn't saving within the structure so that it can be called on later.
What is "friends"? global variable?
What is "contacts"? It is not used in function.
May be you mix them?
BUFFSIZE * strlen(buffer) -> What do you mean? you allocate strlen(buffer) BUFFSIZE times.
Possibly it should be sizeof(char) * strlen(buffer) ?
I also think you should check the length of "buffer" after operation fscanf.
The code you use for allocating space for the char array and then copying to it makes sense but one of two things could be happening in your while() cycle: the condition is evaluated instantly to false so nothing is copied or you iterate over and over again until fscanf writes an empty string to buffer and this overwrites the content of friends[*counter].First_Name, should you increment *counter in the body of while()?
So, for some reason, I need to make a external file (.DAT) to store data by appending the new one to the end of old data.
#include <stdio.h>
#include <stdlib.h>
int main () {
typedef struct {
char *Name;
int Index;
} DataFile;
static FILE *file;
size_t result;
DataFile *DataTable;
file = fopen("database.DAT","ab");
DataTable = (DataFile *) malloc (sizeof(DataFile));
DataTable[0].Name = "somefile.txt";
DataTable[0].Index = 7;
printf("%s %d \n",DataTable[0].Name,DataTable[0].Index);
result = fwrite(DataTable,sizeof(DataFile),1,file);
fclose(file);
free(DataTable);
return 0;
}
After running code above, I then check if the data stored correctly. So, I make this code below.
#include <stdio.h>
#include <stdlib.h>
int main () {
typedef struct {
char *Name;
int Index;
} DataFile;
static FILE *file;
size_t result;
long size;
int i;
DataFile *DataTable;
file = fopen("database.DAT","rb");
if (file == NULL) printf("Error1");
// Determine the size of file
fseek(file,0,SEEK_END);
size = ftell(file);
rewind(file);
DataTable = (DataFile *) malloc ((size/sizeof(DataFile)) * sizeof(DataFile));
if (DataTable == NULL) printf("Error2");
result = fread(DataTable,sizeof(DataFile),size/sizeof(DataFile),file);
fclose(file);
for (i=0; i<result; i++) {
printf("%s %d \n",DataTable[i].Name,DataTable[i].Index);
}
free(DataTable);
return 0;
}
However, it gives output
somefile.txt 7
from the first code block and
Error1 7
from the second code block.
I notice that the problem is not because the failure either when opening .DAT file or when allocating memory for DataTable. Also, it works for int type (Index) but not for char* type (Name) when reading from .DAT file. I have no idea what to do to solve this char*-type-reading problem (and where 'error1' comes from). (not even google gives me answer.)
Your structure DataFile stores one pointer and one integer. When you write it to the file, you write some program specific pointer to a string, and an integer.
When reading from it, you just refill your structure with the pointer and the integer, wich means that DataFile.Name will be a pointer to a probably-not-initialized memory segment. But since you created your file pointing to the first hard-coded string ("filename.txt"), some undefined but understandable behaviour happens, and your pointer in this case points to the first hard-coded string you wrote in you second program (which in your case is Error1)
What you really want to do is write the real string in your file.
A simple solution, if you want to the keep the hole writing structure thing is to create an array instead of a pointer
typedef struct {
char Name[512];
int Index;
} DataFile;
then initialize your data with
strncpy(DataTable[0].Name, "somefile.txt", sizeof(DataTable[0].Name) - 1); // just to make sure you dont overflow your array size
DataTable[0].Name[sizeof(DataTable[0].Name) - 1] = '\0';
and retreview your data the way you did.
A char* is only a pointer, i.e. the address of the character array containing your strings. You don't write the strings themselves to the file. After reading the file, as the same strings aren't in your memory at the same addresses any more, the application will fail.
You'll have to come up with a way to save the strings themselves to file as well. Probably by first writing their length, and then writing their content. Upon reading, you can use the length information to allocate memory dynamically, then read into that memory.
In your writing code you haven't allocated storage for char *Name. When you perform the DataTable[0].Name = "somefile.txt" instruction you're not actually copying the "somefile.txt" into memory pointed by Name, it's actually assigning a Name a value pointing to a constant characters string (moreover, it will become dangling pointer since the string is an rvalue, i.e. doesn't have a memory to be addressed via). Same goes for your file reading code.
You need to:
Allocate storage for your Name.
Copy the string using memcpy or similar into the allocated storage.
I'm trying to save a struct with a char* string into a file.
struct d_object {
int flags;
int time;
int offset;
char *filename;
};
The problem is that when doing that I will obviously only save the address of that pointer rather than the string. So what I've done is simply use a character array and but I'm forced to set the maximum size of the string. This works fine, however I was wondering if there is anyway of storing the struct with a char* (that I malloc at some point) in a file and then retrieve it. I can save the string and the struct separate and then retrieve them but it's quite a mess. It would be preferable if I could load and save the entire struct (the one above) into the file. Thanks!
The code with the char array is below:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
struct d_object {
int flags;
int time;
int offset;
char filename[255];
};
int main(int argc, char **argv) {
struct d_object fcb;
fcb.flags=5;
fcb.time=100000;
fcb.offset=220;
strncpy(fcb.filename,"myfile",255);
int fd=open("testfile",O_RDWR);
write(fd,&fcb,sizeof(fcb));
close(fd);
int fd2 = open("testfile",O_RDONLY);
struct d_object new_fcb;
read(fd2,&new_fcb,sizeof(new_fcb));
printf("read from file testfile: %s\n",new_fcb.filename);
return 0;
}
P.S.: I'm not using the STREAM functions simply because this is actually meant to be run on an embedded OS that doesn't have them. I've just adapted the code for *BSD/Linux so it makes more sense when asking the question.
I understand that portability is not an issue, since you are working for an embedded system. In other case, you should use something like XML.
You can transform back your code to:
struct d_object {
int flags;
int time;
int offset;
char * filename;
};
And then save each piece of data individually:
write( fd, &record.flags, sizeof( int ) );
write( fd, &record.time, sizeof( int ) );
write( fd, &record.offset, sizeof( int ) );
int filename_length = strlen( filename );
write( fd, &filename_length, sizeof( int ) );
write( fd, record.filename, filename_length );
For reading, you'll have to read each item separatedly, and then the filename:
int filename_length;
read( fd, &emptyRecord.flags, sizeof( int ) );
read( fd, &emptyRecord.time, sizeof( int ) );
read( fd, &emptyRecord.offset, sizeof( int ) );
read( filename_length, sizeof( int ), 1, file );
emptyRecord.filename = (char *) malloc( sizeof( char ) * ( filename_length +1) );
read( fd, emptyRecord.filename, filename_length );
*( emptyRecord.filename + filename_length ) = 0;
Serialization is never pretty. How about storing the length of the string in the pointer, and letting the string follow the struct in the file? Something like this (warning, brain-compiled code):
void write_object(struct d_object *s, int fd) {
struct d_object copy = *s;
copy.filename = (char*)strlen(s->filename);
write(fd, ©, sizeof(copy));
write(fd, s->filename, (size_t)copy.filename);
}
void read_object(struct d_object *s, int fd) {
read(fd, s, sizeof(struct d_object));
char *filename = malloc(((size_t)s->filename) + 1);
read(fd, filename, (size_t)s->filename);
filename[(size_t)s->filename] = '\0';
s->filename = filename;
}
Now that I know the nature of your problem, why not try flexible arrays? Instead of using char *filename;, use char filename[1] and malloc(sizeof struct d_object + filename_len) to allocate your structs. Add a size member, and you can easily write the object to disk with a single call to write and load it from disk with 2 calls (first to read the size element, second to read the whole object after allocating it).
Note that the "official" way to do flexible arrays in C99 is [] rather than [1], but [1] is guaranteed to work as a consequence of other requirements in the standard and works on C89 too. [1] will waste a few bytes though, so if your compiler supports [] you might want to use it.
Your problem here is really a symptom of a much larger issue: you shouldn't be reading/writing binary data structures between memory and files. Not only is there no clear way to read/write structures with pointers to other data (this applies not only to strings but to nested data, linked lists, etc.) but the format of the data on disk will depend on your host machine and C implementation and will not be portable to other environments.
Instead, you should design a format for your data on disk and write functions to save and load the data, or use an existing format and find library code for using it. This is usually referred to as "serialization".
If your data is hierarchical, JSON, XML, or EBML may be appropriate. If it's fairly simple, flat text files or a homebrew binary format (written byte-by-byte so it's portable) may be appropriate.
Since you seem to be unfamiliar with these issues, it might be worthwhile to write some code for loading/saving a few simple binary file formats (like .tga or .wav) as an exercise before you try to design something for your own data.
Nope, there's no magic built directly into the language to do this for you.
What you need to do is to write a couple of functions to convert your data into writable form, and to read it back in from file. This is called "serialization" / "deserialization" in languages that make more of a fuss about it.
Your particular structure, you could do something like write the binary stuff to the file straight from the struct, and then follow it up with the contents of the character buffer. You could make things easier for yourself come read time if you precede the character data with an int specifying its length.
When you read that stuff back in, you'll want to malloc/calloc yourself a chunk of memory to hold the char data in; if you stored the size you'll know just how big to make that malloc. Read the binary data into the struct, read the char data into the malloc'd memory, store the pointer to the malloc chunk into the struct, and you've re-created the original.
No magic. Just data and a bit of elbow grease.
EDIT
While I wrote about doing this, Thomas coded an example. I think our answers complement each other very well; together they should tell you everything you need to know.
I have a binary file which contains records. The structure of the file is as such:
Structure (see below)
Name String
Address String
The structure in question:
typedef struct{
char * name;
char * address;
short addressLength, nameLength;
int phoneNumber;
}employeeRecord;
employeeRecord record;
I get the name as such:
char name[50];
printf("\nName:");
fgets(name,50,stdin);
record.nameLength = strlen(name)-1;
record.name = malloc(sizeof(char)*record.nameLength);
strcpy(record.name,name);
I write the structure, the the name, then the address (as mentioned above).
fwrite(&record.name,sizeof(char),record.nameLength,fp);
where fp is a file pointer.
Now i close the file.
However, if i then want to read from the file to get this data back, I believe I need to read in the structure, read the nameLength variable, malloc enough memory for the name to sit in, then fread the name into the variable.
Like so:
char *nameString = malloc(sizeof(char)*record.nameLength);
fread(nameString,sizeof(char),record.nameLength,fp);
printf("\nName: %s",nameString);
However, when i attempt this, i do not get valid data.
Example:
Input name is: Joseph (6 characters)
Output data:
Name length is 6 (correct),
Name is �A � (aka garbage)
So obviously im doing something wrong. Could someone give me some help?
I see two problems with the write, you are setting record.nameLength to be too small, and you are passing the wrong pointer to fwrite for the name. record.name is already a pointer.
Change this
record.nameLength = strlen(name)-1;
...
fwrite(&record.name,sizeof(char),record.nameLength,fp);
to this
record.nameLength = strlen(name);
...
fwrite(record.name,sizeof(char),record.nameLength,fp);
You also have a problem on the read, since you aren't writing the terminating \0 from the strings into your file, when you read back, you need to add that terminator explicitly.
char *nameString = malloc(sizeof(char)* (record.nameLength + 1));
fread(nameString,sizeof(char),record.nameLength,fp);
nameString[record.NameLength] = '\0';
The problem is that you pass the pointer to the char* in your fwrite:
fwrite(&record.name,sizeof(char),record.nameLength,fp);
This means that instead of writing the name, you're writing the memory address of the name. Fwrite expects a pointer to the data to write—in your case, that's the pointer to the char data, not the pointer to the pointer of the char data.
Pass it record.name instead of &record.name and you should be set:
fwrite(record.name, sizeof(char), record.nameLength, fp);