如何使用istringstream提取混合格式

How to extract mixed format using istringstream

本文关键字:混合 格式 提取 istringstream 何使用      更新时间:2023-10-16

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

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

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

请允许我提出以下建议。

我不认为它"更流畅",因为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;
}

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

10','
1.546','
Apple 1

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

为了避免这种情况,您可以为伪变量使用char,该变量只有一个字符的空间。如果您希望将Apple 1放入字符串中,则需要未格式化的提取,因为格式化的提取器operator>>()只读取空白。此处使用的适当函数是std::getline():

string c;
char dummy;
if ((stream >> a >> dummy >> b >> dummy) &&
std::getline(stream >> std::ws, s))
//   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
{
}

格式化提取后清除换行也是必要的,这就是为什么我使用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
} 

您应该进行以下更改:

string str = "10  1.546 Apple 1";

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

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

我可以稍微更改一下代码。还没有实现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;
}