Конвертируйте специально отформатированное число в long / double в C ++ - PullRequest
2 голосов
/ 01 ноября 2011

Я пишу переводчик для языка программирования, и я делаю это на языке C ++, я откровенно новичок в этом деле.

Что я пытаюсь сделать, это конвертировать определенный формат с плавающей точкой в ​​std::string удвоить (или что-то еще). Я хочу, чтобы он был полностью независим от локали и был максимально устойчивым.

У меня есть два случая:

  • целые числа : они должны быть смежными цифрами от 0-9 с или без начального знака минус (знак плюс не допускается, допускаются начальные нули)
  • числа с плавающей точкой : [целая часть]. [Десятичная часть] с или без начального минуса и без каких-либо разделителей тысяч. Любая часть или десятичная часть могут быть пропущены (например, .4 или 4.), но не оба

Я бы хотел, чтобы это был "способ C ++". Есть ли функция, которую я мог бы использовать для указания пользовательских числовых форматов (вроде как date в PHP).

Буду очень признателен за любой предоставленный указатель или фрагмент кода. Спасибо!

Ответы [ 3 ]

2 голосов
/ 01 ноября 2011

Я не знаю о поддержке iostreams для строгого форматирования ввода.

Однако вы можете использовать Boost Spirit:

стандартные реальные парсеры с RealPolicy

См. http://www.boost.org/doc/libs/1_47_0/libs/spirit/doc/html/spirit/qi/reference/numeric/real.html

Это позволит вам явно определить формат (ы), принятые для показателей степени, знаков и любых (тысяч) разделителей. Это также довольно сложный подход, но он очень быстрый и очень гибкий (работает и для нестандартных числовых типов, IIRC).

Двухфазный анализ

Вы можете использовать любое из правил Spirit Qi для указания точного формата и передавать входную последовательность raw[] стандартным числовым анализаторам только в том случае, если она соответствует вашим требованиям.

Более сложным, но и более оптимальным способом было бы использовать Spirit Lexer для токенизации входных данных - эффективно делать то же самое, но более эффективно.

Середина земли

Срединной точкой здесь было бы использование простого старого (Posix | Perl | C ++ 11 | Boost) регулярного выражения для проверки входного формата и передачи его любому подходящему преобразованию (например, Boost Lexical cast или просто std :: stringstream >> double и т. д.)

Пример , показывающий предварительное сопоставление Spirit Qi и regex на работе при разборе числового формата для чисел с плавающей запятой (язык c ++ 0x 1 )

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/regex.hpp>

namespace qi=boost::spirit::qi;
namespace phx=boost::phoenix;

bool is_ok(const std::vector<char>& raw)
{
    static const boost::regex rx(R"(-?(\d*\.\d+|\d+\.\d*))");
    return boost::regex_match(raw.begin(), raw.end(), rx);
}

template <typename Input>
    void test(const Input& input)
{
    auto f(std::begin(input)), l(std::end(input));

    double parsed = 0;
    bool ok = qi::phrase_parse(f, l, 

       // this is the parser expression
       &(+qi::char_)[ qi::_pass = phx::bind(is_ok, qi::_1) ]
       >> qi::double_, 
       // end parser expression

       qi::space, parsed);

    std::cout << "DEBUG: '" << input << "'\t"  << std::boolalpha << ok << "\t" << parsed << std::endl;
}

int main()
{
    const std::string good[]  = { ".300", "300.", "-.4", "-4." };
    const std::string wrong[] = { "", ".", "1", "-1", "-1111", "3..", "+1", "+.1", "+1.", "+1.0", "+-2.", "-+2." };

    for (auto& input : good)
        test(input);

    for (auto& input : wrong)
        test(input);
}

1 с использованием c ++ 11 feat:

  • на основе диапазона для
  • необработанные строковые литералы для спецификации регулярных выражений
0 голосов
/ 01 ноября 2011

целые числа: они должны быть смежными цифрами от 0-9 с или без начального знака минус (без знака плюс, допускаются начальные нули)

числа с плавающей точкой: [вся часть].[десятичная часть] с или без начального минуса и без каких-либо разделителей тысяч.Любая часть или десятичная часть могут быть пропущены (например, .4 или 4.), но не оба

Это вряд ли "пользовательские форматы";они могут быть превосходно проанализированы с помощью stringstream (или, если вы используете BOOST, lexical_cast).

#include <iostream>
#include <string>
#include <sstream>

int main( ... ) {   
    std::string s = "-1.0";
    float f = 0;        
    if( std::stringstream(s) >> f ) {
        std::cout << f;
    }
    else {
        std::cout << "No good!";
    }

    return 0;
}
0 голосов
/ 01 ноября 2011

Если вы подразумеваете, что строки должны быть в C локали:

template<class T>
std::string tostring(const T& input)
{
    stringstream ss;
    if (!(ss << input))
        throw std::runtime_error("cannot convert!");
    return ss.str();
}

template<class T>
void fromstring(const std::string& input, T& output)
{
    stringstream ss(input);
    if (!(ss >> output) || ss)
        throw std::runtime_error("cannot convert!");
}
//Passes output as parameter, in case it's not copiable.

int main() {

    float pi = 3.14159f;  //pi to string and back
    std::string strpi = tostring(pi);
    fromstring(strpi, pi);

    std::ifstream in("in.txt");  //copies a file through a string
    std::string file = tostring(in);
    std::ofstream out("out.txt");
    fromstring(file, out);

    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...