Каждому в своей программисткой практике приходилось сталкиваться с ситуациями, когда необходимо было что-либо преобразовать из строки в, предположим, число, или обратно. В зависимости от того, что, куда и как преобразовывается, для этого можно применять sprintf/sscanf, _itoa/atoi (и другие функции из этой группы), strtoi, и т. п. Все эти методы по своему хороши, но для каждого варианта преобразования необходимо помнить - «а что лучше всего для этого преобразования подходит?»
Стандарт языка C/C++ предусматривает ряд средств для выполнения таких преобразований. Однако они различаются по простоте использования, расширяемости и безопасности.
К примеру, существует ряд ограничений для стандартной для языка C функции atoi:
- Преобразование выполняется только в одном направлении: из текстовой формы во внутренний тип данных. Обратное преобразование с использованием стандартных функций C требует либо использования неудобной и не совсем безопасной функции sprintf, или потери платформонезависимости при использовании таких функций, как itoa.
- Набор поддерживаемых типов является лишь подмножеством встроенных числовых типов, а именно int, long, и double.
- Этот набор типов не может быть расширен однородным образом. К примеру, преобразование из строкового представления в complex или rational.
Стандартная функция C strtol имеет те же же основные ограничения, хотя предлагает более аккуратный контроль за процессом преобразования. Однако, в общем случае такой контроль или не требуется, или не используется. Семейство функций scanf предоставляет еще больший контроль, однако им не хватает надежности и простоты использования.
Стандартная библиотека языка C++ содержит stringstream как возможную основу для обсуждаемого форматирования с преобразованием. Она предлагает удобный набор средств для контроля за форматированием и преобразованием I/O из и в произвольный тип через текст. Для простых преобразований непосредственное использование stringstream может быть неудобным (или введения дополнительных локальных переменных и невозможности использования удобной инфиксной формы выражений) или неочевидным и туманным (когда объекты типа stringstream создаются как временные объекты в выражении). Локаль (facets) содержит приемлемый набор средств для контроля за представлением в текстовом виде, однако сложность кода приводит к тому, что использовать эти средства удобно только небольшму количеству программистов.
Шаблон функции lexical_cast предлагает удобный и целостный подход для поддержки распространенных преобразований в и из произвольных типов, когда они представлены в виде текста. Предлагаемое ей упрощение заключается в использовании удобстве применения в выражениях. Для более сложных преобразований, когда, к примеру, требуется контролировать точность, вместо lexical_cast лучше использовать удобный подход stringstream. Для преобразования одного числа в другое вместо lexical_cast рекомендуется использовать numeric_cast.
Детальное обсуждение всех опций и вопросов, возникающих при форматировании строк, включая сравнение stringstream, lexical_cast, и других средств, можно найти в статье Херба Саттера (Herb Sutter) The String Formatters of Manor Farm.
boost::lexical_cast позволяет снять эту проблему. Для его использования достаточно знать - какой тип в какой вы ходите преобразовать. И все. Например:
// преобразует целое в строку std::string str = boost::lexical_cast<std::string>(10); // строку в целое int value = boost::lexical_cast<int>("10"); // преобразует пару целых в экземпляр класса Point Point pt = boost::lexical_cast<Point>("10, 20"); // обратное преобразование std::wstring wstr = boost::lexical_cast<std::wstring>(pt); // и т. д.
Достаточно удобно, не правда ли?
Принципы работы.
Принцип работы boost::lexical_cast очень прост. Для преобразования он использует строковый поток (std::strstream), выводя (с помощью оператора «) в него преобразуемое значние, после чего читая из него значение типа, в который делается преобразование. Т. е. вызов boost::lexical_cast эквивалентен:
// положим, что from_val и to_val - это, соответственно преобразуемое // значение и приемник результата преобразования std::ostringstream o_str; o_str << from_val std::istringstream i_str(o_str.str()); i_str >> to_val;
Из этого становится очевидным, что для корректного выполнения преобразования необходимо (и достаточно) наличия для преобразуемого типа перегруженного оператора вывода в поток и/или чтения из потока. Для всех примитивных типов такие операторы реализованы в библиотеке STL, а для «пользовательских» типов такой оператор может написать сам разработчик.
Краткое описание
Определенные в хидере "boost/lexical_cast.hpp" части библиотеки:
namespace boost { class bad_lexical_cast; template <typename Target, typename Source> Target lexical_cast(Source arg); }
Тестовая программа - в файле lexical_cast_test.cpp.
lexical_cast
template <typename Target, typename Source> Target lexical_cast(Source arg);
Функция возвращает результат передачи значение arg в стандартный строковый поток и обратного преобразования в объект типа Target. Когда тип Target это std::string или std::wstring, извлечение из потока получает полное содержимое строки, включая пробелы, вместо того, чтобы использовать operator>> по умолчанию. Если преобразование дает ошибку, то генерируется исключение bad_lexical_cast.
Требования к аргументу и возвращаемому значению:
- Source является OutputStreamable, что значает, что определен operator<<, который получает объект типа std::ostream или std::wostream слева и объект типа аргумента справа.
- Target является InputStreamable, что означает, что определен operator>>, который берет объект типа std::istream или std::wistream слева (left hand side) и объект возвращаемого типа справа.
- Source и Target следуют парадигме CopyConstructible [20.1.3].
- Target следует парадигме DefaultConstructible, что означает, что возможна инифиализация по умолчанию объекта этого типа [8.5, 20.1.4].
Символьный тип используемого потока должен быть char, если только Source или Target не требуют использования потоков с wchar_t. Типы Source, которые требую использования wchar_t-потоков это wchar_t, wchar_t *, и std::wstring. Типы Target, которые требуют использования wchar_t-потоков это wchar_t и std::wstring.
Если требуется более высокий уровень контроля за преобразованием, лучше использовать std::stringstream и std::wstringstream. Когда требуется выполнить преобразования без использования потоков, lexical_cast является неподходящим средством.
bad_lexical_cast
class bad_lexical_cast : public std::bad_cast { { public: ... // same member function interface as std::exception };
Для сообщений об ошибках преобразования в lexical_cast используется генерация исключений.
Недостатки
Но не смотря на свою красоту, универсальность и соответствие общепринятым стандартам преобразования, у boost::lexical_cast есть и следущие недостатки:
- Низкая скорость работы. По тестам, проведенным Гербом Саттером, результаты которых он описал в своих «Новых сложных задачах на С++», boost::lexical_cast работает на порядок медленнее, чем тот же sprintf.
- При преобразованиях нельзя специфицировать формат желаемой или исходной строки. Т. е. преобразования выполняются в соответствии со стандартными настройками потоков.
- (для компилятора Visual C++) нельзя (без дополнительных телодвижений) выполнять преобразования в std::wstring/wchar_t*, если в настройках проекта не указано, что wchar_t считается встроенным типом.
- приведение в тип double из строкового типа, возможно лишь в том случае если в качестве разделителя (дробной части от целой) используется точка.
Достоинства
Два из трех указанных выше недостатков достаточно легко обходятся. Поскольку boost::lexical_cast - это шаблон, то достаточно несложно написать необходимую специализацию, выполняющую преобразование настолько быстро, насколько это необходимо разработчику, а также учитывающую особенности типа wchar_t в VC++.
Изменения
- Предыдущая версия шаблона lexical_cast использовала установки точности в потоках по умолчанию для чтения и записи чисел с плавающей запятой. Для числовых типов есть соответствующая специализация в std::numeric_limits, а текущая версия шаблона преобразования соответствующую точность.
- Предыдущая версия шаблона lexical_cast не поддерживала преобразование в или из типов на основе wchar_t. Для компиляторов, которые имеют полную поддержку для wchar_t, lexical_cast теперь поддерживает преобразование из wchar_t, wchar_t *, и std::wstring и в wchar_t и std::wstring.
- Предыдущая версия шаблона exical_cast основывалась на предположении, что обычные операторы извлечения из потока (stream extractor operators) достаточны для чтения значений. Однако, строковый ввод/вывод асимметричен, так как пробелы играют роль разделителей, а не входят в состав строк. Текущая версия исправляет эту ошибку для std::string и, где возможно, std::wstring: lexical_cast<std::string>("Hello, World") выполняется успешно вместо ошибки с генерацией исключения bad_lexical_cast.
- Предыдущая версия шаблона lexical_cast допускала небезопасное и бессмысленное преобразование к указателям. Текущая версия генерирует исключение bad_lexical_cast для преобразований в указатель: lexical_cast<char*>("Goodbye, World") генерирует исключение вместо неопределенного поведения в предыдущей версии.
Пример использования.
Следующий пример преобразует аргументы командной строки в последовательность чисел:
int main(int argc, char * argv[]) { using boost::lexical_cast; using boost::bad_lexical_cast; std::vector<short> args; while(*++argv) { try { args.push_back(lexical_cast<short>(*argv)); } catch(bad_lexical_cast &) { args.push_back(0); } } ... }
Приведенный далее пример использует числовые величины в строковой выражении:
void log_message(const std::string &); void log_errno(int yoko) { log_message("Error " + boost::lexical_cast<std::string>(yoko) + ": " + strerror(yoko)); }
Приведенный далее пример переводит числовые величины в строковое представление:
#include <string> #include <iostream> #include <boost/lexical_cast.hpp> int _tmain(int argc, _TCHAR* argv[]) { try { int a(41); std::string b; b = boost::lexical_cast<std::string>(a); std::cout << b << std::endl; } catch (const std::exception& exc) { std::cout << exc.what() << std::endl; } return 0; }
P.S.: Нагло спёрто отсюда [[doc:cpp:boost:lexical_cast]] и отсюда библиотека преобразований - хидер boost/lexical_cast.hpp
P.P.S.: Об эффективности boost::lexical_cast<> можно прочитать здесь: http://www.rsdn.ru/forum/flame.comp/2986431.flat.1.aspx
Спасибо, отличная статья, помогла новичку С++ понять сходу, что за зверь этот boost::lexical_cast
ОтветитьУдалитьАнонимный: пожалуйста!
ОтветитьУдалитьИ через три года эта статья хороша, почему новых нет?
ОтветитьУдалитьХерня програмист програмист програмист все код пишет а хакнули комп и унесли байты все бля ищи на хуй теперь
ОтветитьУдалить#include
ОтветитьУдалитьusing std::string;
#define _my_ntostring(n) string(#n,sizeof(#n)-1)
inline string my_itostring(int n) {(void)n;return _my_ntostring(n);}
Неужели сам придумал? Или это из теста на знание плюсов?
Удалитьmy_itostring() будет всегда выдавать строку "n", обломись.
Grand Casino Hotel, Tunica - Mapyro
ОтветитьУдалитьGrand Casino 아산 출장마사지 Hotel, Tunica is a hotel and 원주 출장샵 casino located in Tunica Resorts, 천안 출장마사지 Mississippi, United States and is open 제천 출장샵 daily 24 hours. Rating: 7.1/10 · 2,821 충청북도 출장마사지 votes · Price range: $$