13

I am relatively new to C++. Recent assignments have required that I convert a multitude of char buffers (from structures/sockets, etc.) to strings. I have been using variations on the following but they seem awkward. Is there a better way to do this kind of thing?

#include <iostream>
#include <string>

using std::string;
using std::cout;
using std::endl;


char* bufferToCString(char *buff, int buffSize, char *str)
{
    memset(str, '\0', buffSize + 1);
    return(strncpy(str, buff, buffSize));
}


string& bufferToString(char* buffer, int bufflen, string& str)
{
    char temp[bufflen];

    memset(temp, '\0', bufflen + 1);
    strncpy(temp, buffer, bufflen);

    return(str.assign(temp));
}



int main(int argc, char *argv[])
{
   char buff[4] = {'a', 'b', 'c', 'd'};

   char str[5];
   string str2;

   cout << bufferToCString(buff, sizeof(buff), str) << endl;

   cout << bufferToString(buff, sizeof(buff), str2) << endl;

}
Newton Falls
  • 2,148
  • 3
  • 17
  • 22

7 Answers7

27

Given your input strings are not null terminated, you shouldn't use str... functions. You also can't use the popularly used std::string constructors. However, you can use this constructor:

std::string str(buffer, buflen): it takes a char* and a length. (actually const char* and length)

I would avoid the C string version. This would give:

std::string bufferToString(char* buffer, int bufflen)
{
    std::string ret(buffer, bufflen);

    return ret;
}

If you really must use the C-string version, either drop a 0 at the bufflen position (if you can) or create a buffer of bufflen+1, then memcpy the buffer into it, and drop a 0 at the end (bufflen position).

WhiZTiM
  • 21,207
  • 4
  • 43
  • 68
Simon Parker
  • 1,656
  • 15
  • 37
  • 4
    For Newton Falls: note that std::string(Ch* p, size_type n) won't stop at null characters if there are any in the buffer; all n characters will be copied to the string. – outis May 22 '09 at 02:37
  • Thanks Simon. I completely overlooked that constructor. This or David Dolson's suggestion of the range ctor seems a much better solution than what I was doing. – Newton Falls May 22 '09 at 03:24
  • I completely agree about avoiding the C string version if posible. Dealing with raw cstrings will only get you into trouble these days. There are several bugs in your bufferToCString, including overwriting unknown memory by using bufsize + 1, and assuming your output buffer 'str' is bufsize+1 of the input buffer. These bugs will expose themselves in subtle ways and cause you lots of debugging pain. Just use std::string if you can. – Nick Haddad May 22 '09 at 03:27
  • Great point Outis. I just tested it out with a null in the middle of the array and it printed everything up to string.length(). I would have guessed it would stop at the null. Thanks for the insight. – Newton Falls May 22 '09 at 03:28
  • Thanks Nick. I noticed that too. It's my first post and I was unsure of pasting the code so I typed it by hand and made the mistake. But your point stands and I am trying to stay away from the C level as much as possible. – Newton Falls May 22 '09 at 03:31
  • As an FYI, the SGI STL reference is still one of my favorite places to look up info on STL (like the multitude of available member functions in std::basic_string). http://www.sgi.com/tech/stl/ -- SGI STL does have some extensions over the standard, but they're pretty common extensions in my experience. – leander May 30 '09 at 16:17
  • For anyone else seeing this don't return a stack variable or pointer to one as this is doing bad things will happen. – wheredidthatnamecomefrom Jul 28 '18 at 02:19
  • wheredidthatnamecomefrom, I'm not sure what you mean. Returning a pointer/reference to a stack variable is bad, as the underlying data goes away, but returning a stack variable is fine as it will be copied. Returning a stack variable is pretty much universal for POD. – Simon Parker Jul 30 '18 at 23:32
5

If the data buffer may have null ('\0') characters in it, you don't want to use the null-terminated operations.

You can either use the constructor that takes char*, length.

char buff[4] = {'a', 'b', 'c', 'd'};
cout << std::string(&buff[0], 4);

Or you can use the constructor that takes a range:

cout << std::string(&buff[0], &buff[4]); // end is last plus one

Do NOT use the std::string(buff) constructor with the buff[] array above, because it is not null-terminated.

David Dolson
  • 300
  • 2
  • 8
1

std::string to const char*:

  my_str.c_str();

char* to std::string:

  string my_str1 ("test");
  char test[] = "test";
  string my_str2 (test);

or even

  string my_str3 = "test";
Dan Olson
  • 22,849
  • 4
  • 42
  • 56
  • The problem is that the buffer doesn't have a terminating null. – Newton Falls May 22 '09 at 02:20
  • When you initialize an array of char with a constant string, it gets a terminating null. When you use string::c_str(), you also get a terminating null. I don't understand what your complaint is. – Mark Ransom May 22 '09 at 02:27
  • The code in the answer is correct but is not the same as the question. "char buff[4] = {'a', 'b', 'c', 'd'};" does not give you a null terminated string. – markh44 May 22 '09 at 08:02
  • If it's not zero-terminated, it's not really a string. Are you certain that std::string is the best way to represent your data in this case? – Dan Olson May 22 '09 at 09:25
1

The method needs to know the size of the string. You have to either:

  1. in case of char* pass the length to method
  2. in case of char* pointing to null terminating array of characters you can use everything up to null character
  3. for char[] you can use templates to figure out the size of the char[]

1) example - for cases where you're passing the bufflen:

std::string bufferToString(char* buffer, int bufflen)
{
    return std::string(buffer, bufflen);
}

2) example - for cases where buffer is points to null terminated array of characters:

std::string bufferToString(char* buffer)
{
    return std::string(buffer);
}

3) example - for cases where you pass char[]:

template <typename T, size_t N>
std::string tostr(T (&array)[N])
{
    return std::string(array, N);
}


Usage:
char tstr[] = "Test String";
std::string res = tostr(tstr);
std::cout << res << std::endl;

For the first 2 cases you don't actually have to create new method:

 std::string(buffer, bufflen);
 std::string(buffer);
stefanB
  • 77,323
  • 27
  • 116
  • 141
0
std::string buf2str(const char* buffer)
{
    return std::string(buffer);
}

Or just

std::string mystring(buffer);
rlbond
  • 65,341
  • 56
  • 178
  • 228
0

Use string constructor that takes the size:

string ( const char * s, size_t n );

Content is initialized to a copy of the string formed by the first n characters in the array of characters pointed by s.

cout << std::string(buff, sizeof(buff)) << endl;

http://www.cplusplus.com/reference/string/string/string/

Non-null-terminated buffer to C string:

memcpy(str, buff, buffSize);
str[bufSize] = 0; // not buffSize+1, because C indexes are 0-based.
-1

string value (reinterpret_cast(buffer), length);