views:

315

answers:

3

I've been working on this assignment, where I need to read in "records" and write them to a file, and then have the ability to read/find them later. On each run of the program, the user can decide to write a new record, or read an old record (either by Name or #)

The file is binary, here is its definition:

typedef struct{
        char * name;
        char * address;
        short addressLength, nameLength;
        int phoneNumber;
    }employeeRecord;
    employeeRecord record;

The way the program works, it will store the structure, then the name, then the address. Name and address are dynamically allocated, which is why it is necessary to read the structure first to find the size of the name and address, allocate memory for them, then read them into that memory.

For debugging purposes I have two programs at the moment. I have my file writing program, and file reading.

My actual problem is this, when I read a file I have written, i read in the structure, print out the phone # to make sure it works (which works fine), and then fread the name (now being able to use record.nameLength which reports the proper value too). Fread however, does not return a usable name, it returns blank.

I see two problems, either I haven't written the name to the file correctly, or I haven't read it in correctly. Here is how i write to the file: where fp is the file pointer. record.name is a proper value, so is record.nameLength. Also i am writing the name including the null terminator. (e.g. 'Jack\0')

fwrite(&record,sizeof record,1,fp);
fwrite(record.name,sizeof(char),record.nameLength,fp);
fwrite(record.address,sizeof(char),record.addressLength,fp);

And i then close the file. here is how i read the file:

fp = fopen("employeeRecord","r");


fread(&record,sizeof record,1,fp);
printf("Number: %d\n",record.phoneNumber);


char *nameString = malloc(sizeof(char)*record.nameLength);

printf("\nName Length: %d",record.nameLength);
fread(nameString,sizeof(char),record.nameLength,fp);
printf("\nName: %s",nameString);

Notice there is some debug stuff in there (name length and number, both of which are correct). So i know the file opened properly, and I can use the name length fine. Why then is my output blank, or a newline, or something like that? (The output is just Name: with nothing after it, and program finishes just fine)

Thanks for the help.

A: 

I would like to add my input....since you are dumping the memory structure to disk, the pointer addresses used to hold the data would most certainly be valid prior to dumping, but when reading from them, the pointer addresses could be invalid....which would explain why the character pointer is not showing the name...

tommieb75
True, but he reads from the file to `nameString` and prints `nameString`... It should work.
Ben
Assuming he posted actual code, that can't be the problem. In his posted code the print statement is not using the structure (i.e. his code is `printf("\nName: %s", nameString);` - it's not `printf("\nName: %s", record.nameString);`
R Samuel Klatchko
Yes, i posted actual code. I don't see how this could be the problem either. record.name is really only used for writing to the file, not in the reading, in the reading we're concerned only about the length of the name. I mean i could reuse record.name if I wanted to, but it shouldnt have any bearing on the program at this point.
Blackbinary
+1  A: 

I tried your code and it worked fine. In order, here is the output, a hexdump of the file, and your source made to compile.

Update: Updated code to read name and address from stdin or command-line arguments.

prompt$ g++ -g -Wall -o test_records test_records.cpp
prompt$ echo -e "Test User\nSomeplace, Somewhere" | ./test_records
sizeof(employeeRecord) = 24
Number: 5551212

Name Length: 9
Name: Test User

prompt$ hexdump -C employeeRecord 
00000000  90 f7 bf 5f ff 7f 00 00  70 f7 bf 5f ff 7f 00 00  |..._....p.._....|
00000010  14 00 09 00 6c b4 54 00  54 65 73 74 20 55 73 65  |....l.T.Test Use|
00000020  72 53 6f 6d 65 70 6c 61  63 65 2c 20 53 6f 6d 65  |rSomeplace, Some|
00000030  77 68 65 72 65                                    |where|
00000035

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

typedef struct{
        char * name;
        char * address;
        short addressLength, nameLength;
        int phoneNumber;
    }employeeRecord;

