Использование различных спецификаторов формата c в C++
Как и в C мы можем использовать различные спецификаторы формата, такие как
- % nd, где n - это число, чтобы напечатать число с общим количеством не менее n пробелов
- % 0 и то же, что и выше, за исключением предварительного дополнения 0 "% 05d", 3 => 00003
- %.nf, чтобы установить точность n после десятичной
- так далее....
Так есть ли способ использовать их с std:: cout?
Я получил некоторые отрицательные отзывы в недавнем курсе (C++ для программистов на c) в Coursera, за использование printf вместо cout, потому что я хотел немного отформатировать:(
5 ответов
За %nd
%0nd
, Эквиваленты C++ std::setw()
а также std::setfill()
,
#include <iostream> // std::cout, std::endl
#include <iomanip> // std::setfill, std::setw
int main () {
std::cout << std::setfill ('x') << std::setw (10);
std::cout << 77 << std::endl;
return 0;
}
Выход: xxxxxxxx77
%.nf
можно заменить на std::setprecision
а также std::fixed
,
#include <iostream> // std::cout, std::fixed, std::scientific
int main () {
double a = 3.1415926534;
double b = 2006.0;
double c = 1.0e-10;
std::cout.precision(5);
std::cout << "fixed:\n" << std::fixed;
std::cout << a << '\n' << b << '\n' << c << '\n';
return 0;
}
Выход:
fixed:
3.14159
2006.00000
0.00000
Поток C++ не использует спецификаторы формата, такие как C printf()
функции типа; они используют manipulators
,
Например:
#include <iostream>
#include <iomanip>
int main()
{
std::cout << std::fixed << std::setprecision(6) << 42.0 << std::endl;
}
Выход:
42.000000
Обычное решение в C++ состоит в том, чтобы определять манипуляторы, в которых указывается, что вы пытаетесь отформатировать, а не взламывать физические значения непосредственно в точке вывода. (Одним из возможных исключений является ширина, где std::setw
может быть полезным непосредственно.) Таким образом, например, при фактическом выводе чего-либо вы не будете указывать заполнение нулями или фиксированное значение с двумя десятичными знаками, а что-то вроде:
std::cout << temperature(2) << theTemporature;
где temperature
будет что-то вроде:
class temperature
{
int myMinWidth;
public:
temperature( int minWidth )
: myMinWidth( minWidth )
{
}
friend std::ostream& operator<<( std::ostream& dest, temperature const& manip )
{
dest.setf( std::ios_base::fixed, std::ios_base::floatfield );
dest.precision( 2 );
dest.width( myMinWidth );
return dest;
}
};
Список доступных модификаций формата см. В спецификации std::ios_base
и поляstd::ios_base::fmtflags
,
Если вы делаете много выходных данных, вы можете изменить это, чтобы восстановить исходные флаги формата в конце полного выражения. (Вся информация о формате, кроме ширины, является фиксированной, поэтому форсированный фиксированный формат оставляет вам фиксированный формат для остальной части программы, что не обязательно соответствует вашим требованиям.) Я использую следующее в качестве базового класса для всех моих манипуляторы:
class StateSavingManip
{
public:
void operator()( std::ios& stream ) const;
protected:
StateSavingManip() : myStream( nullptr ) {}
~StateSavingManip();
private:
virtual void setState( std::ios& stream ) const = 0;
private:
mutable std::ios* myStream;
mutable std::ios::fmtflags mySavedFlags;
mutable int mySavedPrec;
mutable char mySavedFill;
};
реализация:
namespace {
int getXAlloc() ;
int ourXAlloc = getXAlloc() + 1 ;
int
getXAlloc()
{
if ( ourXAlloc == 0 ) {
ourXAlloc = std::ios::xalloc() + 1 ;
assert( ourXAlloc != 0 ) ;
}
return ourXAlloc - 1 ;
}
}
StateSavingManip::~StateSavingManip()
{
if ( myStream != nullptr ) {
myStream->flags( mySavedFlags ) ;
myStream->precision( mySavedPrec ) ;
myStream->fill( mySavedFill ) ;
myStream->pword( getXAlloc() ) = NULL ;
}
}
void
StateSavingManip::operator()(
std::ios& stream ) const
{
void*& backptr = stream.pword( getXAlloc() ) ;
if ( backptr == nullptr ) {
backptr = const_cast< StateSavingManip* >( this ) ;
myStream = &stream ;
mySavedFlags = stream.flags() ;
mySavedPrec = stream.precision() ;
mySavedFill = stream.fill() ;
}
setState( stream ) ;
}
Обратите внимание на использование pword
поле, чтобы гарантировать, что только первый временный манипулятор восстанавливает формат; деструкторы будут вызываться в обратном порядке построения, но порядок построения обычно не указывается, если в выражении содержится более одного такого манипулятора.
Наконец, не все возможно с помощью этой техники: если вы хотите систематически добавлять знак градуса к температуре, это невозможно сделать. В этом случае вам нужно определить класс Temperature и перегрузить <<
оператор для него; это позволяет все мыслимое (гораздо больше, чем вы могли бы достичь с printf
стиль форматирования).
Поточные манипуляторы C++ ( iomanip) были специально разработаны для поддержки всех стандартных операций спецификаторов формата c, только с совершенно другим интерфейсом. Например setfill
а также setw
для ширины и заполнения части %02d
,
Конечно, если вам действительно нужны строки формата (например, потому что это облегчает i18n), то вам стоит взглянуть на boost::format
, и если у вас есть C++11, то вы можете легко написать вокруг него небольшую переменную оболочку шаблона, чтобы вызов формата был больше похож на printf.
Что бы вы ни делали, пожалуйста, старайтесь не использовать printf. Он не безопасен для типов и не расширяется для операций вывода для пользовательских типов.
Есть потоковые манипуляторы, если они вам нужны.
Но я думаю, что вы хотите знать эту вещь:cout
умнее чем printf()
, Скажем, у вас есть:
int x = 34;
cout<<x;
Теперь компилятор вызовет:
ostream& operator<<(ostream& stream, int arg);
для тебя. И эта функция будет печатать вывод в консоли (так как в вашем случае stream
является cout
). Стандартная библиотека предоставляет перегрузки для этого <<
оператор для всех основных типов данных.