1

I am trying to write a struct to a .dat file and when I open it it shows me this:

"1^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@041720881^@^@^@^@^@^@^@^@^@^@^@Denko^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@Brenko^@^@^@^@^@^@^@^@^@^@^@^@^@^@13.07.2000^@^@^@^@^@^@^@^@^@^@ "

It adds random symbols between the actual values. And now when I at least try to read and print some values, it just doesn't work. It's like the buffer is empty. But I followed the instructions and guides I read.

Using fwrite or similar is not an option since I have to work with these specific functions write() and read().

My code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>

struct info{
    char id[20];
    char telefon[20];
    char ime[20];
    char priimek[20];
    char datum[20];
};
int main(int argc, char* argv[]) {
    struct info dude =
            {
                "01",
                "041720881",
                "Denko",
                "Brenko",
                "13.07.2000"
    };

    struct info dude2 =
            {
                "02",
                "041581734",
                "Denko",
                "Badenko",
                "13.07.1990"
            };

    if(strcmp(argv[1], "-c") == 0){

        int fd = open("xpo.dat", O_CREAT| O_APPEND | S_IRWXG, 0666);
        if(fd == -1){
            perror("Error while creating file");
            exit(EXIT_FAILURE);
        }
    }
    else if(strcmp(argv[1], "-o") == 0){
        struct stat sizefile;

        int fd = open("xpo.dat", O_RDWR);
        if(fd == -1){
            perror("Error while opening file");
            exit(EXIT_FAILURE);
        }       
        fstat(fd,&sizefile);
        int wr = write(fd, &dude,sizeof(struct info));

        char buf[101];
        int sz = read(fd, buf, 100);
        buf[sz] = '\0';
        if(sz == -1) {
            perror("Error while creating file");
            exit(EXIT_FAILURE);
        }
        printf("%s", buf);
        int cl = close(fd);
    }
    return 0;
}
anastaciu
  • 23,467
  • 7
  • 28
  • 53
Banka Don
  • 43
  • 4
  • 1
    Re "*It adds random symbols between the actual values*", No, nothing is added. You are printing `dude.id[0]` (`'0'`) followed by `dude.id[1]` (`'1'`) followed by `dude.id[2]` (`0`, shows as `^@`), followed by `dude.id[3]` (`0`, shows as `^@`), ... And that's exactly what you get. What did you want to print? – ikegami Apr 02 '21 at 19:53
  • What about the read() not working? I just wanted to print the whole struct that was written in file – Banka Don Apr 02 '21 at 19:56
  • You cannot use `write` and `read` one right after another. – n. m. could be an AI Apr 02 '21 at 20:00
  • So I would need to close the file after writing and then opening it again for reading? – Banka Don Apr 02 '21 at 20:04
  • `buf[sz] = '\0'; if(sz == -1) {` That check comes too late. If `sz == -1`, then trying to write to `buf[sz]` is undefined. Do the check first, and don't write if sz < 0. – William Pursell Apr 02 '21 at 20:11
  • regarding: `if(strcmp(argv[1], "-c") == 0){` Never access beyond `argv[0]` without first checking `argc` to assure the user actually entered the expected command line parameter. – user3629249 Apr 03 '21 at 16:39
  • "So I would need to close the file after writing and then opening it again for reading" You can also use `lseek` in between. My original statement was not entirely right, you can in general use `read` and `write` one after another, just not in this case, because the file position after a `write` is at the end of file, and `read` will return **zero** (which you should handle). – n. m. could be an AI Apr 04 '21 at 10:10

2 Answers2

5

The struct contains 100 chars of data. But you are setting only some of them. When you set ime as Denko, the first six chars are set as 'D', 'e', 'n', 'k', 'o','\0'. The remaining 14 are not initialized (or rather initialized implicitly, see @dave_thompson_085's comment below).

If you want to omit those chars, you cannot write the struct as one block. Either write each field separately, or concatenate the fields into a string and write it instead.

mkayaalp
  • 2,631
  • 10
  • 17
  • They _are_ intialized, though not explicitly -- if an aggregate (array or struct) has a partial initializer, remaining elements or fields are initialized to zero (of the appropriate type) (as statics are for no initializer); see https://stackoverflow.com/questions/18688971/c-char-array-initialization and https://stackoverflow.com/questions/37548287/partially-initializing-a-c-struct . OP might (or might not) want to avoid writing the _unneeded_ or _excess_ chars. – dave_thompson_085 Apr 02 '21 at 23:49
3

As stated in the comments and in the accepted answer, you have some issues, the why and the what is already talked about and explained.

I would like to add some more information:

And now when I at least try to read and print some values, it just doesn't work. It's like the buffer is empty.

What happens is that you are reading from the end of the file, if you want to read after you write without closing and reopening the file, you can, but you'll need to reposition the offset of the opened file to the beginning using lseek.

Another thing to note is that if you want to write the data as a structure you then need to read it as a structure as well, not as a string.

Taking that in consideration your code could look more or less like this (skipping return value validations, but you should do it, as you know):

//...
else if(strcmp(argv[1], "-o") == 0){

    int fd = open("xpo.dat", O_RDWR);  
    int wr = write(fd, &dude, sizeof dude);
    lseek(fd, 0, SEEK_SET); // set the reading position to the beginning of the file
    struct info buf;
    wr = read(fd, &buf, sizeof buf);
    int cl = close(fd);
    printf("%s %s %s %s %s", buf.id, buf.telefon, buf.ime, buf.priimek, buf.datum);
}
//...

If you prefer it as a string you can easily concatenate it using something like snprintf or similar.

anastaciu
  • 23,467
  • 7
  • 28
  • 53