PaulH
PaulH

Reputation: 7863

copying from a std::string to a char array and null-terminating the result

I have a Visual Studio 2008 C++03 application where I want to copy from a std::string to a char array, but I need the char array to be null terminated even if it must truncate the string to do so.

This, for instance works as desired:

inline void CopyAndNullTerminate( const std::string& source, 
                                  char* dest, 
                                  size_t dest_size )
{
    source.copy( dest, dest_size );
    using std::min;
    char* end = dest + min( dest_size - 1, source.size() );
    *end = '\0';
}

int main()
{
    enum{ MAX_STRING_SIZE = 15 };
    char dest[ MAX_STRING_SIZE ];
    std::string test = "This is a test";

    CopyAndNullTerminate( test, dest, MAX_STRING_SIZE );

    assert( strcmp( test.c_str(), dest ) == 0 );
    return 0;
}

example: http://ideone.com/0EBYb

Is there a shorter, more efficient method of doing this?

Thanks

Upvotes: 7

Views: 18875

Answers (5)

Steve Jessop
Steve Jessop

Reputation: 279255

Assuming that dest_size is guaranteed to be at least 1 (which seems reasonable to me, since otherwise it is not possible to copy and null terminate anything into the buffer):

inline void CopyAndNullTerminate( const std::string& source, 
                                  char* dest, 
                                  size_t dest_size )
{
    dest[source.copy(dest, dest_size-1)] = 0;
}

In C++11, and in all actively-maintained C++03 implementations (including Visual Studio), std::string has contiguous storage, just like std::vector. Thus, you could use memcpy instead of std::string::copy and compare performance.

Likewise you can compare strncpy, std::copy, std::copy_n, strcpy_s, and possibly others I've forgotten, see which is best optimized. In each case, you can compute the number of bytes to copy as std::min(source.size(), dest_size-1). That also avoids the most inefficient case of strncpy (copying a small string into a large buffer).

When doing all these, you can rely on the fact that it is valid to call source[0] even if source is an empty string.

Upvotes: 9

Jerry Coffin
Jerry Coffin

Reputation: 490148

Here are a couple that are shorter:

inline void CopyAndNullTerminate( const std::string& source, 
                                  char* dest, 
                                  size_t dest_size )
{
    *dest = '\0'; // assumes `dest_size > 0`
    strncat(dest, source, dest_size);
}

void CopyAndNullTerminate(std::string const &source,
                          char *dest,
                          size_t dest_size) { 
    sprintf(dest, "%*s", dest_size, source.c_str());
}

Efficient may be open to more question, but I don't see any reason to believe either will be particularly slow.

Upvotes: 3

jxh
jxh

Reputation: 70412

How about the good ole' C strcpy_s?

strcpy_s(dest, MAX_STRING_SIZE, test.c_str());

I prefer this to strncpy. The strcpy_s (and it's older cousin strlcpy) copy loop will terminate after copying the '\0', but strncpy will always write out the number of bytes specified (padding with '\0' if the source string is shorter).

Upvotes: 1

Chad
Chad

Reputation: 19032

If you initialize your destination string (to null) first, then you can get it will be null terminated if you don't overwrite the last element in your array.

enum{ MAX_STRING_SIZE = 15 };

char dest[ MAX_STRING_SIZE ] = {0}; // all elements are initialized to 0
std::string test = "This is a test";

// determine the length to copy, it will be the min of MAX_STRING_SIZE-1 
// or test.size.  This will ensure that even if the string it shorter than
// the MAX_STRING_SIZE, that it will copy properly -- without reading past the
// array bounds of the string.
auto copy_len = std::min(test.size(), MAX_STRING_SIZE - 1);

std::copy(
   test.begin(),
   test.begin() + copy_len,
   dest);

This will copy at most 14 characters to your dest object. If the test string is shorter than 14 characters, it will only copy the length of the test string. All elements not copied will remain at their initialized value (null).

Upvotes: 5

Richard J. Ross III
Richard J. Ross III

Reputation: 55563

Yes, use strncpy, defined in cstring:

void copyString(const std::string& input, char *dst, size_t dst_size)
{
    strncpy(dst, input.c_str(), dst_size - 1);
    dst[dst_size - 1] = '\0';
}

Note that for some implementations of std::string (as pointed out by @K-ballo), this may be shorter, but less efficient. This is due to the fact that std::string is NOT guaranteed to be implemented using C-syle strings, although for most situations that is probably the case.

Upvotes: 13

Related Questions