【问题标题】:How to extract mixed format using istringstream如何使用 istringstream 提取混合格式
【发布时间】:2014-03-15 21:02:03
【问题描述】:

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

10
1.546
,Apple 1

而不是

10
1
<empty space>

这是我的程序:

#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;
}

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

【问题讨论】:

  • char dummy 会修复它(第二个是吃掉输入)
  • @DieterLücking string dummy; d'oh。我像个白痴一样盯着代码看,没看到:)
  • @DieterLücking 是的,它已改进为输出 10 和 1.546,但在我需要 Apple 1 的地方,我什么也没得到,我现在得到 Apple 但仍然不是 Apple 1。有什么想法吗?
  • @SunilKundal 提取在Apple1 之间的空间停止。您需要使用std::getline()(当然在清除换行符之后)。

标签: c++ istringstream formatted-input


【解决方案1】:

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

为避免这种情况,您可以为虚拟变量使用char,它只有一个字符的空间。如果您希望将Apple 1 放入字符串中,您将需要unformatted 提取,因为格式化提取器operator&gt;&gt;() 只读取直到空白。这里使用的适当函数是std::getline()

string c;
char dummy;

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

}

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


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

您可以使用流中包含的语言环境的std::ctype&lt;char&gt; facet 将逗号字符的分类设置为空白字符。这将使虚拟变量的使用变得不必要。这是一个例子:

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
} 

【讨论】:

    【解决方案2】:

    请允许我提出以下建议。

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

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

     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;
     }
    

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

    10','
    1.546 ','
    苹果 1

    【讨论】:

    • 我喜欢这种方法..而且更简单。谢谢道格拉斯。
    【解决方案3】:

    我可以设法稍微更改我的代码。尚未实现 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;
    }
    

    【讨论】:

      【解决方案4】:

      您应该进行以下更改:

      string str = "10  1.546 Apple 1";
      

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

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

      【讨论】:

      • 你应该解释原因。
      猜你喜欢
      • 1970-01-01
      • 2017-12-15
      • 1970-01-01
      • 1970-01-01
      • 2016-04-18
      • 2020-10-18
      • 2022-11-27
      • 2012-09-09
      • 1970-01-01
      相关资源
      最近更新 更多