tags:

views:

286

answers:

6

Hello, I'm working on a project where I have a Time class and I need to format the time.

void Time::FormatTime(char *string, unsigned int max_string_len) {
    ostrstream fd;
    ft << hour << ":" << minutes;
    cout << ft.str() << endl;    
}

The user passes in a pointer to their string and the max length of the string and I need to check that the time string isn't longer than max_string_len and if it is, then truncate it. I'm not sure how to do the truncation as it's been awhile since I've written any C++.

I'd like to not use the STL if possible.

Thanks

+1  A: 

If the first parameter can be modified then you can just place a 0 in the correct position to be a string terminator, however this is a fairly nasty (but fast) solution. Otherwise, you'll want to create a new array of characters and only copy over the correct number of characters, remembering to allocate an extra character for the zero terminator.

Stephen Cross
+2  A: 

This does what you want:

 void Time::FormatTime(char *string, unsigned int max_string_len) {
      ostrstream fd;
      ft << hour << ":" << minutes;
      std::string str = ft.str();
      str.resize(max_string_len);
      strcpy(string, str.c_str());
 }

EDIT: I've updated my code thanks to the helpful feedback of potatoswatter and quanmrana.

Manuel
This is the most straightforward way to do it. Short and simple.
Sheep Slapper
-1: You're checking whether overflow *already* occurred.
Potatoswatter
-1: strlen(string) might be undefined if max_string_len is 0.
quamrana
`str.resize(n)` adjusts `str` to have `n` characters not including the terminating byte. `strcpy` copies `n` characters plus the terminating byte, which causes an overflow.
Potatoswatter
@Manuel: You should also rename the first parameter from `string` to something like `str` now that you're using the stl. You don't want anything confused with `std::string`.
quamrana
+2  A: 
if( str.length() > max_string_len ) 
{ 
    str = str.substr( 0, max_string_len );
}

strncpy( string, str.c_str(), max_string_len );
Martin Cote
A: 

From your requirements, you seem intent on writing C instead of C++, but I guess that's your business. For your specification, however, it appears that the right answer is strftime().

Jerry Coffin
+1  A: 

I'm half guessing at what you want, but I think it's something like the following, where I'm assuming or changing:

  • ft really should be fd
  • I used an ostringstream object instead of an ostrstream
  • max_string_len is the size of the destination buffer in characters (including the character that will hold the '\0' terminator)
  • you're not really interested in the formatted string being sent to std::cout, but want it in the caller's provided buffer
  • I changed the name of parameter string to s so it wouldn't be confused with the std:string type.

The code:

void Time::FormatTime(char *s, unsigned int max_string_len) {
    if (max_string_len == 0) {
        return;  // no buffer, bail out
    }

    std::ostringstream fd;

    fd << hour << ":" << minutes;

    size_t len = fd.str().copy( s, max_string_len - 1);  // leave room for the null terminator

    s[len] = '\0';
}

If you really don't want any part of the STL, then the following C-styled code should do the trick:

void Time::FormatTime(char *s, unsigned int max_string_len) {

    snprintf( s, max_string_len, "%02u:%02u", hour, minutes);
}

It's simpler, but may C++ developers don't like using the printf() family because it's not typesafe.

Michael Burr
Crud - I just noticed the desire to not use the STL - I'm using the STL here because the example given in the question uses it, and the tag says "C++"... Post a comment if I should delete this answer, and if you want a pure C function, let's retag the question as just a C question.
Michael Burr
I like your first little snippet, but I keep getting this error: "left of '.copy' must have class/struct/union"
Joe
The first snippet compiles just fine on VS2008 after adding `#include <sstream>`, declarations for `hour` and `minutes`, and correcting `max_string_len`.
quamrana
Sorry about the `max_String_len` typo (now fixed). I should have made clear that `std::ostringstream` requires a `#include <sstream>` and that I assumed that `hour` and `minutes` were members of the `Time` class (based on the original snippet).
Michael Burr
+1 - snprintf beats any STL-based solution for this task
Manuel
-1: You need to pass `max_string_len-1` to `snprintf`, that code overflows.
Potatoswatter
@Potatoswatter: that's incorrect - `snprintf()` expects the size of the buffer - you don't need to 'hold back' a character for the terminator. From the standard: "If n is zero, nothing is written,and s may be a null pointer. Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array".
Michael Burr
You're right, I misread the manpage.
Potatoswatter
A: 

Always use strncpy() to safely truncate a string into a char[N].

void Time::FormatTime(char *str, unsigned int max_string_len) {
    if ( len == 0 ) return;
    ostringstream ft; // strstream is obsolete, use stringstream
    ft << hour << ":" << minutes;
    strncpy( str, ft.str().c_str(), max_string_len );
    str[ max_string_len - 1 ] = 0;
}
Potatoswatter
Downvoted because max_string_len can be 0
Manuel
@Manuel: Fine, although I think that's silly. Note that `snprintf` does not include the terminating byte in its limit argument, so passing 0 to this function would be like passing -1 to `snprintf`.
Potatoswatter
oops, scratch that about `snprintf`.
Potatoswatter