在C ++中使用直接访问文件

时间:2014-10-16 09:44:33

标签: c++ file random-access

我对C ++(以及一般的编程)非常陌生,我正在开发一个让我难过的项目(不难做到哈哈)。该项目涉及直接访问文件。我们要创建一个由一系列零件记录组成的文件。以下是一些规范:

  

应该包含一个标题记录(24个字节 - 填充)表示   有效项目数量。

     

每个(24字节长)数据记录将包含一个   库存号码(最多4位数),描述(最多8个字符),计数   (4位数)和"测试部分"指标(4位数最大=> -1 - 结束   文件)。

     

此文件最初将包含20个空白(虚拟)记录   按顺序创建。

     

创建文件后,进行更新   将根据库存号和新的访问顺序文本文件   记录将被插入到文件中。

     

更新完成后,   有效的零件记录将从库存开始按顺序打印   记录" 1"。

     

通过阅读文本更新文件(prog4.dat)和更新来更新   根据库存号码寻找文件位置(不要忘记   标题记录)

例如:

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

我对Direct Access文件一无所知,所以一直在查看我在Google上发现的几个不同的链接(http://cee-ux49.cee.illinois.edu/cee490/public_html/pdfs_vgs/aL23_Direct_Access_Files.pdfhttp://www.learncpp.com/cpp-tutorial/137-random-file-io/),但我遇到了麻烦弄清楚如何使这个特定的程序工作。

我没有在代码方面做得太多,因为就像我说的那样,这让我很难过,而我所拥有的主要是基于第一个链接所以我不知道它&# 39;是对的(不确定给出矢量的大小,因为我并不完全确定在我的具体问题中会是什么(部分数组也许?)),但这里有点我曾经能够拿出来。

#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);
        }
}

以下是我将用于更新的文件:

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

以下是我的一些具体问题:

  1. 我如何将updateFile中的信息存储到要写入输出文件的变量中(还没有创建)?

  2. 如何以正确的顺序编写数据,因为它基于updateFile中每行的最后一个数字。

  3. 例如,输出文件应该以最低stockNumber为1开始,因此根据文件,stockNumber 1的项目是bidgits。然后该文件应该查看该行的最后一个数字(3),并用该stockNumber(didgits)写出该项目的信息,依此类推。

    这些是我现在突然想到的主要问题,但我确定随着这一进展会弹出更多。任何帮助将不胜感激。此外,这是在大约5个小时内完成的,所以我试图尽可能地坚持我已经拥有的代码(如果可能的话,我知道会增加更多的代码)。

2 个答案:

答案 0 :(得分:2)

首先,您似乎在您的班级中有5个成员变量,尽管每个项目中只有4个数据。这很危险。

修好之后,我只是把整个文件读成这些对象的向量。对于写入,只需使用一个小循环就可以根据下一个数字跳转向量。

你不需要所有那些吸气剂和制定者。别担心Grady Booch说的话:你是唯一的程序员,你可以相信自己不要搞砸自己的数据。即使你不能,也可以通过直接访问公共成员变量轻松搞砸了setter。

只需使用cin,scanf或您喜欢的任何内容读取和解析文件。如果字段都是固定宽度,那么用fread读取已知数量的字符可能是最简单的选项。使用atoi从字符串中生成数字。

答案 1 :(得分:2)

对于这样的项目,我想写一些原始函数来读取和写入整个记录并更新文件头。

我会创建POD类型结构来存储要从数据文件中读取或写入的单个记录。

例如:

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
};

编写标题的函数(始终位于文件位置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)
}

在特定位置写一条记录同样的事情

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)
}

然后您可以根据这些原语编写初始化函数

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;
}

然后在真实文件上调用它,就像这样

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;
    }

    // ...
}

注意:此代码是草率编写的,完全未经测试,仅作为如何解决此问题的示例。希望它会给你一些想法。

ALSO:编写这样的二进制文件在系统之间甚至在同一平台上的同一编译器的不同版本之间不是很容易移植。