C++ 如何使用 istringstream 提取混合格式

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21814297/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:49:49  来源:igfitidea点击:

How to extract mixed format using istringstream

c++istringstreamformatted-input

提问by Sunil Kundal

Why does my program not output:

为什么我的程序不输出:

10
1.546
,Apple 1

instead of

代替

10
1
<empty space>

here's my program:

这是我的程序:

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main () {
    string str = "10,1.546,Apple 1";
    istringstream stream (str);
    int a;
    double b;
    string c, dummy;
    stream >> a >> dummy >> b >> dummy >> c;
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
    return 0;
}

Basically I am trying to parse the comma-separated strings, any smoother way to do this would help me immense.

基本上我正在尝试解析逗号分隔的字符串,任何更流畅的方法都会对我有很大帮助。

采纳答案by 0x499602D2

In IOStreams, strings (meaning both C-strings and C++ strings) have virtually no formatting requirements. Any and all characters are extracted into a string only until a whitespace character is found, or until the end of the stream is caught. In your example, you're using a string intended to eat up the commas between the important data, but the output you are experiencing is the result of the behavior I just explained: The dummystring doesn't just eat the comma, but also the rest of the character sequence until the next whitespace character.

在 IOStreams 中,字符串(即 C 字符串和 C++ 字符串)几乎没有格式要求。任何和所有字符都被提取到一个字符串中,直到找到一个空白字符,或者直到捕获到流的末尾。在您的示例中,您正在使用一个字符串来吃掉重要数据之间的逗号,但是您遇到的输出是我刚刚解释的行为的结果:dummy字符串不仅吃掉了逗号,还吃掉了逗号字符序列的其余部分,直到下一个空白字符。

To avoid this you can use a charfor the dummy variable, which only has space for onecharacter. And if you're looking to put Apple 1into a string you will need an unformattedextraction because the formatted extractor operator>>()only reads until whitespace. The appropriate function to use here is std::getline():

为避免这种情况,您可以char对虚拟变量使用 a ,该变量只有一个字符的空间。如果你想Apple 1放入一个字符串,你将需要一个无格式的提取,因为格式化的提取器operator>>()只读取空白处。此处使用的适当功能是std::getline()

string c;
char dummy;

if ((stream >> a >> dummy >> b >> dummy) &&
     std::getline(stream >> std::ws, s))
//   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{

}

Clearing the newline after the formatted extraction is also necessary which is why I used std::wsto clear leading whitespace. I'm also using an ifstatement to contain the extraction in order to tell if it succeeded or not.

在格式化提取后清除换行符也是必要的,这就是我过去std::ws清除前导空格的原因。我还使用一个if语句来包含提取,以判断它是否成功。



Any smoother way to do this would help me immensely.

任何更顺畅的方式来做到这一点都会对我有很大帮助。

You can set the classification of the comma character to a whitespace character using the std::ctype<char>facet of the locale imbued in the stream. This will make the use of a dummy variable unnecessary. Here's an example:

您可以使用std::ctype<char>流中灌输的语言环境的方面将逗号字符的分类设置为空白字符。这将使使用虚拟变量变得不必要。下面是一个例子:

namespace detail
{
    enum options { add, remove };

    class ctype : public std::ctype<char>
    {
    private:
        static mask* get_table(const std::string& ws, options opt)
        {
            static std::vector<mask> table(classic_table(),
                                           classic_table() + table_size);
            for (char c : ws)
            {
                if (opt == add)
                    table[c] |= space;
                else if (opt == remove)
                    table[c] &= ~space;
            }
            return &table[0];
        }
    public:
        ctype(const std::string& ws, options opt)
            : std::ctype<char>(get_table(ws, opt)) { }
    };
}

class adjustws_impl
{
public:
    adjustws_impl(const std::string& ws, detail::options opt) :
        m_ws(ws),
        m_opt(opt)
    { }

    friend std::istream& operator>>(std::istream& is,
                                    const adjustws_impl& manip)
    {
        const detail::ctype* facet(new detail::ctype(manip.m_ws, manip.m_opt));

        if (!std::has_facet<detail::ctype>(is.getloc())
        {
            is.imbue(std::locale(is.getloc(), facet));
        } else
            delete facet;

        return is;
    }
private:
    std::string m_ws;
    detail::options m_opt;
};

adjustws_impl setws(const std::string& ws)
{
    return adjustws_impl(ws, detail::add);
}

adjustws_impl unsetws(const std::string& ws)
{
    return adjustws_impl(ws, detail::remove);
}

int main()
{
    std::istringstream iss("10,1.546,Apple 1");
    int a; double b; std::string c;

    iss >> setws(","); // set comma to a whitespace character

    if ((iss >> a >> b) && std::getline(iss >> std::ws, c))
    {
        // ...
    }

    iss >> unsetws(","); // remove the whitespace classification
} 

回答by 2785528

Allow me to suggest the following.

请允许我提出以下建议。

I don't consider it 'smoother', as cin / cout dialogue is not 'smooth', imho.

我不认为它“更流畅”,因为 cin / cout 对话并不“流畅”,恕我直言。

But I think this might be closer to what you want.

但我认为这可能更接近你想要的。

 int main (int, char**)
 {
    // always initialize your variables 
    // to value you would not expect from input        
    int            a = -99;
    double         b = 0.0;
    std::string    c("");
    char comma1 = 'Z';
    char comma2 = 'z';

    std::string str = "10,1.546,Apple 1";
    std::istringstream ss(str);

    ss >> a >> comma1 >> b >> comma2;

    // the last parameter has the default delimiter in it
    (void)getline(ss, c, '\n');  // to get past this default delimiter, 
                                 // specify a different delimiter

    std::cout << std::endl;
    std::cout << a << "   '" << comma1 <<  "'   " << std::endl;
    std::cout << b << "   '" << comma2 <<  "'   " << std::endl;
    std::cout << c << std::endl;

    return 0;
 }

Results: (and, of course, you need not do anything with the commas.)

结果:(当然,你不需要对逗号做任何事情。)

10 ','
1.546 ','
Apple 1

10 ','
1.546 ','
苹果 1

回答by Sunil Kundal

I could manage to change my code a little. Didn't implement 0x499602D2method yet, but here is what worked for me.

我可以设法稍微更改我的代码。还没有实现0x499602D2方法,但这是对我有用的方法。

#include <iostream>
#include <string>
#include <cstdlib>
#include <sstream>

using namespace std;

int main () {
    string str = "10,1.546,Apple 1";
    istringstream stream (str);
    int a;
    double b;
    string c;
    string token;
    while (getline (stream, token, ',')) {
        if (token.find (".") == string::npos && token.find (" ") == string::npos) {
            a = atoi (token.c_str ());
        } else if (token.find (".") != string::npos) {
            b = atof (token.c_str ());
        } else {
            c = string (token);
        }
    }
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;
    return 0;
}

回答by Nipun Talukdar

You should do the below changes:

您应该进行以下更改:

string str = "10  1.546 Apple 1";

And

 stream >> a >> b >> dummy >> c;

In your example, dummy would have got the string ",1.546,Apple" . Because till a non-numeric char is encountered, it is fed to variable a. After that everything is added to dummy ( a string ) until the default delimiter (space) is reached

在您的示例中, dummy 会得到字符串 ",1.546,Apple" 。因为直到遇到非数字字符,它才会被馈送到变量 a。之后,所有内容都添加到虚拟(字符串)中,直到达到默认分隔符(空格)