Как извлечь смешанный формат с помощью istringstream

Почему моя программа не выводит:

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

По сути, я пытаюсь разобрать строки, разделенные запятыми, любой более плавный способ сделать это помог бы мне безмерно.

4

Решение

В 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
}
4

Другие решения

Позвольте мне предложить следующее.

Я не считаю это «более плавным», поскольку диалог 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 ‘,’
Яблоко 1

7

Вы должны сделать следующие изменения:

string str = "10  1.546 Apple 1";

А также

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

В вашем примере у пустышки была бы строка «, 1.546, Apple». Поскольку, пока не встречается нечисловой символ, он передается в переменную a. После этого все добавляется в пустышку (строку), пока не будет достигнут разделитель по умолчанию (пробел)

0

Мне удалось немного изменить свой код. Не реализовал 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;
}
0
По вопросам рекламы [email protected]