Reputation: 7863
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
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
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
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
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
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