• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

我读取和来自通过接收c ++的std :: istream对象以逗号分隔的用户的输入?

用户头像
it1352
帮助1

问题说明

我在C 中有一个叫做Airplane的类.我需要使用std :: istream创建一个读取函数,该函数允许用户在控制台提示后键入以逗号分隔的行.然后,使用逗号将输入的这一行分开,并分配给该类的不同私有数据成员.例如,如果用户在控制台中键入"abc,12345,hello",那么我将需要解析该行,并将abc分配给一个变量,将12345分配给另一个变量,并向最后一个变量问好.我相信在用户键入"123,abc,hello"之后,那条线存储在某个地方,我可以使用istream以某种方式访问它?

I have a class in c called Airplane. I need to create a read function using std::istream that lets a user type after a prompt in the console a line that is comma separated. This line of input will then be split up using the commas and assigned to different private data members of the class. As an example, if the user types into the console "abc,12345,hello," then I would need to parse that line and assign abc to one variable, 12345 to another and hello to the last. I believe after the user types in "123,abc,hello," that line is stored somewhere and I can access that using istream somehow?

我到目前为止所拥有的是:

What I have so far is below:

std::istream& Airplane::read(std::istream& in) {
   if (comma_separated == true) {
   // parse the line inputted by the user and then assign it to 3 variables
   // after getting the input somehow assign to variables
   this->first_var = info_before_first_comma;
   this->second_var = second_comma_text;
   etc...
   }
}

我相信我还需要某种重载运算符函数来将该类传递给该类,然后该类调用上面的read函数来处理类数据.可能像下面这样?

I believe I also need some sort of overload operator function to pass the class to, which then calls the read function above to process the class data. Something possibly like below?

std::istream& operator>>(std::istream& output, Airplane& airplane) {}

这样,我可以创建一个类,然后调用cin>>class_name,它将接受输入,对其进行处理,然后将其分配给该类变量.tldr:我需要从控制台读取用户输入,并根据逗号分隔文本,然后分配给变量.我的困惑是我不知道从哪里开始或如何实际获得"123,abc,hello".从用户处进行处理.谢谢您的阅读.

That way I could create a class, then call cin >> class_name and it would take in input, process it, and assign it to that classes variables. tldr: i need to read user input from the console and separate the text based on commas, then assign to variables. my confusion is I dont know where to start or how to actually get the "123,abc,hello," line to process from the user. Thank you for reading.

更新信息下面给出的代码可以运行(选择示例3),但没有给出正确的结果.我称cin>>类名和输入"1234,abcdaef,asdasd"然后按回车键.然后我称呼cout<<classname,它将打印存储的旧数据,并忽略给定的输入.

UPDATED INFORMATION The code given below runs (chose example 3), but doesnt give the correct result. I call cin >> classname and input "1234,abcdaef,asdasd," and press enter. Then I call cout << classname and it prints the old data its storing and ignore the input given.

当我尝试执行以下操作以检查令牌是否正在存储数据时:

When I try to do the following to check if the tokens are storing data:

            std::cout << token[0] << std::endl;
            std::cout << token[1] << std::endl;
            std::cout << token[2] << std::endl;

我得到一个调试向量下标超出范围".错误.

I get a debug "vector subscript out of range" error.

这是我将3个值存储到我的私有数据成员中的方式,我有一个int和2个char数组.

This is how I stored the 3 values into my private data members, I have an int and 2 char arrays.

                this->store_int = std::stoi(token[0]);

                this->store_first_char = new char[token[1].length()   1];
                strcpy(this->store_first_char, token[1].c_str());

                this->store_second_char = new char[token[2].length()   1];
                strcpy(this->store_second_char, token[2].c_str());

但是这也不起作用.我忘了要澄清的一件事是,如果重要的话,最后总是会有一个逗号.谢谢.

But this didnt work either. One thing I forgot to clarify is that there is a comma at the end always if that matters. Thank you.

正确答案

#1

让我们一步一步来.

首先也是最重要的一点,将使用功能 std :: getline 读取一条完整的输入行.此函数将从任何 std :: istream 中读取整行,并将其放入 std :: string .

First, and most important, one complete input line will read using the function std::getline. This function will read a complete line from whatever std::istream and put it into a std::string.

然后,我们需要将完整的字符串分成子字符串.子字符串用逗号分隔.最后,我们将有一个STL容器,其中包含所有子字符串.

Then, we need to split up the complete string into substrings. The substrings are separated by comma. In the end, we would have an STL container, containing all the substrings.

然后,我们进行完整性检查,查看拆分字符串后得到的子字符串的数量.如果计数没问题,那么我们要么直接存储字符串,要么将它们转换为所需的数据类型,例如 int float .

Then we make a sanity check and look at the number of substrings that we got after splitting up the string. If the count is OK, then we do either store the strings directly, or convert them to the required data type, for example an int or a float.

由于使用 std :: getline 读取一行很简单,因此我们首先将重点放在拆分字符串上.这也称为标记字符串.

Since the reading of a line with std::getline is simple, we will first concentrate on splitting up a string. This is also called tokenizing a string.

我将向您展示几种不同的方法来标记字符串:

I will show you several different approaches on how to tokenize a string:

将字符串拆分为标记是一项非常古老的任务.有许多可用的解决方案.全部具有不同的属性.有些很难理解,有些很难开发,有些更复杂,更慢或更快,或者更灵活.

Splitting a string into tokens is a very old task. There are many many solutions available. All have different properties. Some are difficult to understand, some are hard to develop, some are more complex, slower or faster or more flexible or not.

