Как напечатать связку целых чисел с одинаковым форматированием?

Я хотел бы напечатать связку целых чисел на 2 полях с '0' как заполнить персонажа. Я могу сделать это, но это приводит к дублированию кода. Как мне изменить код, чтобы дублирование кода могло быть учтено?

#include <ctime>
#include <sstream>
#include <iomanip>
#include <iostream>

using namespace std;

string timestamp() {

time_t now = time(0);

tm t = *localtime(&now);

ostringstream ss;

t.tm_mday = 9; // cheat a little to test it
t.tm_hour = 8;

ss << (t.tm_year+1900)
<< setw(2) << setfill('0') << (t.tm_mon+1) // Code duplication
<< setw(2) << setfill('0') <<  t.tm_mday
<< setw(2) << setfill('0') <<  t.tm_hour
<< setw(2) << setfill('0') <<  t.tm_min
<< setw(2) << setfill('0') <<  t.tm_sec;

return ss.str();
}

int main() {

cout << timestamp() << endl;

return 0;
}

я пытался

std::ostream& operator<<(std::ostream& s, int i) {

return s << std::setw(2) << std::setfill('0') << i;
}

но это не сработало, operator<< звонки неоднозначны.


РЕДАКТИРОВАТЬ Я получил 4 удивительных ответа, и я выбрал тот, который, пожалуй, самый простой и самый общий (то есть не предполагает, что мы имеем дело с метками времени). Для актуальной проблемы, я, вероятно, буду использовать std::put_time или же strftime хоть.

2

Решение

Вам нужен прокси для вашего потока строк, например:

struct stream{
std::ostringstream ss;
stream& operator<<(int i){
ss << std::setw(2) << std::setfill('0') << i;
return *this; // See Note below
}
} ss;

Тогда ваш код форматирования будет таким:

ss << (t.tm_year+1900)
<< (t.tm_mon+1)
<< t.tm_mday
<< t.tm_hour
<< t.tm_min
<< t.tm_sec;

return ss.ss.str();

пс. Обратите внимание на общий формат моего потока :: оператор<<(), который сначала выполняет свою работу, затем возвращает что-то.

3

Другие решения

«Очевидное» решение — использовать манипулятор для установки std::num_put<char> аспект, который просто форматирует ints по желанию.

Вышеприведенное утверждение может быть немного загадочным, хотя оно полностью описывает решение. Ниже приведен код для реализации логики. Первый ингредиент — это особый std::num_put<char> фаска который является просто классом, полученным из std::num_put<char> и переопределение одного из его virtual функции. Используемый фасет — это фильтрующий фасет, который смотрит на флаг, сохраненный в потоке (используя iword()) определить, должно ли это изменить поведение или нет. Вот код:

class num_put
: public std::num_put<char>
{
std::locale loc_;
static int index() {
static int rc(std::ios_base::xalloc());
return rc;
}
friend std::ostream& twodigits(std::ostream&);
friend std::ostream& notwodigits(std::ostream&);

public:
num_put(std::locale loc): loc_(loc) {}
iter_type do_put(iter_type to, std::ios_base& fmt,
char fill, long value) const {
if (fmt.iword(index())) {
fmt.width(2);
return std::use_facet<std::num_put<char> >(this->loc_)
.put(to, fmt, '0', value);
}
else {
return std::use_facet<std::num_put<char> >(this->loc_)
.put(to, fmt, fill, value);
}
}
};

Основная часть do_put() функция-член, которая решает, как значение должно быть отформатировано: если флаг в fmt.iword(index()) не равен нулю, он устанавливает ширину 2 и вызывает функцию форматирования с символом заполнения 0, Ширина все равно будет сброшена, и символ заливки не будет сохранен в потоке, то есть не требуется никакой очистки.

Обычно код, вероятно, будет находиться в отдельном модуле перевода и не будет объявлен в заголовке. Единственные функции, действительно объявленные в заголовке, будут twodigits() а также notwodigits() которые сделаны friendв этом случае, чтобы обеспечить доступ к index() функция-член. index() функция-член просто выделяет индекс, используемый с std::ios_base::iword() когда вызывается время, а затем просто возвращает этот индекс. манипуляторы twodigits() а также notwodigits() в первую очередь установить этот индекс. Если num_put фасет не установлен для потока twodigits() также устанавливает фасет:

std::ostream& twodigits(std::ostream& out)
{
if (!dynamic_cast<num_put const*>(
&std::use_facet<std::num_put<char> >(out.getloc()))) {
out.imbue(std::locale(out.getloc(), new num_put(out.getloc())));
}
out.iword(num_put::index()) = true;
return out;
}

std::ostream& notwodigits(std::ostream& out)
{
out.iword(num_put::index()) = false;
return out;
}

twodigits() манипулятор выделяет num_put использование фасетов new num_put(out.getloc()), Не требует никакой очистки, потому что установка фасета в std::locale объект делает необходимую очистку. Оригинал std::locale доступ к потоку с помощью out.getloc(), Это изменено аспектом. В теории notwodigits может восстановить оригинал std::locale вместо использования флага. Тем не мение, imbue() может быть относительно дорогой операцией, а использование флага должно быть намного дешевле. Конечно, если есть много одинаковых флагов форматирования, все может измениться …

Для демонстрации использования манипуляторов ниже приведена простая тестовая программа. Он устанавливает флаг форматирования twodigits дважды, чтобы убедиться, что фасет создается только один раз (было бы немного глупо создавать цепочку std::locales, чтобы пройти через форматирование:

int main()
{
std::cout << "some-int='" << 1 << "' "<< twodigits << '\n'
<< "two-digits1='" << 1 << "' "<< "two-digits2='" << 2 << "' "<< "two-digits3='" << 3 << "' "<< notwodigits << '\n'
<< "some-int='" << 1 << "' "<< twodigits << '\n'
<< "two-digits4='" << 4 << "' "<< '\n';
}
3

Помимо форматирования целых чисел с std::setw / std::setfill или же ios_base::width / basic_ios::fill, если вы хотите отформатировать объект даты / времени, вы можете рассмотреть возможность использования std::put_time / std::gettime

2

Для удобного форматирования вывода вы можете использовать boost::format() с sprintf-подобные варианты форматирования:

#include <boost/format.hpp>
#include <iostream>

int main() {
int i1 = 1, i2 = 10, i3 = 100;
std::cout << boost::format("%03i %03i %03i\n") % i1 % i2 % i3;
// output is: 001 010 100
}

Небольшое дублирование кода, дополнительные усилия по реализации незначительны.


Если все, что вы хотите сделать, это отформатировать выходную метку времени, очевидно, вы должны использовать strftime(). Вот для чего это сделано:

#include <ctime>
#include <iostream>

std::string timestamp() {
char buf[20];
const char fmt[] = "%Y%m%d%H%M%S";
time_t now = time(0);
strftime(buf, sizeof(buf), fmt, localtime(&now));
return buf;
}

int main() {
std::cout << timestamp() << std::endl;
}
1

operator<<(std::ostream& s, int i) является «неоднозначным», потому что такая функция уже существует.

Все, что вам нужно сделать, это дать этой функции подпись, которая не конфликтует.

0
По вопросам рекламы [email protected]