Подтвердить что ты не робот

Более безопасная, но простая в использовании и гибкая альтернатива С++ для s ++()

Когда мне нужно сканировать значения из нескольких строк, я часто оказываюсь обратно на C sscanf() строго из-за его простоты и простоты использования. Например, я могу очень кратко вывести пару двойных значений из строки с помощью:

string str;
double val1, val2;
if (sscanf(str.c_str(), "(%lf,%lf)", &val1, &val2) == 2)
{
    // got them!
}

Это, очевидно, не очень С++. Я не обязательно считаю это мерзостью, но я всегда ищу лучший способ сделать общую задачу. Я понимаю, что для чтения строк "С++ way" является istringstream, но дополнительная набрав, необходимая для обработки скобок и запятой в строке формата выше, просто делает ее слишком громоздкой, чтобы заставить меня использовать ее.

Есть ли хороший способ либо сгибать встроенные средства по моей воле так, как это было выше, либо есть ли хорошая библиотека С++, которая делает это более безопасным способом? Похоже, Boost.Format действительно решила проблему вывода в хорошем смысле, но я не нашел ничего подобного для ввода.

4b9b3361

Ответ 1

Я написал немного кода, который можно читать в строковых и символьных литералах. Как нормальный поток читает, если он получает недопустимые данные, он устанавливает битбит потока. Это должно работать для всех типов потоков, включая широкие потоки. Вставьте этот бит в новый заголовок:

#include <iostream>
#include <string>
#include <array>
#include <cstring>

template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e(&sliteral)[N]) {
        std::array<e, N-1> buffer; //get buffer
        in >> buffer[0]; //skips whitespace
        if (N>2)
                in.read(&buffer[1], N-2); //read the rest
        if (strncmp(&buffer[0], sliteral, N-1)) //if it failed
                in.setstate(in.rdstate() | std::ios::failbit); //set the state
        return in;
}
template<class e, class t>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, const e& cliteral) {
        e buffer;  //get buffer
        in >> buffer; //read data
        if (buffer != cliteral) //if it failed
                in.setstate(in.rdstate() | std::ios::failbit); //set the state
        return in;
}
//redirect mutable char arrays to their normal function
template<class e, class t, int N>
std::basic_istream<e,t>& operator>>(std::basic_istream<e,t>& in, e(&carray)[N]) {
        return std::operator>>(in, carray);
}

И это сделает входные символы очень легкими:

std::istringstream input;
double val1, val2;
if (input >>'('>>val1>>','>>val2>>')') //less chars than scanf I think
{
    // got them!
}

ДОКАЗАТЕЛЬСТВО КОНЦЕПЦИИ. Теперь вы можете cin строковые и символьные литералы, и если вход не является точным совпадением, он действует так же, как и любой другой тип, который не смог правильно ввести. Обратите внимание, что это соответствует только пробелам в строковых литералах, которые не являются первым символом. Это всего лишь четыре функции, все из которых просты в мозге.

ИЗМЕНИТЬ

Анализ с потоками - плохая идея. Используйте регулярное выражение.

Ответ 2

Лучшее, что я когда-либо использовал для синтаксического анализа строк, - boost.spirit. Он быстрый, безопасный и очень гибкий. Большим преимуществом является то, что вы можете писать правила синтаксического анализа в форме, близкой к грамматике EBNF

using namespace boost::spirit;

boost::fusion::vector < double, double > value_;

std::string string_ = "10.5,10.6 ";

bool result_ = qi::parse(
    string_.begin(),
    string_.end(),
    qi::double_ >> ',' >> qi::double_, // Parsing rule
    value_); // value

Ответ 3

Я думаю, что с регулярным выражением это можно сделать легко. Поэтому boost:: regex или std:: regex в новом стандарте. После этого просто конвертируйте свои токены в float, используя непосредственно lexical_cast или потоки.