简体   繁体   English

从文件中读取不正确的数据(C ++,fstream)

[英]Improper Data Reading from File (C++, fstream)

Entire question : 整个问题:

Question 3 问题3

You are the owner of a hardware store and need to keep an inventory that can tell you what different tools you have, how many of each you have on hand and the cost of each one. 您是五金店的所有者,需要保留一份库存,告诉您您拥有的不同工具,每个工具的数量以及每个工具的成本。 Write a program that initializes the random-access file "hardware.dat" to 100 empty records, let you input the data concerning each tool, enables you to list all your tools, lets you delete a record for a tool that you no longer have and lets you update any information in the file. 编写一个程序,将随机访问文件“hardware.dat”初始化为100个空记录,让您输入有关每个工具的数据,使您能够列出所有工具,让您删除不再拥有的工具的记录并允许您更新文件中的任何信息。 The tool identification number should be the record number. 工具识别号应为记录号。 Use the following information to start your file. 使用以下信息启动文件。

在此输入图像描述


My Code : 我的代码:

int question_3()
{
    cout << "Question 3" << endl;

    fstream hardware;
    hardware.open("hardware.dat" , ios::binary | ios::out);


//Create 100 blank objects---------------------------------------------------------------
    if (!hardware)
    {
        cerr << "File could not be opened." << endl;
        exit(1);
    }

    HardwareData myHardwareData;

    for (int counter = 1; counter <= 100; counter++)
    {
        hardware.write(reinterpret_cast< const char * >(&myHardwareData), sizeof(HardwareData));
    }

    cout << "Successfully create 100 blank objects and write them into the file." << endl;

    hardware.close();
    hardware.open("hardware.dat" , ios::binary | ios::out | ios::in);


//Write data-----------------------------------------------------------------------------
    int record;
    int quantity;
    float cost;
    string tool_name;

    cout << endl;
    cout << "Enter record number (1 to 100, 0 to end input) : ";
    cin >> record;

    while (record != 0)
    {
        cin.sync();
        cout << "Enter tool name : ";           getline(cin, tool_name);
        cout << "Enter quantity : ";            cin >> quantity;
        cout << "Enter cost : ";                cin >> cost;

        myHardwareData.setRecord(record);
        myHardwareData.setToolName(tool_name);
        myHardwareData.setQuantity(quantity);
        myHardwareData.setCost(cost);

        hardware.seekp((myHardwareData.getRecord() - 1) * sizeof(HardwareData));
        hardware.write(reinterpret_cast<const char *>(&myHardwareData), sizeof(HardwareData));

        cout << endl
            << "Enter record number (1 to 100, 0 to end input) : ";
        cin >> record;
    }

    cout << "Successfully write all input data into the file." << endl;


//Read data----------------------------------------------------------------------------
    cout << endl;
    outputDataLineHead();
    hardware.read(reinterpret_cast<char *>(&myHardwareData), sizeof(HardwareData));
    int counter = 0;
    cout << setprecision(2) << fixed;
    while (hardware && !hardware.eof())
    {
        if (myHardwareData.getRecord() != 0)
            outputDataLine(cout, myHardwareData);

        hardware.seekp(counter++ * sizeof(HardwareData));
        hardware.read(reinterpret_cast<char *>(&myHardwareData), sizeof(HardwareData));
    }

    return 0;
}


//Function for showing data in line form.-----------------------------------------------
void outputDataLineHead()
{
    cout << left << setw(17) << "Record No." 
         << left << setw(17) << "Tool Name"
         << left << setw(17) << "Quantity"
         << left << setw(17) << "Cost" << endl; 
}

void outputDataLine(ostream &output, const HardwareData &Object_in_file)
{
    output << left << setw(17) << Object_in_file.getRecord()
           << left << setw(17) << Object_in_file.getToolName()
           << left << setw(17) << Object_in_file.getQuantity()
           << left << setw(17) << Object_in_file.getCost() << endl;
}

HardwareData.h : HardwareData.h:

#ifndef HAREWAREDATA_H
#define HAREWAREDATA_H

#include <iostream>
using std::string;

class HardwareData
{
public :
    HardwareData(string name = "", int recd = 0, int qutity = 0, float cot = 0.0)
    {
        setToolName(name);
        setRecord(recd);
        setQuantity(qutity);
        setCost(cot);
    }

    void setToolName(string name)
    {
        const char *nameValue = name.data();
        int length = 0;
        length = (length < 15 ? length : 14);
        strncpy(tool_name, nameValue, length);
        tool_name[length] = '\n';
}

string getToolName() const
{
    return tool_name;
}

void setRecord(int recd)
{
    record = recd;
}

int getRecord() const
{
    return record;
}

void setQuantity(int qutity)
{
    quantity = qutity;
}

int getQuantity() const
{
    return quantity;
}

void setCost(float cot)
{
    cost = cot;
}

float getCost() const
{
    return cost;
}

private :
    char tool_name[15];
    int record;
    int quantity;
    float cost;
};

#endif

在此输入图像描述

I want to show the data like the following : 我想显示如下数据:

