Я хотел бы напечатать связку целых чисел на 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
хоть.
Вам нужен прокси для вашего потока строк, например:
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();
пс. Обратите внимание на общий формат моего потока :: оператор<<(), который сначала выполняет свою работу, затем возвращает что-то.
«Очевидное» решение — использовать манипулятор для установки std::num_put<char>
аспект, который просто форматирует int
s по желанию.
Вышеприведенное утверждение может быть немного загадочным, хотя оно полностью описывает решение. Ниже приведен код для реализации логики. Первый ингредиент — это особый 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::locale
s, чтобы пройти через форматирование:
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';
}
Помимо форматирования целых чисел с std::setw
/ std::setfill
или же ios_base::width
/ basic_ios::fill
, если вы хотите отформатировать объект даты / времени, вы можете рассмотреть возможность использования std::put_time
/ std::gettime
Для удобного форматирования вывода вы можете использовать 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;
}
operator<<(std::ostream& s, int i)
является «неоднозначным», потому что такая функция уже существует.
Все, что вам нужно сделать, это дать этой функции подпись, которая не конфликтует.