Как использовать библиотеку boost lexical_cast для проверки ввода - PullRequest
2 голосов
/ 12 февраля 2010

Я часто использую библиотеку boost lexical_cast для разбора текстовых данных в числовые значения. Однако в некоторых ситуациях мне нужно только проверить, являются ли значения числовыми; Мне на самом деле не нужно и не нужно использовать преобразование.

Итак, я думал о написании простой функции для проверки, является ли строка двойной:

template<typename T> 
bool is_double(const T& s)
{
  try 
  {
    boost::lexical_cast<double>(s); 
    return true;
  }
  catch (...) 
  {
    return false;
  }
}

У меня вопрос: есть ли оптимизирующие компиляторы, которые бы пропускали здесь lexical_cast, так как я никогда не использовал это значение?

Есть ли лучший способ использовать библиотеку lexical_cast для проверки ввода?

Ответы [ 7 ]

3 голосов
/ 13 мая 2015

Теперь вы можете использовать boost::conversion::try_lexical_convert, теперь определенное в заголовке boost/lexical_cast/try_lexical_convert.hpp (если вы хотите только try_lexical_convert). Вот так:

double temp;
std::string convert{"abcdef"};
assert(boost::conversion::try_lexical_convert<double>(convert, temp) != false);
2 голосов
/ 12 февраля 2010

Я думаю, что вы хотите немного переписать эту функцию:

template<typename T>  
bool tryConvert(std::string const& s) 
{ 
    try         { boost::lexical_cast<T>(s);} 
    catch (...) { return false; }

    return true; 
} 
2 голосов
/ 12 февраля 2010

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

Попытка сделать lexical_cast может быть неоптимальной с точки зрения производительности, но если вы не проверите миллионы значений таким образом, беспокоиться не о чем.

1 голос
/ 12 февраля 2010

Вы можете попробовать что-то вроде этого.

#include <sstream>

//Try to convert arg to result in a similar way to boost::lexical_cast
//but return true/false rather than throwing an exception.
template<typename T1, typename T2>
bool convert( const T1 & arg, T2 & result )
{
    std::stringstream interpreter;
    return interpreter<<arg && 
           interpreter>>result && 
           interpreter.get() == std::stringstream::traits_type::eof();
}

template<typename T>
double to_double( const T & t )
{
   double retval=0;
   if( ! convert(t,retval) ) { /* Do something about failure */ }
   return retval;
}

template<typename T>
double is_double( const T & t )
{
   double retval=0;
   return convert(t,retval) );
} 

Функция convert в основном выполняет те же функции, что и boost :: lexical_cast, за исключением того, что лексическое приведение более тщательно избегает выделения динамического хранилища с помощью фиксированных буферов и т. Д.

Можно было бы реорганизовать код boost :: lexical_cast в эту форму, но этот код довольно плотный и сложный - ИМХО жаль, что lexical_cast не был реализован с использованием чего-то подобного под капотом ... это может выглядеть так:

template<typename T1, typename T2>
T1 lexical_cast( const T2 & t )
{
  T1 retval;
  if( ! try_cast<T1,T2>(t,retval) ) throw bad_lexical_cast();
  return retval;
}
0 голосов
/ 12 февраля 2010

Поскольку тип T является шаблонным типом имени, я считаю, что ваш ответ правильный, так как он сможет обрабатывать все случаи, уже обработанные boost :: lexical_cast.

Тем не менее, не забудьте специализировать функцию для известных типов, таких как char *, wchar_t *, std::string, wstring и т. Д.

Например, вы можете добавить следующий код:

template<>
bool is_double<int>(const int & s)
{
   return true ;
}

template<>
bool is_double<double>(const double & s)
{
   return true ;
}

template<>
bool is_double<std::string>(const std::string & s)
{
   char * p ;
   strtod(s.c_str(), &p) ; // include <cstdlib> for strtod
   return (*p == 0) ;
}

Таким образом, вы можете «оптимизировать» обработку для известных вам типов и делегировать оставшиеся случаи для boost :: lexical_cast.

0 голосов
/ 12 февраля 2010

Лучше сначала использовать регулярные выражения и lexical_cast просто для преобразования в реальный тип.

0 голосов
/ 12 февраля 2010

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

if ( s.empty() ) return false;
string::const_iterator si = s.begin();
if ( *si == '+' || * si == '-' ) ++ si;
if ( si == s.end() ) return false;
while ( '0' <= *si && *si <= '9' && si != s.end() ) ++ si;
if ( si == s.end() ) return true;
if ( * si == '.' ) ++ si;
if ( ( * si == 'e' || * si == 'E' )
 && si - s.begin() <= 1 + (s[0] == '+') + (s[0] == '-') ) return false;
if ( si == s.end() ) return si - s.begin() > 1 + (s[0] == '+') + (s[0] == '-');
while ( '0' <= *si && *si <= '9' && si != s.end() ) ++ si;
if ( si == s.end() ) return true;
if ( * si == 'e' || * si == 'E' ) {
    ++ si;
    if ( si == s.end() ) return false;
    if ( * si == '-' || * si == '+' ) ++ si;
    if ( si == s.end() ) return false;
    while ( '0' <= *si && *si <= '9' && si != s.end() ) ++ si;
}
return si == s.end();

Не проверено ... Я позволю вам просмотреть все возможные комбинации форматов; v)

Редактировать: Также обратите вниманиечто это совершенно несовместимо с локализацией.У вас абсолютно нет надежды на международную проверку без конвертации.

Редактировать 2: Упс, я думал, что кто-то другой уже предложил это.boost::lexical_cast на самом деле обманчиво прост.Чтобы, по крайней мере, избежать создания или перехвата исключения, вы можете переопределить его несколько:

istringstream ss( s );
double d;
ss >> d >> ws; // ws discards whitespace
return ss && ss.eof(); // omit ws and eof if you know no trailing spaces

Этот код, с другой стороны, был проверен; v)

...