简体   繁体   中英

C++ Random Access Files

its my first time to experiment random access files in C++ however, I've tried it before in Java but I can't get it work in C++. The idea is I a want to create 100 empty records, and then store a record at a particular record number in the file. Here is my code, I kept it as simple as possible.

I have here a structure named Tool:

struct Tool {
    int number;
    char name[20];
    int quantity;
    double cost;
};

And here is what I have in the main function:

fstream outFile;
outFile.open("inventory.dat");

// Create 100 empty tool records
Tool tool;
tool.number = 0;
tool.name[0] = '\0';
tool.quantity = 0;
tool.cost = 0;  

for (int i = 0; i < 100; i++) {
    outFile.write(reinterpret_cast<char *>(&tool.number), sizeof(int));
    outFile.write(tool.name, sizeof(char)* 20);
    outFile.write(reinterpret_cast<char *>(&tool.quantity), sizeof(int));
    outFile.write(reinterpret_cast<char *>(&tool.cost), sizeof(double));
}

// Insert A tool record
Tool t;
t.number = 3;
t.quantity = 7;
t.cost = 57;
strcpy(tool.name, "Electric Sander");

outFile.seekp((tool.number - 1) * sizeof(Tool));
outFile.write(reinterpret_cast<char *>(&tool.number), sizeof(int));
outFile.write(tool.name, sizeof(char)* 20);
outFile.write(reinterpret_cast<char *>(&tool.quantity), sizeof(int));
outFile.write(reinterpret_cast<char *>(&tool.cost), sizeof(double));

outFile.close();

The part where I initialize 100 empty record works fine (assuming we are to comment to the insert section of the code.

However, when the insert section is performed, my program generates 4 GB of data. I'm not sure what's going on. I appreciate any kind of help. Thanks in advance.

You could write the entire contents of the struct

    outFile.seekp(t.number*sizeof(Tool));
    outFile.write(reinterpret_cast<char *>(&tool),sizeof(Tool));

Don´t forget to tell the compiler to don´t insert the padding

#ifdef MSVC
#pragma pack(push,1)
#endif
struct Tool {
    int number;
    char name[20];
    int quantity;
    double cost;
#ifdef GCC
}__attribute__((packed));
#else
};
#endif

#ifdef MSVC
#pragma pack(pop)
#endif

Sources:
https://codereview.stackexchange.com/questions/26344/writing-reading-data-structure-to-a-file-using-c
https://stackoverflow.com/a/18654265/194717
Comments of our valuable members.

You used tool where you meant t in the last part. In particular:

outFile.seekp((tool.number - 1) * sizeof(Tool));

Should be:

outFile.seekp((t.number - 1) * sizeof(Tool));

As well as all the other tool. fields at the end (presuming you want to use t ). tool.number at that point is 0, so tool.number - 1 is -1. If pos_type is unsigned and 32-bits, the wrapped value puts your requested position at around 4GB.

Also the alignment point Thomas Matthews brought up in his comment and Tony detailed in his answer is important to ensure that the data in the file is correct.

The point Deduplicator and Galik raised in the comments regarding binary mode is also important to ensure that data is written correctly.

Here's an alternative:

struct Tool
{
    int number;
    char name[20];
    int quantity;
    double cost;
    void binary_write(std::ostream& out) const
    {
      out.write((char *) &number, sizeof(number));
      out.write((char *) &name[0], sizeof(name);
      out.write((char *) &quantity, sizeof(quantity));
      out.write((char *) &cost, sizeof(cost));
    }
    void binary_read(std::istream& inp)
    {
      inp.read((char *) &number, sizeof(number));
      inp.read((char *) &name[0], sizeof(name);
      inp.read((char *) &quantity, sizeof(quantity));
      inp.read((char *) &cost, sizeof(cost));
    }
    size_t binary_size(void) const
    {
      return sizeof(number) + sizeof(name)
             + sizeof(quantity) + sizeof(cost);
    }
};

int main(void)
{
  std::ofstream outfile("test.dat", ios::binary);
  if (!outfile)
  {
    cerr << "Error opening test.dat\n";
    return 1;
  }
  Tool t;
  for (unsigned int i = 0; i < 100; ++i)
  {
    t.binary_write(outfile);
  }
  outfile.close();
  std::ifstream in_file("test.dat", ios::binary)
  if (!in_file)
  {
    cerr << "Error opening test.dat for reading\n";
    return 2;
  }
  in_file.seekg(10 * t.binary_size(), ios::beg);
  t.binary_read(in_file);
  return 0;
}  

The concept here is to place the read and write functions into the object, because the object knows about its members and you can hide the data from other objects (a good thing).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM