Working with Direct Access Files in C++

2020-03-08 08:00发布

问题:

I am extremely new to C++ (and programming in general really) and am working on a project that has me stumped (not hard to do ha). The project involves Direct Access Files. We are to create a file consisting of a series of parts records. Here are some of the specifications:

Should contain a header record (24 bytes - filled) indicating the number of valid items.

Each (24 byte-long) data record will contain a stock number (4 digits max), a description (8 characters max), a count (4 digits), and a "test part" indicator( 4 digits max => -1 - end of file).

This file will initially hold 20 blank (dummy) records and will be created sequentially.

Once the file has been created, an update sequential text file will be accessed based on stock numbers and new records will be inserted into the file.

When the update is complete, the valid parts records will be printed in order starting at stock record "1".

Update by reading the text update file (prog4.dat) and seeking the file position based on the stock number (don't forget the header record)

For example:

Initial (empty)
Input (updates)
    1 widgits 25 3
    6 gidgits 12 8
    8 kidgits 6 -1
    3 didgits 11 6
Output
    1 widgits 25
    3 didgits 11
    6 gidgits 12
    8 kidgits 6

I know absolutely nothing about Direct Access files, so have been looking at a couple different links that I've found on Google (http://cee-ux49.cee.illinois.edu/cee490/public_html/pdfs_vgs/aL23_Direct_Access_Files.pdf , and http://www.learncpp.com/cpp-tutorial/137-random-file-io/ specifically), but am having trouble figuring out how to make this work for this particular program.

I haven't done much in the way of code since like I said, this has me stumped, and what I do have is based primarily off of that first link so I don't know that it's right (not sure what size to give vector since I'm not entirely sure what that would be in my specific problem (the array of parts perhaps?)), but here is what little I've been able to come up with.

#include <iostream>
#include <string>
#include <fstream>
#include <stream>
using namespace std;

class records {
public:
        int getStckNumber() {
                return stockNumber;
        }
        void setStockNumber(int stockNum) {
                stockNumber = stockNum;
        }

        string getItemDespcription() {
                return itemDescription;
        }
        void setItemDespcription(string itemDescrip) {
                itemDescription = itemDescrip;
        }

        int getItemAmount() {
                return itemAmount;
        }
        void setItemAmount(int itemAmt) {
                 itemAmount = itemAmt;
        }

        int getNext() {
                return next;
        }
        void setNext(int nxt) {
                next = nxt;
        }
private:
        int stockNumber;
        string itemDescription;
        int itemAmount;
        int next;
        int recNum;
}


int main() {
        int stockNumber;
        string itemDescription;
        int itemAmount;
        int next;
        int recNum;
        int recSize = sizeof(int) + sizeof(string) + sizeof(int) + sizeof(int) + sizeof(int);

        istream updateFile;
        updateFile.open("prog4.dat");
        if(!updateFile) {
                cerr << "Open Failure" << endl;
                exit(1);
        }
}

Here is the file I will be using for the updates:

10 zidgits 17 -1
14 lidgits 2 7
6 gidgits 12 8
1 bidgits 25 3
16 widgits 9 10
7 midgits 0 2
3 didgits 11 6
5 tidgits 5 16
2 pidgits 7 5
8 kidgits 6 14

Here are some specific questions that I have:

  1. How would I go about storing the information from the updateFile to the variables to be written to the output file (hasn't been created yet)?

  2. How would I get it to write the data in the correct order since it's based on the last number on each line in the updateFile.

For example, the output file is supposed to start with the lowest stockNumber which is 1, so based on the file, the item with stockNumber 1 is bidgits. The file is then supposed to look at the last number on that line (3) and write the information for the item with that stockNumber (didgits) and so on.

Those are the main questions that jump out at me right now, but I'm sure more will pop up as this progresses. Any help would be GREATLY appreciated. Also, this is due in about 5 hours, so I'm trying to stick as closely as I can to the code I already have (I know much more will be added of course) if at all possible.

回答1:

What I would to for a project like this is write some primitive functions for reading and writing whole records and updating the file header.

I would create POD type structs to store individual records that are to be read from or written to the data files.

For example:

struct header
{
    uint32_t valid; // number of valid records
    char pad[20]; // padding to make this object 24 bytes
};

struct record
{
    char no[4]; // stock number
    char desc[8]; // description
    uint32_t count;
    uint32_t test_part;
    char pad[4];  // padding to make this object 24 bytes
};

A function to write the header (which is always at file position 0):

std::iostream& write(std::iostream& ios, const header& h)
{
    ios.clear(); // clear any errors
    ios.seekg(0); // move to beginning of file
    ios.write(reinterpret_cast<const char*>(&h), sizeof(h)); // write the header to file
    return ios; // return the stream (for easy error detection/chaining)
}

Same thing to write a record at a specific position:

std::iostream& write(std::iostream& ios, const record& r, size_t pos)
{
    ios.clear(); // clear any errors
    ios.seekg(sizeof(header) + (sizeof(record) * pos)); // move to record's position
    ios.write(reinterpret_cast<const char*>(&r), sizeof(r)); // write the record to file
    return ios; // return the stream (for easy error detection/chaining)
}

You can then write an initialization function in terms of those primitives:

std::iostream& init(std::iostream& ios, size_t num)
{
    // Update the header to zero records
    header h;
    h.valid = 0;
    write(ios, h);

    // create each record with a -1 (EOF) marker
    record r;
    r.test_part = uint32_t(-1);

    // output 20 copies of that record.
    for(size_t pos = 0; pos < num; ++pos)
        write(ios, r, pos);

    return ios;
}

Then invoke it all on a real file a bit like this:

int main()
{
    assert(sizeof(header) == 24);
    assert(sizeof(record) == 24);

    // binary mode io!
    std::fstream fs("records.dat", std::ios::in|std::ios::out|std::ios::binary);

    if(!init(fs, 20))
    {
        std::cerr << "ERROR: initializing data file:" << std::endl;
        return 1;
    }

    // ...
}

NOTE: This code is hastily written and completely untested and merely presented as an example of how this problem can be approached. Hopefully it will give you some ideas.

ALSO: Writing binary files like this is not very portable between systems or even between different versions of the same compiler on the same platform.



回答2:

Firstly, you seem to have 5 member variables in your class although there are only 4 data in each item. That's dangerous.

After fixing that, I'd just read the whole file into a vector of those objects. For writing, just use a little loop to hop around the vector according to the next number.

You don't need all those getters and setters. Never mind what Grady Booch says: you are the only programmer and you can trust yourself not to screw up your own data. Even if you couldn't, you could just as easily screw it up with the setters as by directly accessing a public member variable.

Just read and parse the file with cin, scanf or whatever you prefer. If the fields are all fixed width then reading a known number of characters with fread is probably your simplest option. Use atoi to make numbers out of strings.