替代品

  1. 使用指针或迭代器手工制作的许多变体,可能难以开发并且容易出错.
  2. 使用旧式 std :: strtok 函数.也许不安全.也许不应该再使用
  3. std :: getline .最常用的实现.但是实际上是滥用".不太灵活
  4. 使用专门为此目的而开发的专用现代功能,该功能最灵活且最适合STL环境和算法环境.但是比较慢.
  1. Handcrafted, many variants, using pointers or iterators, maybe hard to develop and error prone.
  2. Using old style std::strtok function. Maybe unsafe. Maybe should not be used any longer
  3. std::getline. Most used implementation. But actually a "misuse" and not so flexible
  4. Using dedicated modern function, specifically developed for this purpose, most flexible and good fitting into the STL environment and algortithm landscape. But slower.

请在一个代码中查看4个示例.

Please see 4 examples in one piece of code.

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <regex>
#include <algorithm>
#include <iterator>
#include <cstring>
#include <forward_list>
#include <deque>

using Container = std::vector<std::string>;
std::regex delimiter{ "," };


int main() {

    // Some function to print the contents of an STL container
    auto print = [](const auto& container) -> void { std::copy(container.begin(), container.end(),
        std::ostream_iterator<std::decay<decltype(*container.begin())>::type>(std::cout, " ")); std::cout << '\n'; };

    // Example 1:   Handcrafted -------------------------------------------------------------------------
    {
        // Our string that we want to split
        std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
        Container c{};

        // Search for comma, then take the part and add to the result
        for (size_t i{ 0U }, startpos{ 0U }; i <= stringToSplit.size();   i) {

            // So, if there is a comma or the end of the string
            if ((stringToSplit[i] == ',') || (i == (stringToSplit.size()))) {

                // Copy substring
                c.push_back(stringToSplit.substr(startpos, i - startpos));
                startpos = i   1;
            }
        }
        print(c);
    }

    // Example 2:   Using very old strtok function ----------------------------------------------------------
    {
        // Our string that we want to split
        std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
        Container c{};

        // Split string into parts in a simple for loop
#pragma warning(suppress : 4996)
        for (char* token = std::strtok(const_cast<char*>(stringToSplit.data()), ","); token != nullptr; token = std::strtok(nullptr, ",")) {
            c.push_back(token);
        }

        print(c);
    }

    // Example 3:   Very often used std::getline with additional istringstream ------------------------------------------------
    {
        // Our string that we want to split
        std::string stringToSplit{ "aaa,bbb,ccc,ddd" };
        Container c{};

        // Put string in an std::istringstream
        std::istringstream iss{ stringToSplit };

        // Extract string parts in simple for loop
        for (std::string part{}; std::getline(iss, part, ','); c.push_back(part))
            ;

        print(c);
    }

    // Example 4:   Most flexible iterator solution  ------------------------------------------------

    {
        // Our string that we want to split
        std::string stringToSplit{ "aaa,bbb,ccc,ddd" };


        Container c(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});
        //
        // Everything done already with range constructor. No additional code needed.
        //

        print(c);


        // Works also with other containers in the same way
        std::forward_list<std::string> c2(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {});

        print(c2);

        // And works with algorithms
        std::deque<std::string> c3{};
        std::copy(std::sregex_token_iterator(stringToSplit.begin(), stringToSplit.end(), delimiter, -1), {}, std::back_inserter(c3));

        print(c3);
    }
    return 0;
}

因此,在拥有诸如"abc,12345,hello"之类的初始字符串之后,我们现在将拥有一个 std :: string s的容器,例如包含子字符串的 std :: vector :和"hello".

So, after having an initial string, like "abc,12345,hello", we will now have a container of std::strings, e.g. a std::vector containing the substrings: So, "abc","12345" and "hello".

"abc"和你好"可以直接存储(分配给)类的字符串变量中." 12345"必须使用现有函数(例如 std :: stoi )转换为 int 并分配给成员变量.

"abc" and "hello" can directly be stored (assigned to) in a string variable of your class. "12345" must be converted using an existing function, like for example std::stoi, to an int and assigned to a member variable.

最后一步是在类(或结构)中使用所有这些内容.

The last step is to use all of this in a class (or struct).

这看起来像这样:

struct MyData {
    // Our data
    std::string item1{};
    int value{};
    std::string item2{};
    
    // Overwrite extractor operator
    friend std::istream& operator >> (std::istream& is, MyData& md) {
        if (std::string line{};std::getline(is, line)) {

            // Here we will store the sub strings
            std::vector<std::string> token{};

            // Put in an istringstream for further extraction
            std::istringstream iss{ line };
            
            // Split
            for (std::string part{}; std::getline(iss, part, ','); c.push_back(part))
                ;

            // Sanity check
            if (token.size() == 3) {
            
                // Assigns value to our data members
                md.item1 = token[0];
                md.value = std::stoi(token[1]);
                md.item2 = token[2];
            }

        }
        return is;
    }
};

很抱歉,但这是未编译的,未经测试的代码.它应该为您提供一个有关如何实施的想法.

Sorry to say, but this is uncompiled, not tested code. It shoud give you an idea, on how it could be implemented.

现在您可以使用 std :: iostream 将数据获取到您的结构中.

And now you can use std::iostream to get data into your struct.

MyData md;
std::cin >> md;

我希望我能回答你的问题.如果没有,请询问.

I hope I could answer your question. If not, then please ask.

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /reply/detail/tangkkbib
系列文章
更多 icon
同类精品
更多 icon
继续加载