1

I want to write a struct object to a file using the write() function. It has to be that function.

My input from terminal is: ./main.c output.dat John Doe 45

When I run the program and open the output.dat there are bunch of letters that don't make sense. Please help me.

The output I want in my output.dat file is: John Doe 45

My code:

struct Person{
  char* name;
  char* lastName;
  char* age;
};

int main(int argc, char** argv){

    struct Person human;
    /* get the argument values and store them into char*         */
    char* fileName = argv[1];
    char* name = argv[2];
    char* lastName = argv[3];
    char* age = argv[4];

    /* set the values of human object */
    human.name = name;
    human.lastName = lastName;
    human.age = age;

    /* open the file */
    int file = 0;
    file = open(fileName, O_RDWR); /* I want to have read&write set! */
    write(file, &human, sizeof(human));
    close(file);


    return 0;
}
6
  • 1
    The struct only contains pointers, so you write the pointers to the file, not what they point to. Commented Mar 29, 2018 at 23:50
  • 1
    Google "serialisation" and have a little think about what human actually contains, byte-wise. Commented Mar 29, 2018 at 23:51
  • @Bo Persson if that is the case, how can I change the code, so I will be writing actual values? :) Commented Mar 29, 2018 at 23:52
  • 1
    Is that the POSIX function? Also which language are you using? C or C++? Commented Mar 29, 2018 at 23:53
  • 2
    You don't. One day someone will change the struct, the compiler, the compiler options, or the #pragmas or something else that determines struct memory layout and break all your existing serialized structs. Don't do this. Commented Mar 29, 2018 at 23:53

4 Answers 4

5

When you write the struct, you only write the values in the struct itself. In your case, those values are pointers to other places in memory, not the string data. Thus, you end up writing three pointers worth of memory addresses (12 or 24 bytes on most systems) that aren't all that useful (since they apply to the memory space of the currently running program, which won't be the same on the next run).

You're going to need to design a more useful serialization format that actually writes out the contents of the strings, not their addresses. Options would include simple newline or NUL separated text, binary length prefixed text, or with third party libraries to get it right, CSV, JSON, or XML (if you're feeling ambitious, a database of some sort).

For example, with binary length prefixed text, you might do something like:

uint32_t len;

len = strlen(name);
write(file, &len, sizeof(len));
write(file, human.name, len);
len = strlen(lastName);
write(file, &len, sizeof(len));
write(file, human.lastName, len);
... repeat for age ...

which allows you to read it back in by reading each string length (fixed size), then using it to figure out how many bytes must be read to get the string.

Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for the answer, however if I try to write each property separately it works fine :). For example if I write: write(file, human.name,strlen(human.name)). But I don't want that. I want to write it as a single object.
@MarkoMlakar When the single object contains sub-objects you can't avoid having to write those out.
@MarkoMlakar: If you don't insert some form of formatting (separator or length prefixed being simple suggestions), you'll never be able to figure out where each field in the output ends and the next begins. There is no such thing as writing "a single object", you need to define a serialization format of some sort. Sure, you could do some setup and use writev to write all the data out as a single system call, but that's no more "a single object" than any other solution.
@ShadowRanger What if I instead of setting the values as I am, I do: snprintf(human.name,sizeof(human.name),"%s",name); and same for lastname and age. ? will it work then?
@MarkoMlakar: human.name is a char*; if you do that, you'll either segfault (normal case if the pointer is uninitialized) or if you malloced/newed memory for the pointer first, write no more than 3-7 characters (because sizeof(char*) is a fixed size unrelated to what it points to). If it was a fixed size char array that would work, but then you're imposing an arbitrary limit on name lengths and (if you want to mitigate that problem) often writing out a lot of padding when the strings are shorter than the predefined array lengths.
|
1

You can't just write out the object. You need to write out each of the internal pointers individually.

Something like this:

file = open(fileName, O_RDWR); /* I want to have read&write set! */

write(file, human.name, std::strlen(human.name) + 1);
write(file, human.lastName, std::strlen(human.lastName) + 1);
write(file, human.age, std::strlen(human.age) + 1);

close(file);

Notice I add +1 to the length of the strings to make sure I also write out the terminating zero.

Comments

1

If you know the maximum length of each field, you can try making the fields an array. Remember to add 1 for null byte

struct Person{
  char name[32]; //31 char long + null 
  char lastName[32]; // 31 char long + null
  char age[4]; // 3 char long + null
};

Then your fwrite will work fine. But you need to strcpy the values in to the struct.

strlcpy(human.name, name, sizeof(human.name));

and so on for each field. strlcpy makes sure your string is null terminated.

Comments

0

Thanks to all, I sloved it like this. Although it is not ideal it gets the job done :) :

struct Person{
   char name[20];
   char lastName[20];
   char age[20];
};

int main(int argc, char** argv){

   struct Person human;
   /* get the argument values and store them into char*         */
   char* fileName = argv[1];
   char* name = argv[2];
   char* lastName = argv[3];
   char* age = argv[4];

   sprintf(human.name,name);
   sprintf(human.lastName,lastName);
   sprintf(human.age,age);

   /* open the file */
   int file = 0;
   file = open(fileName, O_RDWR); /* I want to have read&write set! */
   write(file, &human, sizeof(human));
   close(file);


   return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.