Как я могу отформатировать std::string, используя набор аргументов?

Можно ли отформатировать std::string, передав набор аргументов?

В настоящее время я форматирую строку следующим образом:

string helloString = "Hello %s and %s";
vector<string> tokens; //initialized vector of strings
const char* helloStringArr = helloString.c_str();
char output[1000];
sprintf_s(output, 1000, helloStringArr, tokens.at(0).c_str(), tokens.at(1).c_str());

Но размер вектора определяется во время выполнения. Есть ли функция, аналогичная sprintf_s, которая принимает набор аргументов и форматирует std::string/char*? Моя среда разработки — MS Visual C++ 2010 Express.

EDIT: я хотел бы добиться чего-то подобного:

sprintf_s(output, 1000, helloStringArr, tokens);

person jilt3d    schedule 22.02.2011    source источник
comment
возможный дубликат форматирования std::string, такого как sprintf   -  person Rup    schedule 22.02.2011
comment
Формат также определяется во время выполнения? Или это фиксировано? Вы просто хотите перечислить содержимое vector в своем выводе? Вы можете проверить соединение: boost.org/doc/libs/1_46_0/doc/html/string_algo/   -  person Matthieu M.    schedule 22.02.2011
comment
Формат также определяется во время выполнения. Смотрите раздел РЕДАКТИРОВАТЬ для более подробной информации. Спасибо!   -  person jilt3d    schedule 22.02.2011


Ответы (3)


Вы можете сделать это с помощью библиотеки Boost.Format, потому что вы можете подавать аргументы один за другим.

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

Пример:

#include <boost/format.hpp>
#include <string>
#include <vector>
#include <iostream>
std::string format_range(const std::string& format_string, const std::vector<std::string>& args)
{
    boost::format f(format_string);
    for (std::vector<std::string>::const_iterator it = args.begin(); it != args.end(); ++it) {
        f % *it;
    }
    return f.str();
}

int main()
{
    std::string helloString = "Hello %s and %s";
    std::vector<std::string> args;
    args.push_back("Alice");
    args.push_back("Bob");
    std::cout << format_range(helloString, args) << '\n';
}

Вы можете работать отсюда, сделать его шаблонным и т. д.

Обратите внимание, что он генерирует исключения (обратитесь к документации), если вектор не содержит точного количества аргументов. Вам нужно будет решить, как с ними справиться.

person visitor    schedule 22.02.2011
comment
Вопрос заключается в том, чтобы std::string не повышался. - person hfrmobile; 13.04.2017

Наиболее похожим на C++ способом достижения функциональности, подобной sprintf, было бы использование потоков строк.

Вот пример, основанный на вашем коде:

#include <sstream>

// ...

std::stringstream ss;
std::vector<std::string> tokens;
ss << "Hello " << tokens.at(0) << " and " << tokens.at(1);

std::cout << ss.str() << std::endl;

Довольно удобно, не так ли?

Конечно, вы получаете все преимущества манипулирования IOStream для замены различных флагов sprintf, см. здесь http://www.fredosaurus.com/notes-cpp/io/omanipulators.html для справки.

Более полный пример:

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

int main() {
  std::stringstream s;
  s << "coucou " << std::setw(12) << 21 << " test";

  std::cout << s.str() << std::endl;
  return 0;
}

который печатает:

coucou           21 test

Изменить:

Как указано в OP, этот способ ведения дел не допускает вариативных аргументов, потому что нет заранее созданной строки «шаблона», позволяющей потоку перебирать вектор и вставлять данные в соответствии с заполнителями.

person SirDarius    schedule 22.02.2011
comment
Спасибо, но я все еще не понимаю, как я могу передать вектор в качестве аргумента, не получая значения по одному. Мне нужно что-то подобное: sprintf_s(output, 1000, helloStringArr, tokens); - person jilt3d; 22.02.2011
comment
С++-подобный способ по-прежнему заключался бы в добавлении токенов в поток строк вместо того, чтобы сначала создавать коллекцию токенов, а затем пытаться переформатировать их снова. Вероятно, было бы полезно, если бы мы знали, что мы делаем, а не то, как мы должны это делать. :-) - person Bo Persson; 22.02.2011
comment
@Bo: Таким образом, подход C++ состоит в том, чтобы не использовать строки формата и просто отказаться от всего хорошего, такого как удобочитаемость;) или базовой интернационализации? (Фрагменты строк, которые вы должны передавать в потоки, в основном не поддаются осмысленному переводу) - person visitor; 23.02.2011
comment
@visitor: Читабельность, возможно, не является реальной силой строк формата. :-). Интернационализация также будет довольно простой, если не учитывать, что порядок элементов может зависеть от языка или что используемые фразы зависят от количества элементов. - person Bo Persson; 23.02.2011

Библиотека boost::format может быть интерес, если вы хотите избежать ручной работы с выходным буфером.

Что касается простого вектора в качестве входных данных, что бы вы хотели, чтобы произошло, если tokens.size()<2? Разве вам не нужно было убедиться, что вектор был достаточно большим, чтобы индексировать элементы 0 и 1 в любом случае?

person Martin Stone    schedule 22.02.2011
comment
Я проверил эту библиотеку, но не нашел способа передать аргументы форматирования как один набор аргументов. - person jilt3d; 22.02.2011