重载运算符>>() 表示未知的输入长度/结构

Overloading operator>>() for unknown length/structure of input

本文关键字:gt 输入 结构 表示 运算符 重载 未知      更新时间:2023-10-16

我正在尝试读取包含个人信息的文件。每行包含一个人的数据,假设它看起来像这样:

First(s) Last ID SSN
Peter Barker 1234 5678
James Herbert Bond 007 999
Barack Hussein Obama 2007 14165

所以我想用std::copy来读取每一行,并将其复制到(std::vector<Person>(中,如下所示:

struct Person
{
std::string firstName_s;
std::string lastName;
int ID;
int SSD;
}

我认为为此重载提取运算符会很方便:

std::istringstream& operator>>(std::istringstream& in, struct Person& person)
{
struct Person tmp;
in  >> tmp.firstName_s 
>> tmp.lastName 
>> tmp.ID 
>> tmp.SSN;
person = std::move(tmp);
return in;
}

但是,我遇到的问题是我不知道这个人会有多少个名字

我想过将全名读成一个字符串,直到我遇到一个数字,他们将姓氏与包含名字的字符串分开,这工作正常,但看起来很"丑陋"。 如果有人有更好的建议,或者我可以查看的链接,那就太好了,我似乎无法自己找到一些东西! 谢谢。

OP 不想

更改文件,因此此答案不再适用。工作正在进行中。

这里有两大策略

我们可以选择:

  • 让用户为我们设置数据格式。
  • 程序格式化数据。

然后我们解析它。

1(固定数据格式

最简单的解决方案。
让用户以易于解析的格式输入数据。在这里,由用户正确输入数据,程序将检查输入的数据是否正确。这可以通过多种方式完成,包括但不完全:

Peter-Richmond Barker 1234 5678        // hyphen(-) separated
"James Herbert" Bond 007 999           // enclosed in quotes("")
Barack_Hussein Obama 2007 14165        // underscore(_) separated

在第一种和第三种情况下,std::cin >> person.first_names就足够了。
在第二种情况下,您必须

std::getline(std::cin, person.first_names, '"'); // any character delimiter

它,在用std::cin.get() == '"'检查打开分隔符后。

2(缓慢而稳定

另一个非常简单的解决方案。只需让用户一次输入一件事

std::cout << "Enter some datum 1: ";
std::cin >> person.some_datum_1;
...

(与流行的想象相反,数据是单数的,数据是复数的(。
对于多个输入,请参阅行标记化:

让我在这里抓住一种方法:

std::cout << "Enter some data 1: ";
// Grab the line and put into a stream
std::getline(std::cin, line);
std::stringstream line_buffer(line);
// Prepare to iterate over the stream
std::istream_iterator<std::string> it(line_buffer);
std::istream_iterator<std::string> end;
// Set the name with a move assignment operator
person.first_names = std::move(std::vector<std::string>(it, end));
...

请注意,此方法需要person.first_namesstd::vector<std::string>

3(从最后开始

在这里,我们首先输入未确定的大小数据。

警告:它仅适用于单个未确定大小的输入。如果名字和姓氏都可以超过两个,这将不起作用。我提到它只是为了完整。

如果您不想强迫用户这样做并破坏他们的体验,则必须自己解析输入。用好的旧std::getline(std::cin, line);输入整行。

初始化int read_from = std::string::npos;
现在,用read_from = line.rfind(' ', read_from);找到最后一个空格。read_from == std::string::npos会告诉您所有输入都已解析,或者存在错误。

line.substr(read_from)将获取最后一个输入。将其转换为适当的类型并存储。您还必须使用line.resize(read_from);擦除解析的输入

冲洗并重复其他输入。

注意:建议将未确定的数据存储在适当类型的std::vector中。

4(字节进行曲

我知道你会说我们没有解决OP的问题,

。读取包含个人信息的文件...

现在我们已经讨论了从用户那里获取输入,我们还可以选择如何存储它(并获取它(。

最简单的方法是:

personal_data_file.write((char*)&person_list[i], sizeof(Person));  // Write it...
personal_data_file.read((char*)&person_list[i], sizeof(Person));   // ...Now read it.

在循环中,其中person_listPersons 的std::vector

注意:请记住以std::ios::binary模式打开文件!

优雅!


但以防万一您不熟悉上面示例中使用的类和某些功能。以下是一些链接:

标准::获取线https://www.geeksforgeeks.org/how-to-use-getline-in-c-when-there-are-black-lines-in-input/

std::istream::readhttp://www.cplusplus.com/reference/istream/istream/read/

std::ostream::writehttp://www.cplusplus.com/reference/ostream/ostream/write/

标准::矢量https://www.geeksforgeeks.org/vector-in-cpp-stl/

标准::istream_iteratorhttp://www.cplusplus.com/reference/iterator/istream_iterator/

如果你有一个可变长度的行(就字数而言(,你可以简单地读取整行并从右侧处理它,或者缓存所有单词并使用偏移量。下面的示例执行后一个示例。

int to_int(std::string_view str)
{
int val = 0;
std::from_chars(str.data(), str.data() + str.size(), val);
return val;
}
std::istream& operator>>(std::istream& in, Person& person)
{
std::string line;
// read whole line
if (std::getline(in, line))
{
// split line into words
std::vector<std::string> words;
std::stringstream tmp_stream(line);
for (std::string word; tmp_stream >> word; )
words.push_back(word);
// join first names
tmp_stream.str(words[0]);
for (std::size_t i = 1; i < words.size() - 3; i++)
tmp_stream << ' ' << words[i];
person.firstName_s = tmp_stream.str();
person.lastName = words[words.size() - 3];
person.ID = to_int(words[words.size() - 2]);
person.SSN = to_int(words[words.size() - 1]);
}
return in;
}

我认为代码是不言自明的。这是一个完整的示例。