使用 std::ios_base::iostate 验证 istream

问题描述 投票:0回答:1

我必须编写一个流提取

operator>>
函数,该函数检查参数中的istream变量(流)是否有效,然后将其与分隔符(已提供)一起返回。

我应该检查不仅流是否有效(并且没有错误,例如它不在文件的位置、具有权限或具有正确的类型),而且我要做所有的事情在本地对象内,如果一切顺利的话,最后将其移动到位。

std::istream& operator>>(std::istream& stream, GroceryItem& groceryItem) {
    GroceryItem local;            // local object
    char delimiter = `\x{0000}`;  // already provided
    std::string check;
    std::istringstream iss;
    // implementing the rest of the code...
    if (stream && stream.good()) {
        // the if statement here checks if stream can be open and if there are
        // no errors with it
         while (std::getline(stream, check) {
              if (!getline(iss, check, delimiter) {
                stream.setstate(std::ios::fail) abort();
              } else {
                iss >> std::quoted(check, delimiter);
                local._upcCode = check;
                local._brandName = check;
                local._productName = check;
                local._price = check;
              }
         stream >> std::quoted(local._upcCode) >> delimiter
                >> std::quoted(local._brandName) >> delimiter
                >> std::quoted(local._productName) >> delimiter
                >> local._price >> delimiter;
         }
      groceryItem = std::move(local)
    } else {
        return stream.setstate(std::ios::fail);
    }
    return stream;
}

这是流的示例

"00072250018548","Nature's Own","Nature's Own Butter Buns Hotdog - 8 Ct",10.79

"00028000517205", "Nestle"             , 
"Nestle Media Crema Table Cream"       , 
17.97

"00034000020706"    , 
"York", 
"York Peppermint Patties Dark Chocolate Covered Snack Size", 
12.64 "00038000570742", 
"Kellogg's", "Kellogg's Cereal Krave Chocolate",
  18.66

"00014100072331" , "Pepperidge Farm", "Pepperidge Farm Classic Cookie Favorites", 14.43

"00000000000000", "incomplete / invalid item"

所以我的问题是,这一切都能编译吗?或者也许有一种简单的方法可以做到这一点?

c++ eof istream
1个回答
0
投票

有关 OP 的注释

所以我的问题是,这一切都能编译吗?或者也许有一个简单的方法可以做到这一点?

这里有两个问题。答案是:(1)尝试一下,然后看看。 (2)是的!请参阅下面的课程

Student

有问题
delimiter

当我将 OP 中的代码插入我创建的测试框架时,我得到的第一个错误是关于用于初始化变量

delimiter
的 C++23 字符文字的抱怨。我的编译器尚不支持它。

char delimiter = '\x{ 0000 }';  // already provided

因为我打算将其更改为简单的逗号,所以无论如何,我还是这么做了。

char const delimiter = ',';
不匹配的括号

下一个投诉是关于几个不匹配的括号,所以我解决了这个问题。

while (std::getline(stream, check) {         // <------ missing )
    if (!getline(iss, check, delimiter) {    // <------ missing )
        stream.setstate(std::ios::fail) abort();
    }
    //...
}
一大堆错误

完成这些预备工作后,我终于能够让编译器告诉我它真正的想法,但结果并不好。

它给了我一系列错误,从这些开始。

1>C:\Users\blotc\OneDrive\Documents\Programming\StackOverflow\Answers\StackOverflow_78980542_ExtractGroceryItem_Question.cpp(57,47): error C3867: 'std::ios_base::fail': non-standard syntax; use '&' to create a pointer to member
1>C:\Users\blotc\OneDrive\Documents\Programming\StackOverflow\Answers\StackOverflow_78980542_ExtractGroceryItem_Question.cpp(57,53): error C2146: syntax error: missing ';' before identifier 'abort'
1>C:\Users\blotc\OneDrive\Documents\Programming\StackOverflow\Answers\StackOverflow_78980542_ExtractGroceryItem_Question.cpp(64,36): error C2440: '=': cannot convert from 'std::string' to 'double'
1>    C:\Users\blotc\OneDrive\Documents\Programming\StackOverflow\Answers\StackOverflow_78980542_ExtractGroceryItem_Question.cpp(64,36):

错误已经够多了,因此我决定最好演示如何正确执行,而不是逐一解决每个错误。

类似的
operator>>
,用于班级
Student

这似乎是某种家庭作业,所以让我们以类似的学生/GPA 为例,而不是杂货。

在此示例中,也在 OP 中,示例数据被结构化为 records

  • 每条记录有 4 个字段
  • 字段以逗号分隔,但第 4 个字段后面没有逗号。
  • 空格可以出现在分隔逗号之前和之后。
  • 前 3 个字段是带引号的字符串。
  • 第4个字段是浮点数。
  • 记录之间可以出现任意数量的空白。
operator>>

operator>>
在类
Student
中定义,作为隐藏的朋友。这样,它就可以访问类的私有数据成员。

friend std::istream& operator>>(std::istream& stream, Student& student)
{
    Student local;
    char const delimiter{ ',' };  // Use comma, NOT '\x{0000}'
    char a, b, c;
    stream >> std::quoted(local._studentID)
        >> a >> std::quoted(local._studentName)
        >> b >> std::quoted(local._major)
        >> c >> local._gpa;
    if (stream.fail() ||
        a != delimiter ||
        b != delimiter ||
        c != delimiter) {
        stream.setstate(std::ios_base::failbit);
    }
    else {
        student = std::move(local);
    }
    return stream;
}
流提取

流提取与 OP 中的操作之一非常相似。它将多个输入操作链接在一起。如果其中任何一个失败,

stream
将被置于失败状态,并且随后的 I/O 将失败。

    stream >> std::quoted(local._studentID)
        >> a >> std::quoted(local._studentName)
        >> b >> std::quoted(local._major)
        >> c >> local._gpa;
delimiter

请注意,分隔符是

','
,而不是
'x{0000}'

读入分隔符时,每个分隔符都存储在单独的字符变量

a
b
c
中。这样就可以验证每个都是逗号。

如果其中任何一个不是逗号,则以下 if 语句会将流置于失败状态。

值得指出的一件事:如果流已经失败,则无需再次设置其

failbit
。我们在这里这样做,只是为了避免 if 语句的混乱。

if (stream.fail() ||
    a != delimiter ||
    b != delimiter ||
    c != delimiter) {
    stream.setstate(std::ios_base::failbit);
}
测试框架

这是我用于测试的框架。

  • 在OP中,

    operator>>
    包含一个输入多个记录的循环。该循环已移至下面的函数
    main
    中。

  • 在OP中,

    operator>>
    包含似乎是检查输入文件是否已成功打开的内容。该检查属于函数
    main
    ,文件打开操作本身也是如此。

  • 下面的程序通过将示例数据放入

    std::istringstream
    对象并从中读取来避免使用文件。

// main.cpp
#include <iomanip>   // quoted, setprecision
#include <iostream>  // cout, fixed, ios_base, istream, ostream
#include <sstream>   // istringstream
#include <string>    // getline, string
#include <utility>   // move

//======================================================================
// Student
//======================================================================
class Student
{
    std::string _studentID;
    std::string _studentName;
    std::string _major;
    double _gpa{};
public:
    Student()
        = default;
    Student(
        std::string studentID,
        std::string studentName,
        std::string major,
        double gpa)
        : _studentID{ studentID }
        , _studentName{ studentName }
        , _major{ major }
        , _gpa{ gpa }
    {}

    // More member functions
    // ...

    friend std::ostream& operator<<(std::ostream& stream, Student const& student)
    {
        stream << std::fixed << std::setprecision(1)
            << "Student ID   : " << student._studentID
            << "\nStudent name : " << student._studentName
            << "\nMajor        : " << student._major
            << "\nGPA          : " << student._gpa
            << '\n';
        return stream;
    }
    friend std::istream& operator>>(std::istream& stream, Student& student)
    {
        Student local;
        char const delimiter{ ',' };  // Use comma, NOT '\x{0000}'
        char a, b, c;
        stream >> std::quoted(local._studentID)
            >> a >> std::quoted(local._studentName)
            >> b >> std::quoted(local._major)
            >> c >> local._gpa;
        if (stream.fail() ||
            a != delimiter ||
            b != delimiter ||
            c != delimiter) {
            stream.setstate(std::ios_base::failbit);
        }
        else {
            student = std::move(local);
        }
        return stream;
    }
};
//======================================================================
// sample_data
//======================================================================
std::string sample_data() {
    return
        R"sample("00072250018548","Dwayne “The Rock” Johnson","Wrestling",2.8

"00028000517205", "Beyoncé"             , 
"Music"       , 
4.0

"00034000020706"    , 
"Cristiano Ronaldo", 
"Futebol", 
4.0 "00038000570742", 
"Kim Kardashian", "Undeclared",
  1.9

"00014100072331" , "Oprah Winfrey", "Broadcast Journalism", 3.2

"00000000000000", "incomplete / invalid item"
)sample";
}
//======================================================================
// main
//======================================================================
int main()
{
    std::istringstream iss{ sample_data() };
    Student student;
    int count{};
    while (iss >> student) {
        std::cout << student << '\n';
    }
    return 0;
}
// end file: main.cpp
输出
Student ID   : 00072250018548
Student name : Dwayne ôThe Rockö Johnson
Major        : Wrestling
GPA          : 2.8

Student ID   : 00028000517205
Student name : BeyoncΘ
Major        : Music
GPA          : 4.0

Student ID   : 00034000020706
Student name : Cristiano Ronaldo
Major        : Futebol
GPA          : 4.0

Student ID   : 00038000570742
Student name : Kim Kardashian
Major        : Undeclared
GPA          : 1.9

Student ID   : 00014100072331
Student name : Oprah Winfrey
Major        : Broadcast Journalism
GPA          : 3.2
© www.soinside.com 2019 - 2024. All rights reserved.