Ningyu said:
I recently started to write a program in c++, which involves some I/O
of file. Quickly, I found that it's difficult to use the c++ iostream
stuff.
say I want to do
printf("%10.3f %.4f %5d",a,b,c)
in c++
is it right to write like
cout<<setprecision(3)<<setw(10)<<a<<........?
that's too complicated...
I've read a lot of old discussion on this group, still confused, is
there a easier way to do my job in c++ way? I just want some simple
controled data output.
is there a way like
cout<<fmt("%10.3f %.4f %5d")<<a<<b<<c?
thanks
What about the following:
#include <iostream>
#include <sstream>
#include <iomanip>
int StringToInt(std::string stringValue)
{
std::stringstream ssStream(stringValue);
int iReturn;
ssStream >> iReturn;
return iReturn;
}
class decorated_ostream
{
public:
std::string FormatSpecifier;
std:
stream* OriginalStream;
decorated_ostream& operator<<(std:
stream& (__cdecl *_F)(std:
stream&))
{
((*_F)(*OriginalStream));
return *this;
}
};
class ostream_decorator
{
public:
std::string FormatSpecifier;
};
ostream_decorator fmt (const char* Format)
{
ostream_decorator RetVal;
RetVal.FormatSpecifier = Format;
return RetVal;
}
decorated_ostream operator<< (std:
stream& p_refLHS, ostream_decorator&
Specifier)
{
decorated_ostream RetVal;
RetVal.OriginalStream = &p_refLHS;
RetVal.FormatSpecifier = Specifier.FormatSpecifier;
return RetVal;
}
template <class T>
decorated_ostream& operator<< (decorated_ostream& p_refLHS, T p_RHS)
{
// If the format specifier is zero, we behave just like an ordinary
ostream.
if (p_refLHS.FormatSpecifier.size () == 0)
{
(*p_refLHS.OriginalStream) << p_RHS;
return p_refLHS;
}
// Search the format specifier for the first format specification.
int Temp = p_refLHS.FormatSpecifier.find_first_of ("%");
if (Temp == std::string::npos)
{
// If we haven't found any '%', we can assume that the format
string can be copied to the underlying
// stream one by one.
(*p_refLHS.OriginalStream) << p_refLHS.FormatSpecifier;
p_refLHS.FormatSpecifier.erase ();
return p_refLHS;
}
else
{
// Print the string before the '%' symbol and process the format
specifier.
(*p_refLHS.OriginalStream) << p_refLHS.FormatSpecifier.substr (0,
Temp);
p_refLHS.FormatSpecifier .erase (0, Temp + 1);
// We only process size specifier and precision of floats and doubles.
int Temp = p_refLHS.FormatSpecifier.find_first_not_of ("0123456789");
if (p_refLHS.FormatSpecifier[Temp] == '.' ||
p_refLHS.FormatSpecifier[Temp] == 'd' ||
p_refLHS.FormatSpecifier[Temp] == 'f')
{
// If Temp is non-zero, the user has supplied a width specifier.
if (Temp > 0)
{
// Parse the integer value and set it as requested width.
(*p_refLHS.OriginalStream) << std::setw (StringToInt
(p_refLHS.FormatSpecifier.substr (0, Temp)));
p_refLHS.FormatSpecifier.erase (0, Temp);
}
}
else
// TODO: do some meaningful error handling here.
throw 0;
// If there is a point in the format specification, we must search
for a precision specification.
if (p_refLHS.FormatSpecifier[0] == '.')
{
p_refLHS.FormatSpecifier.erase (0, 1);
Temp = p_refLHS.FormatSpecifier.find_first_not_of ("0123456789");
if (p_refLHS.FormatSpecifier[Temp] == 'd' ||
p_refLHS.FormatSpecifier[Temp] == 'f')
{
// If Temp is non-zero, the user has supplied a precision
specifier.
if (Temp > 0)
{
// Parse the integer value and set it as requested precision.
(*p_refLHS.OriginalStream) << std::setprecision (StringToInt
(p_refLHS.FormatSpecifier.substr (0, Temp)));
p_refLHS.FormatSpecifier.erase (0, Temp);
}
}
else
// TODO: do some meaningful error handling here.
throw 0;
}
(*p_refLHS.OriginalStream) << p_RHS;
p_refLHS.FormatSpecifier = p_refLHS.FormatSpecifier.erase (0, 1);
return p_refLHS;
}
}
int main ()
{
std::cout << fmt ("test1 %10ftest2 %4.2d") << 3.5 << 3.1416 <<
"test3" << std::endl;
return 0;
}
Above code has some drawbacks, however: only format specifications for
double and floats are recognized, and for these only the width and
precision is parsed. If you should need more, you'd have to modify the
parsing of the format specifiers. Furthermore, no type checking is
performed. This means that a user may specify "%d" but put a string as
next parameter into the decorated_ostream. Of course, you could check
this by retrieving the type of T at run-time and check against the
format specifier. You should keep in mind, that you cannot pass a second
fmt () specification into the call chain, since the second
ostream_decorator would be passed to the underlying stream, but the
resulting new decorated_ostream will be discarded. If you need this
behaviour urgently i'd think of a better solution.
Regards,
Stuart