如何使用istringstream提取混合格式

Sun*_*dal 5 c++ formatted-input istringstream

为什么我的程序没有输出:

10
1.546
,Apple 1
Run Code Online (Sandbox Code Playgroud)

代替

10
1
<empty space>
Run Code Online (Sandbox Code Playgroud)

这是我的计划:

#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;
}
Run Code Online (Sandbox Code Playgroud)

基本上我试图解析逗号分隔的字符串,任何更平滑的方式来做这将帮助我巨大.

278*_*528 8

请允许我提出以下建议.

我不认为它"更顺畅",因为cin/cout对话不是'顺畅',imho.

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

 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;
 }
Run Code Online (Sandbox Code Playgroud)

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

10','
1.546','
Apple 1


0x4*_*2D2 4

在 IOStreams 中,字符串(意味着 C 字符串和 C++ 字符串)实际上没有格式要求。仅在找到空白字符或捕获流末尾之前,才会将所有字符提取到字符串中。在您的示例中,您使用的字符串旨在吃掉重要数据之间的逗号,但您遇到的输出是我刚才解释的行为的结果:该字符串不仅dummy吃掉了逗号,还吃掉了字符序列的其余部分,直到下一个空白字符。

为了避免这种情况,您可以使用 achar作为虚拟变量,该变量只有一个字符的空间。如果您想要放入Apple 1字符串中,您将需要未格式化的提取,因为格式化的提取器operator>>()只能读取到空格。这里使用的适当函数是std::getline()

string c;
char dummy;

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

}
Run Code Online (Sandbox Code Playgroud)

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


任何更顺利的方法都会对我有很大帮助。

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
} 
Run Code Online (Sandbox Code Playgroud)