Могу ли я измерить необходимый буфер для sprintf в Microsoft C++?

Я пишу небольшую тестовую консольную программу с Visual Studio 2008, и я хотел, чтобы она выводила цветной текст для удобства чтения. Для простоты кодирования я также хотел сделать быструю замену printf, что-то, где я мог бы написать так:

MyPrintf(L"Some text \1[bright red]goes here\1[default]. %d", 21);

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

Однако я столкнулся с wsprintf, потому что не могу найти функцию, которая позволила бы мне узнать требуемый размер буфера перед передачей его в функцию. Я мог бы, конечно, выделить 1 МБ на всякий случай, но это было бы некрасиво, и я предпочел бы оставить это в качестве решения для резервного копирования, если я не могу найти лучшего способа.

Кроме того, в качестве альтернативы я рассматриваю возможность использования std::wstring (на самом деле я больше парень C с небольшим опытом работы с C++, поэтому сейчас мне проще использовать обычные массивы символов), но у него нет ничего похожего на wsprintf, где вы могли бы построить строку с замененными в них значениями.

И что я должен делать?


person Vilx-    schedule 15.02.2010    source источник
comment
Это была известная проблема с sprintf() в течение очень долгого времени. Вот почему C99 добавил snprintf(). Хотя вы можете догадываться о длине строки, ее правильное определение требует воссоздания большинства функций printf().   -  person David Thornley    schedule 15.02.2010


Ответы (6)


Вам нужен _snwprintf. Эта функция принимает размер буфера, и если буфер недостаточно велик, просто удвойте размер буфера и повторите попытку. Чтобы каждый раз не выполнять несколько вызовов _snwprintf, отслеживайте размер буфера, который вы использовали в прошлый раз, и всегда начинайте с него. Вы будете делать несколько лишних вызовов здесь и там, и время от времени будете тратить немного оперативной памяти, но это прекрасно работает и ничего не может перегрузить.

person Michael Kohne    schedule 15.02.2010
comment
В GCC есть аналогичная функция snprintf. Вместо того, чтобы использовать тот или тот, который вы предлагаете для MS, лучше использовать переносную замену, например: ijs.si/software/snprintf - person Manuel; 15.02.2010
comment
Как заявил Мануэль, следует четко указать, что Microsoft _snprintf() существенно отличается от стандартного snprintf(), и его следует избегать, а не использовать тот, который ведет себя более стандартно, если это возможно. К сожалению, похоже, что MS не исправила эту проблему в VS2010... Я предпочитаю реализацию Хольгера Вайса, так как лицензия не является ограничительной (я не уверен, как лицензия Frontier Artistic применяется ко всей моей программе, если я включу Отметьте snprintf() Мартинека из ijs.si в мою программу). - person Michael Burr; 16.02.2010
comment
Я хотел дать ответ, который не требовал добавления библиотек, поэтому я выбрал решение, локальное для OP. Лично я также предпочитаю стандартный snprintf, потому что его возвращаемое значение гораздо полезнее, но я ненавижу советовать людям перетаскивать внешние библиотеки для одной функции. - person Michael Kohne; 17.02.2010

Ваш вопрос помечен как C++, и в этом случае я бы сказал, что std::wstringstream - это путь. Пример:

#include <sstream>

void func()
{
    // ...

    std::wstringstream ss;  // the string stream

    // like cout, you can add strings and numbers by operator<<
    ss << L"Some text \1[bright red]goes here\1[default]. " << 21;

    // function takes a C-style const wchar_t* string
    some_c_function(ss.str().c_str()); // convert to std::wstring then const wchar_t*
    // note: lifetime of the returned pointer probably temporary
    // you may need a permanent std::wstring to return the c_str() from
    // if you need it for longer.

    // ...
}
person AshleysBrain    schedule 15.02.2010

Я бы выбрал строковый поток С++. Он не такой компактный, как sprintf, но даст вам желаемую функциональность.

person Sean    schedule 15.02.2010
comment
Не могли бы вы привести несколько примеров использования? Как я уже сказал, я не очень силен в C++ и от всех этих шаблонов голова идет кругом. Я не могу понять, как это должно работать из документации. :( - person Vilx-; 15.02.2010
comment
AshleysBrain предоставила пример, который соответствует вашему сообщению. Я не буду повторять его пример. - person Sean; 15.02.2010

Если вы можете позволить себе использование Boost, вы можете рассмотреть возможность boost::format. Это дало бы вам гибкость std::strings и функции форматирования sprintf. Он сильно отличается от C-стиля, но также довольно прост в использовании. Вот пример.

person Dmitry    schedule 15.02.2010

_scprintf, _scprintf_l, _scwprintf, _scwprintf_l

Эта функция вернет количество символов в отформатированной строке.

person mheyman    schedule 29.08.2013

Использование std::wstring кажется хорошим решением, если вы планируете передавать строки между вашими объектами - оно обрабатывает размер и имеет хороший метод c_str, который даст вам массив широких символов.

Дополнительным преимуществом является то, что вы можете передавать его по ссылке, а не по указателю.

Когда вам нужна фактическая строка, просто используйте метод c_str:

wprintf(L"string %s recieved!", myWString.c_str());
person Dror Helper    schedule 15.02.2010
comment
Хорошо, но что, если я хочу что-то вроде "Opened connection to %s on port %hd" ? - person Vilx-; 15.02.2010