Я имею в формат std::string
с sprintf
и отправить его в файловый поток. Как я могу это сделать?
c++
string
formatting
stdstring
c++-standard-library
Макс Фрай
источник
источник
boost::format
(как решение Kennytm использует здесь ).boost::format
уже поддерживает потоковые операторы C ++ тоже! Пример:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
имеет наименьшее количество строк кода ... рецензируется и прекрасно интегрируется с потоками C ++.std::format
был добавлен в C ++ 20 Кстати: stackoverflow.com/a/57286312/895245 Круто!C++20
вчерашнем дне и увидел, что онаC++20
скопированаboost
(в миллионный раз), добавивstd::format
вC++20
спецификацию! Я был очень, очень счастлив! Почти все файлы C ++, которые я написал за последние 9 лет, были использованыboost::format
. Добавление официального вывода в стиле printf к потокам в C ++ будет иметь большое значение для всего C ++.Ответы:
Вы не можете сделать это напрямую, потому что у вас нет прав на запись в базовый буфер (до C ++ 11; см. Комментарий Дитриха Эппа ). Сначала вам нужно будет сделать это в c-строке, а затем скопировать в std :: string:
Но я не уверен, почему бы вам не использовать поток строк? Я предполагаю, что у вас есть конкретные причины не просто делать это:
источник
char buf[100];
делает это решение не очень надежным. Но основная идея есть.asprintf
, что выделяет новую строку с достаточным пространством для хранения результата. Затем скопируйте это,std::string
если хотите, и запомнитеfree
оригинал. Кроме того, можно поместить это в макрос, чтобы любой хороший компилятор помог вам проверить формат - вы не хотите помещать туда,double
где%s
ожидаетсяСовременный C ++ делает это очень просто.
C ++ 20
C ++ 20 вводит
std::format
, что позволяет вам делать именно это. Он использует поля замены, аналогичные тем, что есть в python :Ознакомьтесь с полной документацией ! Это огромное улучшение качества жизни.
C ++ 11
С C ++ 11 с
std::snprintf
, это уже стало довольно легко и безопасно задачей.Вышеприведенный фрагмент кода распространяется под лицензией CC0 1.0 .
Построчное объяснение:
Цель: написать
char*
с помощьюstd::snprintf
а затем преобразовать это вstd::string
.Сначала мы определяем желаемую длину массива char с помощью специального условия в
snprintf
. С cppreference.com :Это означает, что желаемый размер равен числу символов плюс один , так что нулевой терминатор будет располагаться после всех других символов и что он может быть снова обрезан конструктором строки. Эта проблема была объяснена @ alexk7 в комментариях.
snprintf
вернет отрицательное число, если произошла ошибка, поэтому мы проверим, работало ли форматирование должным образом. Невыполнение этого требования может привести к тихим ошибкам или выделению огромного буфера, как указано @ead в комментариях.Затем мы выделяем новый массив символов и назначаем его для
std::unique_ptr
. Обычно это рекомендуется, так как вам не придется делатьdelete
это вручную .Обратите внимание, что это небезопасный способ выделения
unique_ptr
с пользовательскими типами, так как вы не можете освободить память, если конструктор выдает исключение!После этого мы, конечно, можем просто использовать
snprintf
его по назначению и записать отформатированную строку вchar[]
.Наконец, мы создаем и возвращаем новое
std::string
из этого, удостоверяясь, что в конце опускается нулевой терминатор.Вы можете увидеть пример в действии здесь .
Если вы также хотите использовать
std::string
список аргументов, взгляните на эту суть .Дополнительная информация для пользователей Visual Studio :
Как объясняется в этом ответе , Microsoft переименована
std::snprintf
в_snprintf
(да, безstd::
). Кроме того, MS устанавливает его как устаревшее и рекомендует использовать_snprintf_s
вместо него, однако_snprintf_s
не примет буфер равным нулю или меньшему, чем форматированный вывод, и не вычислит длину выходных данных, если это произойдет. Таким образом, чтобы избавиться от предупреждений об устаревании во время компиляции, вы можете вставить следующую строку вверху файла, которая содержит использование_snprintf
:Последние мысли
Многие ответы на этот вопрос были написаны до C ++ 11 и используют фиксированную длину буфера или vargs. Если вы не застряли в старых версиях C ++, я бы не рекомендовал использовать эти решения. В идеале идти по пути C ++ 20.
Поскольку решение C ++ 11 в этом ответе использует шаблоны, оно может генерировать довольно много кода, если его часто использовать. Однако, если вы не разрабатываете для среды с очень ограниченным пространством для двоичных файлов, это не будет проблемой, и все равно будет значительным улучшением по сравнению с другими решениями как в отношении ясности, так и безопасности.
Если эффективность использования пространства очень важна, эти два решения с помощью vargs и vsnprintf могут быть полезны. НЕ ИСПОЛЬЗУЙТЕ какие-либо решения с фиксированной длиной буфера, которые просто вызывают проблемы.
источник
return string(&buf[0], size);
или что-то подобное. Во-вторых, если бы вы возвращали c-строку подобным образом, это вызвало бы неопределенное поведение, потому что вектор, содержащий значения, на которые вы указываете, будет недействительным при возврате. В-третьих, когда я начал изучать C ++, стандарт не определял, в каком порядке элементы должны храниться внутриstd::vector
, поэтому доступ к его хранилищу через указатель был неопределенным поведением. Теперь это будет работать, но я не вижу никакой выгоды в этом.std::string
будет создан из неявно преобразованного вектора ( инициализация копии ), который затем возвращается как копия, как предполагает сигнатура функции. Кроме того, элементы astd::vector
хранятся и должны были храниться непрерывно . Но я понимаю, что в этом нет никакой пользы.return string(buf.get(), buf.get() + size);
должна быть,return string(buf.get(), buf.get() + size - 1);
иначе вы получите строку с нулевым символом в конце. Я обнаружил, что это относится к gcc 4.9.C ++ 11 решение, которое использует
vsnprintf()
внутренне:Более безопасный и эффективный (я это проверил, и он быстрее) подход:
fmt_str
Передается по значению , чтобы соответствовать требованиямva_start
.ПРИМЕЧАНИЕ. «Более безопасная» и «более быстрая» версия не работает на некоторых системах. Следовательно, оба все еще перечислены. Кроме того, «быстрее» полностью зависит от правильности шага предраспределения, в противном случае
strcpy
он отображается медленнее.источник
size
на каждой итерации, когда вы можете получить ее при первом вызовеvsnprintf()
.boost::format()
обеспечивает функциональность, которую вы хотите:Как из краткого обзора библиотек формата Boost:
источник
C ++ 20 будет включать в себя то,
std::format
что похожеsprintf
на API, но полностью типобезопасно, работает с пользовательскими типами и использует синтаксис строки формата, подобный Python. Вот как вы сможете отформатироватьstd::string
и записать его в поток:или
В качестве альтернативы, вы можете использовать библиотеку {fmt} для форматирования строки и записи в нее
stdout
или файлового потока за один раз:Что касается
sprintf
или большинства других ответов здесь, к сожалению, они используют переменные и по своей сути небезопасны, если вы не используете что-то вродеformat
атрибута GCC, который работает только со строками литерального формата. Вы можете увидеть, почему эти функции небезопасны на следующем примере:где
string_format
реализация из ответа Эрика Аронести. Этот код компилируется, но, скорее всего, он потерпит крах при попытке его запустить:Отказ от ответственности : я являюсь автором {fmt} и C ++ 20
std::format
.источник
error: 'fmt' has not been declared
fmt
реализация была добавлена в C ++ 20! stackoverflow.com/a/57286312/895245 fmt в настоящее время заявляет о поддержке его. Отличная работа!Если вам нужен только printf-подобный синтаксис (без вызова printf самостоятельно), взгляните на Boost Format .
источник
Я написал свой собственный, используя vsnprintf, поэтому он возвращает строку вместо того, чтобы создавать свой собственный буфер.
Таким образом, вы можете использовать его как
источник
vsnprintf
непосредственно в строку.std::unique_ptr<char[]> buffer (new char[size]);
Чтобы выполнить форматирование
std::string
в стиле sprintf, вызовитеsnprintf
(argumentsnullptr
и0
), чтобы получить необходимую длину буфера. Напишите свою функцию, используя шаблон C ++ 11 variadic следующим образом:Компилировать с поддержкой C ++ 11, например, в GCC:
g++ -std=c++11
Применение:
источник
char buf[length + 1];
вместоchar* buf = new char[length + 1];
?char[]
иchar*
с новым заключается в том, что в первом случае buf будет размещен в стеке. Это нормально для небольших буферов, но поскольку мы не можем гарантировать размер результирующей строки, использовать ее немного лучшеnew
. Например, на моем компьютереstring_sprintf("value: %020000000d",5)
выведите огромное количествоnew char[length + 1]
std::move
это лишнее .[edit: 20/05/25] еще лучше ...:
В шапке:
PRINTSTRING(r)
-Функция для обслуживания графического интерфейса или терминала или любые потребности специального вывода с использованием#ifdef _some_flag_
, по умолчанию:[edit '17 / 8/31] Добавление вариативно-шаблонной версии 'vtspf (..)':
которая по сути является разделенной запятыми версией (вместо) иногда мешающих
<<
операторов, используемых следующим образом:[править] Адаптирован, чтобы использовать технику в ответе Эрика Аронести (выше):
[предыдущий ответ]
Очень поздний ответ, но для тех, кому, как и мне, нравится путь sprintf: я написал и использую следующие функции. Если вам это нравится, вы можете расширить% -опции, чтобы они более близко подходили к sprintf; в настоящее время там достаточно для моих нужд. Вы используете stringf () и stringfappend () так же, как и sprintf. Просто помните, что параметры для ... должны быть типа POD.
источник
fmt
качестве справки он работает нормально. (Но я полагаю, что люди захотят поиграть с игрушками, так что ...) Если есть какие-то другие предполагаемые «ошибки», не могли бы вы уточнить?Вот как это делает Google:
StringPrintf
(Лицензия BSD)и Фейсбук делают это довольно схожим образом:
StringPrintf
(Лицензия Apache)И то, и другое обеспечивает удобство
StringAppendF
.источник
Мои два цента по этому очень популярному вопросу.
Чтобы процитировать man-страницу
printf
-подобных функций :Другими словами, разумная реализация C ++ 11 должна быть следующей:
Работает довольно хорошо :)
Шаблоны Variadic поддерживаются только в C ++ 11. Ответ от pixelpoint показывает похожую технику с использованием более старых стилей программирования.
Странно, что C ++ не имеет такой вещи из коробки. Они недавно добавили
to_string()
, что, на мой взгляд, является большим шагом вперед. Мне интересно, если они добавят.format
оператор вstd::string
конце концов ...редактировать
Как указал alexk7, значение A
+1
необходимо для возвращаемого значенияstd::snprintf
, поскольку нам нужно пространство для\0
байта. Интуитивно, на большинстве архитектур отсутствует+1
приведет к тому ,required
целое число , чтобы быть частично перезаписан с0
. Это произойдет после оценкиrequired
как фактического параметраstd::snprintf
, поэтому эффект не должен быть виден.Однако эта проблема может измениться, например, при оптимизации компилятора: что если компилятор решит использовать регистр для
required
переменной? Это ошибки, которые иногда приводят к проблемам с безопасностью.источник
bytes
буфера, возможно, черезrequired
целое число (которое, к счастью, в этот момент уже вычислено).nullptr
качестве аргумента буфера, исключивchar b;
строку в вашем коде. ( Источник )Использование C99 snprintf и C ++ 11
источник
Проверено, ответ качества продукции
Этот ответ обрабатывает общий случай с помощью методов, соответствующих стандартам. Тот же подход приведен в качестве примера на CppReference.com в нижней части их страницы. В отличие от их примера, этот код соответствует требованиям вопроса и испытан в полевых условиях в робототехнике и спутниковых приложениях. Это также улучшило комментирование. Качество дизайна обсуждается ниже.
Предсказуемая линейная эффективность
Два прохода необходимы для безопасной, надежной и предсказуемой функции многократного использования согласно спецификациям вопроса. Предположение о распределении размеров vargs в многократно используемой функции является плохим стилем программирования и его следует избегать. В этом случае произвольно большие представления переменной переменной vargs имеют ключевое значение при выборе алгоритма.
Повторная попытка при переполнении экспоненциально неэффективна, что является еще одной причиной, обсуждаемой, когда комитет по стандартам C ++ 11 обсуждал вышеупомянутое предложение, чтобы обеспечить пробный запуск, когда буфер записи равен нулю.
В приведенной выше реализации готового производства первый прогон является таким пробным прогоном, чтобы определить размер распределения. Распределение не происходит. Парсинг директив printf и чтение vargs стали чрезвычайно эффективными в течение десятилетий. Повторно используемый код должен быть предсказуемым, даже если нужно пожертвовать небольшой неэффективностью для тривиальных случаев.
Безопасность и Надежность
Эндрю Кениг сказал небольшой группе из нас после своей лекции на мероприятии в Кембридже: «Пользовательские функции не должны полагаться на использование отказа из-за исключительной функциональности». Как обычно, его мудрость была показана в записи с тех пор. Исправленные и закрытые ошибки безопасности часто указывают на повторные попытки в описании дыры, использованной до исправления.
Это упоминается в официальном предложении о пересмотре стандартов для функции нулевого буфера в альтернативе sprintf, предложении о пересмотре C9X , документе ISO IEC WG14 N645 / X3J11 96-008 . Строка произвольной длины, вставляемая в директиву печати «% s» в рамках ограничений доступности динамической памяти, не является исключением и не должна использоваться для создания «Необычайной функциональности».
Рассмотрим предложение вместе с примером кода, приведенным внизу страницы C ++ Reference.org, на которую есть ссылка в первом абзаце этого ответа.
Кроме того, тестирование случаев неудачи редко бывает таким же успешным.
портативность
Все основные поставщики ОС предоставляют компиляторы, которые полностью поддерживают std :: vsnprintf как часть стандартов c ++ 11. Хосты, использующие продукты поставщиков, которые больше не поддерживают дистрибутивы, должны быть снабжены g ++ или clang ++ по многим причинам.
Использование стека
Использование стека в первом вызове std :: vsnprintf будет меньше или равно использованию второго, и оно будет освобождено до начала второго вызова. Если первый вызов превысит доступность стека, то std :: fprintf также потерпит неудачу.
источник
C ++ 20
std::format
Это прибыло! Функция описана по адресу: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html и использует Python-подобный
.format()
синтаксис.Я ожидаю, что использование будет как:
Я попробую, когда придет поддержка GCC, GCC 9.1.0 с
g++-9 -std=c++2a
все еще не поддерживает его.API добавит новый
std::format
заголовок:Существующая
fmt
библиотека утверждает, что реализует ее, если вам нужен polyfill: https://github.com/fmtlib/fmtи был ранее упомянут в: std :: string форматирование как sprintf
источник
Основываясь на ответе, предоставленном Эриком Аронести:
Это избавляет от необходимости отбрасывать
const
результат, результат.c_str()
которого был в исходном ответе.источник
источник
_vscprintf
есть. Я думаю, что вы должны уточнить этот ответ.Строка не имеет то, что вам нужно, но std :: stringstream делает. Используйте поток строк, чтобы создать строку, а затем извлечь строку. Вот полный список того, что вы можете сделать. Например:
даст вам 10 знаков после запятой точности при печати двойной или плавающей.
источник
Вы можете попробовать это:
источник
Если вы используете систему asprintf (3) , вы можете легко обернуть ее:
источник
format
, так как она говорит gcc проверить типы аргументов и дать достойное предупреждение с -Wall:std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
va_end
. «если va_end не вызывается до того, как функция, которая вызывает va_start или va_copy, возвращает поведение, оно не определено.» - docsthrow std::bad_alloc();
, так как я не использую исключения C ++ в своей кодовой базе, и для людей, которые делают это, они могут легко добавить его на основе на исходный комментарий и ваш комментарий здесь.Это код, который я использую, чтобы сделать это в моей программе ... Ничего особенного, но он делает свое дело ... Обратите внимание, вам придется корректировать свой размер в зависимости от обстоятельств. MAX_BUFFER для меня - 1024.
источник
vsnprintf
непосредственно в строку.Взял идею от Dacav и ответа pixelpoint . Я немного поиграл и получил это:
При правильной практике программирования я считаю, что кода должно быть достаточно, однако я по-прежнему открыт для более безопасных альтернатив, которые все еще достаточно просты и не требуют C ++ 11.
А вот еще одна версия, которая использует начальный буфер для предотвращения второго вызова,
vsnprintf()
когда начальный буфер уже достаточно.(Оказывается, эта версия просто похожа на ответ Пити Онгмонгколкула , только то, что она не использует
new
иdelete[]
, а также указывает размер при созданииstd::string
.Идея здесь не в том, чтобы использовать
new
иdelete[]
подразумевать использование стека поверх кучи, поскольку ему не нужно вызывать функции выделения и освобождения, однако, если не использовать должным образом, это может быть опасно для переполнения буфера в некоторых (возможно, старых или возможно просто уязвимые) системы. Если это проблема, я настоятельно рекомендую использоватьnew
иdelete[]
вместо. Обратите внимание, что единственная проблема здесь связана с распределением,vsnprintf()
которое уже вызывается с ограничениями, поэтому указание ограничения на основе размера, выделенного во втором буфере, также предотвратит это.)источник
Я обычно использую это:
Недостаток: не все системы поддерживают vasprint
источник
Ниже слегка измененная версия ответа @iFreilicht, обновлена до C ++ 14 (использование
make_unique
функции вместо необработанного объявления) и добавлена поддержкаstd::string
аргументов (на основе статьи Кенни Керра )Вывод:
Не стесняйтесь объединить этот ответ с оригинальным, если хотите.
источник
Библиотека Poco Foundation имеет очень удобную функцию форматирования, которая поддерживает std :: string как в строке формата, так и в значениях:
источник
Вы можете отформатировать вывод C ++ в cout, используя заголовочный файл iomanip. Убедитесь, что вы включили заголовочный файл iomanip, прежде чем использовать какие-либо вспомогательные функции, такие как setprecision, setfill и т. Д.
Вот фрагмент кода, который я использовал в прошлом, чтобы напечатать среднее время ожидания в векторе, который я «накопил».
Вот краткое описание того, как мы можем форматировать потоки C ++. http://www.cprogramming.com/tutorial/iomanip.html
источник
Могут возникнуть проблемы, если буфер недостаточно велик для печати строки. Вы должны определить длину отформатированной строки перед печатью отформатированного сообщения там. Я делаю собственный помощник в этом (проверено на Windows и Linux GCC ), и вы можете попробовать использовать его.
String.cpp: http://pastebin.com/DnfvzyKP
String.h: http://pastebin.com/7U6iCUMa
String.cpp:
string.h:
источник
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
- безопасно ли предполагать, что в буфере строки есть место для завершающего нулевого символа? Существуют ли реализации, которые не выделяют размер + 1 символов. Было бы безопаснее сделатьdst.resize(length+1); vsnprintf((char *)dst.data(), dst.size(), format, ap); dst.resize(length);
data
иc_str
являются синонимами.источник
Очень-очень простое решение.
источник
Я понимаю, что на это отвечали много раз, но это более кратко:
пример:
Смотрите также http://rextester.com/NJB14150
источник
ОБНОВЛЕНИЕ 1 : добавлено
fmt::format
тестыЯ взял свое собственное исследование вокруг методов, представленных здесь, и получил диаметрально противоположные результаты по сравнению с упомянутыми здесь.
Я использовал 4 функции более 4 методов:
vsnprintf
+std::unique_ptr
vsnprintf
+std::string
std::ostringstream
+std::tuple
+utility::for_each
fmt::format
функция изfmt
библиотекиДля тестового бэкенда
googletest
использовался.for_each
Реализация взята отсюда: перебрать кортежТесты:
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR
.unsued.hpp :
unused.cpp :
РЕЗУЛЬТАТЫ :
Как видите, реализация через
vsnprintf
+std::string
равнаfmt::format
, но быстрее, чем черезvsnprintf
+std::unique_ptr
, что быстрее, чем черезstd::ostringstream
.Тесты собраны
Visual Studio 2015 Update 3
и запущены вWindows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB
.источник