int main(int argc, char *argv[])
{
  employeeRecord record;

#if 0
  // Commmand line arguments
  if (argc < 3)
    return 1;

  record.nameLength = strlen(argv[1]);
  record.name = (char *)malloc(sizeof(char)*(record.nameLength + 1));
  strncpy(record.name, argv[1], record.nameLength + 1);

  record.addressLength = strlen(argv[2]);
  record.address = (char *)malloc(sizeof(char)*(record.addressLength + 1));
  strncpy(record.address, argv[2], record.addressLength + 1);
#else
  // stdin
  char input[1024];

  fgets(input, sizeof(input), stdin);
  record.nameLength = strlen(input);
  record.name = (char *)malloc(sizeof(char)*(record.nameLength + 1));
  strncpy(record.name, input, record.nameLength + 1);

  fgets(input, sizeof(input), stdin);
  record.addressLength = strlen(input);
  record.address = (char *)malloc(sizeof(char)*(record.addressLength + 1));
  strncpy(record.address, input, record.addressLength + 1);
#endif

  record.phoneNumber = 5551212;

  FILE *fp = NULL;

  printf("sizeof(employeeRecord) = %lu\n", sizeof(employeeRecord));

  // Write
  fp = fopen("employeeRecord","w");
  fwrite(&record,sizeof(employeeRecord),1,fp);
  // Note: we're not including terminating NULLs.
  fwrite(record.name,sizeof(char),record.nameLength,fp);
  fwrite(record.address,sizeof(char),record.addressLength,fp);
  fclose(fp);

  // Read
  fp = fopen("employeeRecord","r");
  fread(&record,sizeof(employeeRecord),1,fp);
  printf("Number: %d\n",record.phoneNumber);

  char *nameString = (char *)malloc(sizeof(char)*(record.nameLength + 1));
  printf("\nName Length: %d",record.nameLength);
  fread(nameString,sizeof(char),record.nameLength,fp);
  nameString[record.nameLength] = '\0';
  printf("\nName: %s",nameString);
  printf("\n");

  fclose(fp);

  return 0;
}
Harvey
The difference between your program and mine is I do not use a static name. I wrote a smaller writing program (just opens and uses static variables), and the reader part works. For example, using: `record.name = "Jack"` works in the reader program.I don't know what this means though, is there something wrong with fgets? Could you try your program getting the name from stdin?
Blackbinary
Also, i edited the program i mentioned in the above comment to use `fgets(record.name,sizeof(char)*record.nameLength,stdin);` and get a segfault? thats literally the only change i made :(
Blackbinary
@Blackbinary watch out, `fgets()` does NOT allocate memory. It takes as arguments a pointer to allocated memory and the maximal size it can write. It means here `record.nameLength` must be defined to the size of the memory allocated pointed by `record.name`.
Ben
Thanks Ben, i figured that out in debugging, i actually meant to just have 50 or a big number like that. using `fgets(record.name,50,stdin)` works. So i've got a working reader/writer. I'm going to try and apply it to my program, will let all know how it goes.
Blackbinary
Ok thanks a bunch for all the help guys, Harvey i've marked you as the accepted answer, it was actually a tonne of help. Especially because i didn't know about hexdump, which helped a lot. I had been taking in the name as a string, using strlen to find the actual length, then mallocing record.name to that size, and using strcpy to transfer the string into it. Perhaps i had done something wrong, but if I just used record.name from the get-go, giving it a big size, and then mallocing the size I actually write to be smaller, it works. In short, i used my 'writer' program to solve things.
Blackbinary
A: 

Firstly, a nitpick: you never need sizeof (char) - it's 1, always, by definition.

As for the blank name output: do you perhaps need a newline after the %s to flush the output? I've seen weird behaviour when you leave this out, and you don't state which platform you are using. If the platform's printf() is implemented bizarrely enough, you could have the format string printed and flushed, but the name itself stuck in the C library's buffers when your program exits.

And I'm never happy about reading or writing blobs of binary data like a struct to and from files. Realise that by doing so you're promising your program that it will only ever read what it wrote on the same platform. You couldn't write a file on, say, a 64-bit host and read the file back in on a 16-bit microwave oven controller.

Bernd Jendrissek