Мы все знаем, как преобразовать строку в число, т.е.
int str2num(const string& str)
{
stringstream is(str);
int result;
return is >> result ? result : 0;
};
Мой вопрос, который я хочу иметь возможность различать, когда строка не может быть преобразована в число, но не 0, например:
1.) "0" => 0
2.) "0dasd" => 0
3.) "" => 0
4.) "some string" => 0 but true
5.) "345" => 345
Я хочу быть в состоянии обнаружить случай (4).
Единственная идея, которая у меня есть, это поиск строки !! .find()
или что-то..
Есть ли у stringstream какой-либо способ указать на эту ситуацию?
РЕДАКТИРОВАТЬ: некоторые уточнения:
Как num2str()
Функция Я в порядке, когда разговор не удается и / или возвращается 0
, функция также return 0
то есть (cases:1,2,3,4)
,
Но тогда в case 4
Я хочу, чтобы иметь возможность обнаружить его внутри функции, так что, как вы сказали, бросьте err
… или вернуть внеполосные данные, используя пару<> или вне переменной.
Или, чтобы быть более ясным, я хочу обнаружить:
если is >> num returns 0 (ex:"0","0.0","000", "0sad","asd0ss")
действительно ли это НОЛЬ или это строка, которая не подлежит преобразованию в число I.E. различать 0-строку и не-0-строку
PS>
Моя путаница, вероятно, также возникает, потому что я не уверен, какие случаи 0-in-a-string интерпретируются как 0-num или just-string при преобразовании.
Неужели я запутал еще больше 🙂 или сейчас стало понятнее?
Я хочу реализовать что-то в духе семантики Perl.
РЕДАКТИРОВАТЬ 2: Спасибо за все примеры того, как точно вернуть данные за пределами, я действительно ценю их .. Я имею в виду действительно …
(Я, вероятно, буду использовать пару<>, не хочу использовать повышение или C++11
семантики пока нет).
Но меня больше интересовало «что такое струнный поток 0-string
а также non-0-string
а как обнаружить разницу?
Ваша функция преобразования строк плохая, и вы должны чувствовать себя плохо.
template<typename F> int str2num(const string& str, F&& f) {
stringstream is(str);
int result;
if (is >> result) return result;
return f();
}
int str2num(const string& str) {
return str2num(str, [] -> int {
throw std::runtime_error("Parse failure!");
});
}
int str2num(const string& str, int def) {
return str2num(str, [=] {
return def;
});
}
В качестве магического значения по умолчанию выбрано 0 — возможно, 32 — нормальное значение по умолчанию для тех, кто хочет попытаться разобрать целое число. 0 также может быть значимым значением отдельно от ошибки разбора. Если вы хотите использовать значение по умолчанию, вам нужно, чтобы пользователь явно указал его, потому что ваша функция случайного ввода-вывода имеет без понятия что значимым может быть значение по умолчанию.
Если пользователь не предоставляет явную стратегию обработки ошибок, будь то в форме функции обработки ошибок или значимого значения по умолчанию, то исключение является единственным способом.
Довольно легко проверить, успешно ли вы что-то конвертировали. Различить ваш третий и четвертый случаи будет немного сложнее, хотя трактовать «нет данных для преобразования» как «успешно преобразованных что-то» не кажется (мне), хотя и имеет большой смысл. Если вы можете рассматривать случаи 3 и 4 как неудачные преобразования, а остальные — как успешные, это довольно просто: просто проверьте состояние потока после попытки преобразования:
#include <sstream>
#include <string>
#include <iostream>
int main(){
char const *inputs[] = { "0", "0dasd", "", "some string", "345"};
for (int i=0; i<5; i++) {
std::istringstream buf(inputs[i]);
int val;
if (buf>>val)
std::cout << "Converted : \"" << inputs[i] << "\" To: " << val << "\n";
else
std::cout << "Could not convert: \"" << inputs[i] << "\" To int\n";
}
return 0;
}
Который производит:
Converted : "0" To: 0
Converted : "0dasd" To: 0
Could not convert: "" To int
Could not convert: "some string" To int
Converted : "345" To: 345
Если вы действительно хотите относиться к случаю 3 как к успешному, я думаю, не потребовалось бы много времени, чтобы добавить специальную обработку для пустой строки, которая всегда успешно конвертируется.
Вам необходимо вернуть два бита информации: одно целое число с результатом преобразования и один флаг, указывающий успех или нет. Условие ошибки также можно обработать, вызвав исключение, но обычно внешние данные никогда не бывают «исключительными», и ошибки синтаксического анализа должны рассматриваться как обычный поток управления, а не как исключение.
Результат будет выглядеть примерно так:
template <typename T>
boost::optional<int> parse_as(std::string const & s)
{
if (s.empty()) { return T(); }
T result;
return std::istringstream(s) >> result ? result : boost::none;
}
Использование: auto n = parse_as<int>(str);
и проверьте, установлен ли результат.
Альтернативное извлечение токена, требующее совпадения всей строки:
std::istringstream iss(s);
return iss >> result >> std::ws && iss.get() == EOF ? result : boost::none;
Неясно, как вы хотите сообщить о результате: функции извлечения устанавливают поток в плохое состояние, если они не могут выполнить преобразование. Преобразование завершается неудачно для целых чисел, если никакая цифра не может быть расширена. То есть, похоже, у вас есть три случая:
empty()
и вы вернетесь 0
,Спасибо всем, кто прокомментировал.
Вот что я имел в виду:
#include <iostream>
#include <string>
#include <sstream>
#include <assert.h>
using namespace std;
#define pBool std::pair<bool,int>
pBool str2num(const string& str) {
istringstream is(str);
pBool rv(false,0);
if (str == "0E0") {//Zero but true
rv = std::make_pair(true,0);
} else {
is >> rv.second;//convert
//cout << "fail?:" << is.fail() << endl;
//logical XOR : !fail != !0?
if ( !is.fail() != (rv.second == 0) ) rv.first = true;
// if (!is.fail() && rv.second != 0) rv.first = true;//successful conversion
// if (is.fail() && rv.second == 0) rv.first = true;
};
return rv;
};
template<class T>
void dump_pair(T& p) {
cout << "Bool:" << p.first << endl;
cout << "Val :" << p.second << endl;
}
int main() {
cout << ">>0E0" << endl;
pBool rv1 = str2num("0E0");
dump_pair(rv1);
cout << ">>0" << endl;
pBool rv2 = str2num("0");
dump_pair(rv2);
cout << ">>0.0dsad" << endl;
pBool rv3 = str2num("0dsad");
dump_pair(rv3);
cout << ">>456ttt" << endl;
pBool rv4 = str2num("456ttt");
dump_pair(rv4);
cout << ">>adttt" << endl;
pBool rv5 = str2num("ad555ttt");
dump_pair(rv5);
return 0;
}
====OUTPUT====
>>0E0
Bool:1
Val :0
>>0
Bool:0
Val :0
>>0.0dsad
Bool:0
Val :0
>>456ttt
Bool:1
Val :456
>>adttt
Bool:1
Val :0