formatted output question with strings

Discussion in 'C++' started by Jojo, Jul 23, 2007.

  1. Jojo

    Jojo Guest

    Hi all,

    I was wondering how I can perform formatted output with C++ strings. For
    example, suppose I have in plain C:

    sprintf(C_string, "%5.2f %6d");

    How can I do such a thing with C++ strings? Using a string stream and
    the << operator does not give me the formatting control like sprintf. Or
    did I miss something.... :-|

    Jeroen
    Jojo, Jul 23, 2007
    #1
    1. Advertising

  2. Jojo

    Tim Love Guest

    Jojo <> writes:

    >Hi all,


    >I was wondering how I can perform formatted output with C++ strings. For
    >example, suppose I have in plain C:


    > sprintf(C_string, "%5.2f %6d");

    ???

    >Or did I miss something.... :-|

    Look up fill, width, adjustfield, setprecision, floatfield etc. E.g.

    cout << 1331 <<endl;
    cout << "In hex " << hex<< 1331 <<endl;

    cout << 1331.123456 <<endl;
    cout.setf(ios::scientific,ios::floatfield);
    cout <<1331.123456 <<endl;
    cout << setprecision(3) << 1331.123456 <<endl;
    cout << dec << 1331 <<endl;
    cout.fill('X');
    cout.width(8);
    cout << 1331 <<endl;
    cout.setf(ios::left,ios::adjustfield);
    cout.width(8);
    cout << 1331 <<endl;
    Tim Love, Jul 23, 2007
    #2
    1. Advertising

  3. Jojo

    Jojo Guest

    Tim Love schreef:
    > Jojo <> writes:
    >
    >> Hi all,

    >
    >> I was wondering how I can perform formatted output with C++ strings. For
    >> example, suppose I have in plain C:

    >
    >> sprintf(C_string, "%5.2f %6d");

    > ???


    I was a bit in a hurry, I forgot the numeric arguments...

    >
    >> Or did I miss something.... :-|

    > Look up fill, width, adjustfield, setprecision, floatfield etc. E.g.
    >
    > cout << 1331 <<endl;
    > cout << "In hex " << hex<< 1331 <<endl;
    >
    > cout << 1331.123456 <<endl;
    > cout.setf(ios::scientific,ios::floatfield);
    > cout <<1331.123456 <<endl;
    > cout << setprecision(3) << 1331.123456 <<endl;
    > cout << dec << 1331 <<endl;
    > cout.fill('X');
    > cout.width(8);
    > cout << 1331 <<endl;
    > cout.setf(ios::left,ios::adjustfield);
    > cout.width(8);
    > cout << 1331 <<endl;
    >


    I'll look that up. At first glance, the C++ method seems much less
    'comprehensive' than sprintf. But I think it's better for me to get rid
    of my plain-C habits when coding C++ :)

    Thanx,

    Jeroen
    Jojo, Jul 23, 2007
    #3
  4. Jojo wrote:
    > I'll look that up. At first glance, the C++ method seems much less
    > 'comprehensive' than sprintf. But I think it's better for me to get rid
    > of my plain-C habits when coding C++ :)


    The C-style format string functions are compact, relatively easy
    to learn and remember, and fast. Once you get the hang of them, it's
    very easy to perform relatively complex formatting with a very small
    amount of code which only takes a small amount of time to write.
    Also, you never need to worry if some formatting setting "leaks" or
    not (ie. if the stream will "remember" that setting or forget about
    it immediately after printing the value).
    The C++-style printing functions are much more verbose, and a
    complex C one-liner can easily become over a dozen of lines of C++
    (or, alternatively, a dozen of << operator calls in a row) using
    diverse function names from <iostream> and <iomanip>. In some cases
    it also feels a bit inconsistent whether some stream setting is
    remembered or forgotten between << operator calls. Most are forgotten,
    but some are remembered, causing potential "formatting leaks".

    On the other hand:

    C-style printing functions lack abstraction, which is the whole point
    in the C++-style ones. For example, assume you have this:

    typedef int MyIndex;
    ....
    // Somewhere else:
    MyIndex index;
    ....
    std::printf("The value is: %i\n", index);

    The printf() breaks the abstraction of 'MyIndex' because it assumes
    it's an int. If you later change the typedef to:

    typedef long MyIndex;

    the printf() will break if long is larger than int (which is usually
    the case in 64-bit systems). Even if the compiler is so smart as to
    warn you about the incompatible printf format string, it could still
    be tedious to go and change every single printf which uses that type
    (which is something you wanted to avoid doing by using the typedef
    in the first place).

    Getting around this problem can be done, in a limited way, with all
    kinds of awkward kludges, such as:

    typedef int MyIndex;
    #declare MyIndexFormatString "%i"
    ....
    std::printf("The value is: " MyIndexFormatString "\n", index);

    Of course this breaks immediately if you change MyIndex to something
    not supported by printf(), such as a class.

    Naturally you could try to avoid this problem by writing a function
    for the only purpose of printing your MyIndex, like this:

    void printMyIndex(MyIndex value);
    ....
    std::printf("The value is: ");
    printMyIndex(index);
    std::printf("\n");

    But this has two problems: You already lost the advantage over
    the C++-way (ie. compactness), and you can't specify the formatting
    for printing the value anylonger. You would have to add even more
    kludges to this if you wanted to be able to specify the formatting:

    void printMyIndex(MyIndex value, const char* format);
    ....
    std::printf("The value is: ");
    printMyIndex(index, "05");
    std::printf("\n");

    Of course if you wanted that width parameter to be defined by
    a variable instead of being a string literal, it would become even
    more complicated and awkward. The more you develop this further,
    the more you are actually re-implementing C++ printing functions.

    Also, the lack of abstraction causes a really bad breakdown with
    templates. Consider:

    template<typename T>
    void print(const T& value)
    {
    std::printf("???", value); // What to write here???
    }

    The big advantage of C++-style printing is that it's abstract.
    You don't have to worry about types. For example:

    std::cout << index << std::endl; // I don't care what 'index' is.

    Or:

    template<typename T>
    void print(const T& value)
    {
    std::cout << T << std::endl; // It doesn't matter what T is.
    }

    If you create your own class and you want to support printing it
    in the regular way, you can! So it's perfectly possible to do this:

    class MyIndex { ... };
    ....
    MyIndex index;
    ....
    std::cout << index << std::endl;

    That MyIndex could be an int, a long, a double, a string or your
    own Class, it doesn't matter. You don't have to specify the type
    explicitly in the printing command, unlike in C.
    Juha Nieminen, Jul 23, 2007
    #4
  5. Jojo

    Jojo Guest

    Juha Nieminen schreef:
    > Jojo wrote:
    >> I'll look that up. At first glance, the C++ method seems much less
    >> 'comprehensive' than sprintf. But I think it's better for me to get rid
    >> of my plain-C habits when coding C++ :)

    >


    SNIP

    >
    > If you create your own class and you want to support printing it
    > in the regular way, you can! So it's perfectly possible to do this:
    >
    > class MyIndex { ... };
    > ...
    > MyIndex index;
    > ...
    > std::cout << index << std::endl;
    >
    > That MyIndex could be an int, a long, a double, a string or your
    > own Class, it doesn't matter. You don't have to specify the type
    > explicitly in the printing command, unlike in C.


    Thanks for the big story, very informative :) I guess that for the last
    example you have to write an 'operator <<' for class MyIndex in order to
    get it to work?
    Jojo, Jul 23, 2007
    #5
  6. On 2007-07-23 13:45, Jojo wrote:
    > Juha Nieminen schreef:
    >> Jojo wrote:
    >>> I'll look that up. At first glance, the C++ method seems much less
    >>> 'comprehensive' than sprintf. But I think it's better for me to get rid
    >>> of my plain-C habits when coding C++ :)

    >>

    >
    > SNIP
    >
    >>
    >> If you create your own class and you want to support printing it
    >> in the regular way, you can! So it's perfectly possible to do this:
    >>
    >> class MyIndex { ... };
    >> ...
    >> MyIndex index;
    >> ...
    >> std::cout << index << std::endl;
    >>
    >> That MyIndex could be an int, a long, a double, a string or your
    >> own Class, it doesn't matter. You don't have to specify the type
    >> explicitly in the printing command, unlike in C.

    >
    > Thanks for the big story, very informative :) I guess that for the last
    > example you have to write an 'operator <<' for class MyIndex in order to
    > get it to work?


    You can use some ugly work-arounds like adding a print() method to the
    class which returns a char* containing the output, this lets you do
    something like

    MyIndex index;

    printf("index: %s\n", index.print());

    --
    Erik Wikström
    =?ISO-8859-1?Q?Erik_Wikstr=F6m?=, Jul 23, 2007
    #6
  7. Jojo

    James Kanze Guest

    On Jul 23, 9:39 am, Jojo <> wrote:

    > I was wondering how I can perform formatted output with C++ strings. For
    > example, suppose I have in plain C:


    > sprintf(C_string, "%5.2f %6d");


    > How can I do such a thing with C++ strings?


    To do exactly the above is a bit awkward, since you need several
    manipulators. But normally, you only do something like the
    above because C doesn't offer any alternative. What is the
    first argument? What is the second? What semantic
    characteristic does the first have which means that it should be
    formatted using %5.2f, for example. In C++, you define the
    formatting for such semantic characteristics, then write
    something like:

    ... << ItsAToto << totosValue << ...

    Something like ItsAToto is called a manipulator. Anytime you
    write an application, you define the logical manipulators for
    the various semantics that you want to support on output. That
    way, if the request comes that all Toto now have to be output
    with three digits after the decimal, you just change the
    manipulator.

    Note that most of the time, you're not outputting int's or
    double's, but user defined types, which already have a specific
    application dependant semantic. In such cases, the type of
    "totosValue" says it all, and you don't need the manipulator.

    (In text processing, this is known as logical mark-up. Roughly
    speaking: C requires you to specify the physical mark-up,
    locally, at each output site. C++ allows you to define a style
    sheet.)

    > Using a string stream and
    > the << operator does not give me the formatting control like sprintf.


    It gives you a lot more.

    > Or did I miss something.... :-|


    If you're not familiar with maniipulators, or user defined
    operator<<, you've missed practically all of iostream. (The
    third important point is user defined streambufs.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jul 24, 2007
    #7
  8. Jojo

    James Kanze Guest

    On Jul 23, 12:45 pm, Juha Nieminen <> wrote:
    > Jojo wrote:
    > > I'll look that up. At first glance, the C++ method seems much less
    > > 'comprehensive' than sprintf. But I think it's better for me to get rid
    > > of my plain-C habits when coding C++ :)


    > The C-style format string functions are compact, relatively easy
    > to learn and remember, and fast.


    They're compact. That's all you can say for them. They're
    very, very limited in what they can do, however, and they're
    very, very low level, and don't support higher level
    abstractions. They also quite exoteric; even after 25 years of
    using them, and actually having written an implementation of
    printf, I still have to check with the manual for anything but
    the simplest formatting.

    > Once you get the hang of them, it's
    > very easy to perform relatively complex formatting with a very small
    > amount of code which only takes a small amount of time to write.


    And it's impossible to write any maintainable code, because the
    low level formatting details creep down into the high level
    output statements.

    > Also, you never need to worry if some formatting setting "leaks" or
    > not (ie. if the stream will "remember" that setting or forget about
    > it immediately after printing the value).
    > The C++-style printing functions are much more verbose, and a
    > complex C one-liner can easily become over a dozen of lines of C++
    > (or, alternatively, a dozen of << operator calls in a row) using
    > diverse function names from <iostream> and <iomanip>.


    Not if you use it correctly. (Anything can be abused, of
    course.) The difference is that in the C output, you have to
    provide all of the information for physical formatting in the
    single statement. In C++, you provide the information for the
    physical formatting in the manipulator (which works more or less
    like a style sheet in a word processor); the client code just
    invokes the correct logical manipulator. And most of the time,
    even that isn't necessary, because all of the logical
    information for formatting is implicit in the type of the
    expression. So you end up with:

    std::cout << myVariable ;

    instead of:

    printf( "%-30s %5.2f %3d",
    myVariable.getName(),
    myVariable.getValue1(),
    myVariable.getValue2() ) ;

    (What was that about printf being compact?) And of course, the
    C++ version continues to work correctly when value2 is changed
    from int to long, or when an additional value is added, or when
    it is suddenly decided that value one need three digits after
    the decimal.

    > In some cases it also feels a bit inconsistent whether some
    > stream setting is remembered or forgotten between << operator
    > calls. Most are forgotten, but some are remembered, causing
    > potential "formatting leaks".


    That's probably the one real pain of iostream formatting. All
    of the formatting flags except width are "sticky". So you throw
    in a quick additional output for debugging purposes:

    std::cout << std::hex << value << std::endl ;

    and all of the output which follows is in hex. (It's a
    "feature" when outputting tabular data, of course: set the
    precision once, before iterating over the table. But
    generally...)

    My own manipulators restore the original formatting flags in
    their destructor, i.e. at the end of the full expression. And
    of course, I've got an RAII class for restoring the original
    format as well, which can be used in a function which modifies
    formatting. But I agree that such things really shouldn't be
    necessary.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jul 24, 2007
    #8
  9. Jojo

    James Kanze Guest

    On Jul 23, 1:45 pm, Jojo <> wrote:
    > Juha Nieminen schreef:


    [...]
    > > class MyIndex { ... };
    > > ...
    > > MyIndex index;
    > > ...
    > > std::cout << index << std::endl;


    > > That MyIndex could be an int, a long, a double, a string or your
    > > own Class, it doesn't matter. You don't have to specify the type
    > > explicitly in the printing command, unlike in C.


    > Thanks for the big story, very informative :) I guess that for the last
    > example you have to write an 'operator <<' for class MyIndex in order to
    > get it to work?


    Obviously. The compiler can't know how you want it formatted.

    Another point that hasn't been mentionned is that the C++ idiom
    rigorously separates formatting from data sinking and sourcing.
    So you can write your own sinks and sources, and still get all
    of the existing formatting. In addition, the sinks and sources
    can chain, so you can e.g. expand tabs or strip comments from
    the source before the input functions ever see it. (There's
    less need of this in output, but I use it, for example, to
    insert time stamps in log files.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jul 24, 2007
    #9
  10. Jojo

    Jojo Guest

    James Kanze schreef:
    > On Jul 23, 9:39 am, Jojo <> wrote:
    >
    >> I was wondering how I can perform formatted output with C++ strings. For
    >> example, suppose I have in plain C:

    >
    >> sprintf(C_string, "%5.2f %6d");

    >
    >> How can I do such a thing with C++ strings?

    >
    > To do exactly the above is a bit awkward, since you need several
    > manipulators. But normally, you only do something like the
    > above because C doesn't offer any alternative. What is the
    > first argument? What is the second? What semantic
    > characteristic does the first have which means that it should be
    > formatted using %5.2f, for example. In C++, you define the
    > formatting for such semantic characteristics, then write
    > something like:
    >
    > ... << ItsAToto << totosValue << ...
    >
    > Something like ItsAToto is called a manipulator. Anytime you
    > write an application, you define the logical manipulators for
    > the various semantics that you want to support on output. That
    > way, if the request comes that all Toto now have to be output
    > with three digits after the decimal, you just change the
    > manipulator.
    >
    > Note that most of the time, you're not outputting int's or
    > double's, but user defined types, which already have a specific
    > application dependant semantic. In such cases, the type of
    > "totosValue" says it all, and you don't need the manipulator.
    >
    > (In text processing, this is known as logical mark-up. Roughly
    > speaking: C requires you to specify the physical mark-up,
    > locally, at each output site. C++ allows you to define a style
    > sheet.)
    >
    >> Using a string stream and
    >> the << operator does not give me the formatting control like sprintf.

    >
    > It gives you a lot more.
    >
    >> Or did I miss something.... :-|

    >
    > If you're not familiar with maniipulators, or user defined
    > operator<<, you've missed practically all of iostream. (The
    > third important point is user defined streambufs.)


    Well, maybe I can see that I didn't miss it because I haven't take a
    close look at it yet... :) Or is this too far from programmers logic?
    Nevertheless, thanks for your explanation and I'll definitly take a deep
    dive into iostream to become more familiar with it.

    Jeroen

    >
    > --
    > James Kanze (GABI Software) email:
    > Conseils en informatique orientée objet/
    > Beratung in objektorientierter Datenverarbeitung
    > 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    >
    Jojo, Jul 25, 2007
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Thomas Liesner

    newbie question concerning formatted output

    Thomas Liesner, Nov 29, 2005, in forum: Python
    Replies:
    11
    Views:
    429
    Thomas Liesner
    Nov 30, 2005
  2. dkk

    formatted input/output question

    dkk, Apr 10, 2006, in forum: C Programming
    Replies:
    2
    Views:
    305
    Herbert Rosenau
    Apr 12, 2006
  3. Ben

    Strings, Strings and Damned Strings

    Ben, Jun 22, 2006, in forum: C Programming
    Replies:
    14
    Views:
    732
    Malcolm
    Jun 24, 2006
  4. Peter Otten
    Replies:
    12
    Views:
    448
    John Posner
    Feb 10, 2010
  5. 7stud --
    Replies:
    2
    Views:
    116
    mortee
    Oct 15, 2007
Loading...

Share This Page