Record No.        Tool Name          Quantity         Cost
4                 electric hammer    3                34.32

How to achieve this? 怎么做到这一点?


Thank you for your attention. 感谢您的关注。

I think your problem is while reading data.. Please check your variables if they get correct data or not.. You can check this with counting characters or try to printf them. 我认为您的问题是在读取数据时..请检查您的变量是否获得正确的数据..您可以通过计算字符或尝试打印它们来检查。

If they are not correct. 如果他们不正确。 You can use such an example which i used in below. 你可以使用我在下面使用的这样一个例子。

First of all i prefer you to read your line like this example ; 首先,我更喜欢你像这个例子一样阅读你的行;

In this example i get coordinates of faces. 在这个例子中,我得到了面部的坐标。 You should change parameters.. In order not to read no need data 你应该改变参数..为了不读不需要数据

std::string str;
    while(std::getline(in, str))
    {
        sscanf(str.c_str(), "%d %f %f", &fiducial.number, &fiducial.x, &fiducial.y);

        coord_Num[fiducial.number] = fiducial.get_number();
        coord_X[fiducial.number] = fiducial.get_x();
        coord_Y[fiducial.number] = fiducial.get_y();

    }

If everything looks fine. 如果一切都很好。 You should check 你应该检查一下

void outputDataLine(ostream &output, const HardwareData &Object_in_file)

The core issue here is that you're reading and writing bytes to/from objects of type HardwareData when rather you should be creating inserters/extractors so you can implement correct I/O semantics. 这里的核心问题是,您正在向/从类型为HardwareData对象读取和写入字节,而您应该创建插入器/提取器,以便您可以实现正确的I / O语义。 For example: 例如:

 // Inside HardwareData class friend std::ostream& operator<<(std::ostream&, const HardwareData&); friend std::istream& operator>>(std::istream&, HardwareData&); 

These two declarations are for the inserter and extractor respectively. 这两个声明分别用于插入器和提取器。 Input should consist of extracting into the record , tool_name , quantity and cost data members; 输入应包括提取到record ,工具tool_namequantitycost数据成员; and output should simply be an stream insertion which is trivial to implement. 输出应该只是一个流插入,这是很容易实现的。


It is often the problem when mixing formatted input with unformatted input that the residual newline inhibits further input. 将格式化输入与未格式化输入混合时,通常存在问题,即残余换行禁止进一步输入。 That seems to be the case here: 这似乎就是这样的情况:

cin >> record;                                                             /*
^^^^^^^^^^^^^^                                                             */

while (record != 0)
{
    cin.sync();
    cout << "Enter tool name : ";           getline(cin, tool_name);
    //                                      ^^^^^^^^^^^^^^^^^^^^^^^^

    // ...
}

After cin >> record; cin >> record; finishes, there will be a newline left inside the stream. 完成后,流中会留下一个换行符。 That newline will stop std::getline() from working correctly because std::getline() only reads until the newline. 这换行符将停止std::getline()无法正常工作,因为std::getline()只读取直到换行。

The fix here is to ignore this new line by using the std::ws manipulator: 这里的修复是通过使用std::ws操纵器忽略这个新行:

 std::getline(std::cin >> std::ws, tool_name); // ^^^^^^^^^^^^^^^^^^^ 

Note: I talk about this in more detail here . 注意:我在这里更详细地讨论这个问题

But this manual extraction isn't needed as we've already defined the inserter and extractor for our class. 但是不需要这种手动提取,因为我们已经为我们的类定义了插入器和提取器。 So all that's really needed is the following: 所以真正需要的是以下内容:

 while (std::cin >> myHardwareData) { hardware << myHardwareData; } 

or 要么

 std::copy(std::istream_iterator<HardwareData>(std::cin), std::istream_iterator<HardwareData>(), std::ostream_iterator<HardwareData>(hardware)); 

Noticed how I've also taken out the check for a 0 value of record in the while loop. 注意到我如何在while循环中检查了0值的record That's because the extractor takes care of it by reflecting a 0 value of record as invalid input. 那是因为提取器通过将record0值反映为无效输入来处理它。 It sets the stream state of the stream if this occurs, thus allowing ourselves to be ejected from the while if that happens: 它设置了流的流状态,如果发生这种情况,因此允许我们从被喷射while如果出现这种情况:

 std::istream& operator>>(std::istream& is, HardwareData& hd) { cout << "Enter record number (1 to 100, 0 to end input) : "; if ((is >> record) && record != 0) { // ... } else { is.setstate(std::ios_base::failbit); } // ... } 

And the rest of your code be changed to: 并将其余代码更改为:

 std::cout << myHardwareData; hardware >> myHardwareData; std::cout << std::setprecision(2) << std::fixed; while (hardware >> myHardwareData) { if (myHardwareData.getRecord() != 0) std::cout << myHardwareData; } 

I don't really know what the seekp s are for. 我真的不知道搜索seekp的用途。 If you elaborate on that, that would really help me adapt my code more accurately to your needs. 如果您详细说明,那将真正帮助我更准确地调整我的代码以满足您的需求。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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