Convert string vector to buffer - c++

I have a string vector which contains some number of strings greater than 500. I am using openssl functions that require buffers for encryption/decryption. Given that I am using string vectors and buffers in this manner, what is the best algorithm in terms of space and time to make this conversion. Each string can be assumed to be less than 200 chars.
Currently, I am extracting each entry in paths, concatenating the strings, calling the .c_str() method and using strcpy to extract from a function.
void copy(vector<string>& paths, unsigned char** plainTextBuffer, size_t& p_len){
int size = paths.size();
int i = 0;
string temp = "";
for(i=0; i<size; i++){
temp+= paths[i];
temp+= "\n";
}
p_len = temp.length();
(*plainTextBuffer) = malloc(p_len + 1);
strcpy((*plainTextBuffer), temp.c_str());
return;
}
Are there any built in tools to do this better and faster? (I have excluded error checking and casting from this snippet)
Edit:
I added the +1 to the malloc. I asked for a minimum complexity manner of getting from the initial conditions to the expected output. I am using malloc because I am using a simple buffer and it is faster than new.
Edit 2:
Thanks to some of the comments I am cutting out the middleman with some of the copying and have the following
void copy(vector<string>& paths, unsigned char** plainTextBuffer, size_t& p_len){
int size = paths.size(), total = 0, i = 0;
for(i=0; i<size; i++){
total+= paths[i].size() + 1;
}
p_len = total;
(*plainTextBuffer) = malloc(p_len + 1);
(*plainTextBuffer)[0] = '\0';
for(i=0; i<size; i++){
strcat((*plainTextBuffer), paths[i].c_str());
strcat((*plainTextBuffer, "\n");
}
return;
}
Again I left out some casting. Is there a more efficient manner of getting the buffer data into the plainTextBuffer?

The fastest way to convert a string to a C-style string is to not do any conversions. So first, let's convert our std::vector<std::string> into one std::string:
std::vector<std::string> v = ...;
std::string output;
for (auto& s : v) {
output += s;
output += '\n';
}
And then you can pass that in:
void some_c_api(char*, size_t );
void some_const_c_api(const char*, size_t);
some_c_api(&output[0], output.size());
some_const_c_api(output.c_str(), output.size());

Appending repeatedly into a string will result in the string repeatedly reallocating memory and shuffling its content into the new bigger space. A stringstream has a bigger creation cost, but it appends much faster than std::string, so instead of appending to a string in a loop, append to a stringstream:
stringstream temp;
for(size_t i=0; i<paths.size(); i++){
temp << paths[i] << endl;
}
const std::string& tmp = temp.str();
Then just use tmp like you would have used your previous temp string. It is better to get a constant reference to temp.str() because it will not copy the content of the temporary created by str().

Related

After padding a string with zeroes - it prints unspecified characters? (C++)

Basically, here, I'm trying to reverse an array, and convert the reversed int array into a string (I'm trying to write the equivalent of Java's BigInteger class in C++ - basically turning the input into big endian ordering, breaking down the operations, reversing the result back to little endian format, and returning the string).
And as you can see below, it outputs some strange characters (I think it's an out of range reference?) - but I'm not entirely sure what caused this output?
I would really appreciate if you could take a look at it:
Sample input
int a[] = {1, 2, 3};
int rA[3];
reverseIntArray(a, rA, 3);
string aString = intArrayToString(a, 3);
cout << aString << endl;
Console output
123\216\377
As you can see - it calculates the answer correctly, with the exception of the \277_\377.
I'll post the rest of the relevant functions:
reverseIntArray
void reverseIntArray(int array[], int reversedArray[], int arrayLength) {
for (int i = 0; i < arrayLength; i++) {
reversedArray[i] = array[arrayLength - 1 - i];
}
}
intArrayToString
string intArrayToString(int digits[], int length) {
// convert int array to char array
char digitsChar[length];
for (int i = 0; i < length; i++) {
digitsChar[i] = '0' + digits[i];
}
// convert char array to string
string intString(digitsChar);
return intString;
}
I'm quite sure this is a subtle issue to do with pointers, but I'm still relatively new to C++ (migrating from Java) and I've stared at this for hours but haven't come up with any ideas.
The std::string constructor you are using is assuming that the string you pass is properly terminated, which it isn't and that leads to undefined behavior as the std::string constructor goes beyond the end of the digitsChar array.
Three possible solutions:
Make room for another character in the digitsChar array and terminate it:
char digitsChar[size + 1];
for (...) { ... }
digitsChar[3] = '\0';
string intString(digitsChar);
Use another constructor where you pass the length of the character array:
string intString(digitsChar, length);
Append the characters directly to the string:
string intString;
for (int i = 0; i < length; i++) {
intString += '0' + digits[i];
}
There are of course other solutions as well, like for example using std::ostringstream.

Get string content from string pointer C++

so I'm working on a project that I have to read contents from a file and then analyze them. But I'm having a problem with getting the string out of a pointer that contains the address to what I need.
string lePapel(vector<char> vec){
string *str, s;
int i, j = 0;
vector<char> aux;
aux.resize(6);
for (i = 57; i <= 62; i++){
aux[j] = vec[i];
j++;
}
str = new string[aux.size()];
for (i = 0; i < 6; i++){ str[i] = aux[i]; }
return s;
}
So, the file contains in the array positions from 57 to 62 the word: ABCB4, but when returning the string s my output is A only as expected because of the pointer.
The thing is that I have been trying to find a solution and storing the whole content from vec[57] to vec[64] into the string s and returning it, and the closest that I got to returning anything plausible was using a pointer.
So, now to my question, how can I iterate the *str pointer and copy the whole content to s and return it?
Thanks in advance
I'd suggest you to not use pointers on string in your case. The following code is probably what you want :
#include <iostream>
#include <string>
#include <vector>
using namespace std;
string lePapel(vector<char> vec){
int j = 0;
vector<char> aux;
aux.resize(6);
for (int i = 57; i <= 62; i++){
aux[j] = vec[j];
j++;
}
string str;
str.reserve(6);
for (int i = 0; i < 6; i++){ str.push_back(aux[i]); }
return str;
}
int main() {
char x[5] = {'A', 'B', 'C', 'B', '4'};
vector<char> vec(x, x + 5);
string s = lePapel(vec);
cout << s;
return 0;
}
Tested here : Tested code
About reserving space to your vector : c++ vector::reserve
Same for strings : reserve for strings
The dynamic array of string objects and the whole aux vector seem completely needless here (unless there's some other purpose for them in your code). Additionally, str is currently causing a memory leak because you never delete it when you're finished.
A much simpler approach is just to append the characters one-at-a-time to the s string object (assuming it's a std::string):
string lePapel(vector<char> vec) {
string s;
for (int i = 57; i <= 62; i++) {
s += vec[i];
}
return s;
}
There are various ways to make the code even shorter (and more efficient) than that though, if you really want to.
EDIT: If you still need/want to iterate your dynamic array and concatenate the contents into s, here's how you could do it:
for (i = 0; i < 6; i++) s += str[i];
delete [] str; //<-- very important!
Short answer, you don't want a string * you want a char *. What you created is a string array. String objects contain a pointer to the char * data you are trying to capture. Also, the sizeof(std::string) (8 bytes in size) is a lot bigger than sizeof(char) (1 byte in size) the second character you store is 8 bytes away from the first character instead of being adjacent.
There are a lot of other C++ style and safety concerns, but I'll stick with the question. ;)

How to convert char* array into std::string

I declared a char * array char *excluded_string[50] = { 0 };
Later each element of excluded_string array gets one word. Now I want to convert it into string so that I can have all words seperated by space.
std::string ss(excluded_string); gives error:
`server.cpp:171:32: error: no matching function for call to ‘std::basic_string::basic_string(char* [50])’ and large tricky explaination!
I declared char * array char *excluded_string[50] = { 0 };
Later each element of ex_str array gets one word. Now I want to convert it into string so that I can have all words seperated by space.
To convert it into a single string:
char *excluded_string[50] = { 0 };
// excluded_string filled in the meantime
std::ostringstream buffer; // add #include <sstream> at the top of
// the file for this
for(int i = 0; i < 50; ++i)
buffer << excluded_string[i] << " ";
std::string result = buffer.str();
Edit: A few notes:
if possible, do not concatenate strings directly: that will create and destroy a lot of objects and perform lots of unnecessary allocations.
if your code has stringent efficiency requirements, consider allocating/reserving the result beforehand to ensure a single allocation instead of repeated allocations.
if you concatenate strings, consider using operator += instead of + and =.
Edit 2: (answering comments)
What if + and = instead of +=?
Here's the resolution of the two alternatives for concatenating strings (s += s1 + s2 vs s += s1; s += s2):
Using = and +:
code:
std::string ss;
for (int i=0; i<50; i++)
ss += std::string(excluded_string[i]) + " ";
Equivalent code (in terms of objects constructed and allocations):
std::string ss;
for (int i=0; i<50; i++)
{
// ss += std::string(excluded_string[i]) + " ";
std::string temp1(excluded_string[i]); // "std::string(excluded_string[i])"
std::string temp2 = temp1 + " "; // call std::string operator+(std::string, char*)
ss += temp2; // call std::string::operator +=(std::string)
}
temp1 is created once per iteration;
temp2 is created for the concatenation operator
the second temporary is appended to ss.
Both temporaries create a copy of the data (allocate buffer, copy data, deallocate buffer).
Using += twice:
code:
std::string ss;
for (int i=0; i<50; i++)
{
ss += excluded_string[i]; // call std::string::operator +=(char*)
ss += " "; // same as above
}
std::string::operator += is called twice; It allocates space (if necessary), copies current contents of the string to newly allocated space, then copies new data at the end of the allocated buffer.
single pre-allocated space:
allocating/reserving the result beforehand to ensure a single allocation
std::size_t total_length = 0;
for(int i = 0; i < 50; ++i)
total_length += std::strlen(excluded_strings[i]); // assumes argument is not null
std::string ss;
ss.reserve(total_length + 51); // reserve space for the strings and spaces between
for (int i=0; i<50; i++)
{
ss += excluded_string[i]; // calls std::string::operator +=
ss += " "; // same as above
}
In this case, operator+= doesn't allocate space internally, just at the beginning (a single operation). This is still a bit slow, because you iterate over the strings twice (0->49) and over each string twice (once to compute length, once to copy it to ss).
If your excluded_string were a std::vector instead, it would be more efficient because computing the strings lengths would not iterate each string, just the vector).
Possible solution
Since you took care to initialize your array of pointers to c_str to 0, we can use that knowledge to only add actually allocated words :
Also, you need to first build a std::string based on the original c_str before you can use the concatenation operator+.
std::string stringResult;
for (int i=0; i!=50; ++i)
if(excluded_string[i])
{
stringResult.append(std::string(excluded_string[i]) + " ");
}
Details about the original error
The type of your excluded_string object is a static array of 50 pointers to char. All pointers to char being initialized to 0 by your code.
A pointer to char can be referred as a C string, or more concisely c_str.
C++ STL gives you the std::string class, for which several constructors are defined. One of them taking a c_str (i.e. a pointer to char) to initialize the string (pedantically, converting it to a pointer to const char, which is an implicit conversion in this expression).
This constructor is the one we use in the solution when we write:
std::string(excluded_string[i])
But as you can see, there is no constructor taking an array of c_str, which is exactly what you compiler error's tells you.
EDIT : Wording, thanks to Lightness Races in Orbit comment. (cast meaning in fact explicit conversion).
#include <iostream>
#include <string.h>
#include <algorithm>
#include <vector>
#include <sstream>
#include <iterator>
using namespace std;
int main()
{
char *arr[10] = {0};
for(int i = 0; i < 10; i++)
arr[i] = "wang";
ostringstream str;
copy(arr, arr+10, ostream_iterator<string>(str, " "));
cout<<str.str()<<endl;
}
Try a loop:
std::string ss;
for (int i=0; i < 50; i++)
ss += std::string(excluded_string[i]) + " ";
You're code in the best situation will put first string in ss.

Concatenating strings in Visual Studio C++ 6.0 in a loop

Can you help me optimize this block of code in C++ for Visual Studio C++ 6.0:
char output[10000] = "";
for (int i = 0; i < cnt; i++) {
char row[150];
_snprintf(row, 149, "…", …);
row[149] = '\0';
strcat(output, row);
}
return _snprintf(buffer, size-1, "%s\r\nend\r\n", output);
What I need is that I do not specify size of output[] but increase it dynamically. The same maybe true for row[]. Sorry I'm novice in C++.
Thanks for any help.
In C++, you ought to use std::string for strings instead of char arrays, and std::stringstream and its cousins std::istringstream and std::ostringstream instead of sprintf() or snprintf() for formatting in string buffers. Here’s the basis of a C++ solution:
std::ostringstream result;
for (int i = 0; i < cnt; ++i) {
result << "...\n";
}
result << "end\n";
return result.str();
The std::string class handles all of the details of managing memory, and std::stringstream uses std::string internally.
std::stringstream coupled with operator << works like a charm.
I think the use of C++ containers given as answers are not as optimized as you can get. There has been no memory reserved to begin with, and the results do not copy into the buffer as in your supplied code.
You can still do better like this:
char suffix[] = "\r\nend\r\n";
int suffix_len = strlen(suffix);
char *buf_end = buffer + size - suffix_len - 1;
char *buf_begin = buffer;
for (int i = 0; i < cnt; i++) {
int nchars = _snprintf(buf_begin, buf_end-buf_begin, "…", …);
if( nchars >= 0 ) {
buf_begin += nchars;
} else {
// You may want to set an overflow flag here.
buf_begin = buf_end;
break;
}
}
// There will always be enough room in the buffer to store the suffix, so
// this will null-terminate even if the above loop overflowed the buffer.
_sprintf(buf_begin, "%s", suffix);
I've modified this to write directly into buffer instead of output. It utilizes the fact that the _sprintf family returns the number of characters written (or negative if max chars written). To me, this is the preferable way to concatenate data into a buffer, even in C++.
If you are using MFC, it is very easy using the CString class:
// loop count
int nCount = 100;
CString strOutput, strOne;
for (int i=0 ; i<nCount ; i++)
{ // format one line
strOne.Format(_T("..."), ...);
// accumulate the result
strOutput += strOne;
}
return strOutput;

String concatenation C++

Given an arbitrary floating point number, say -0.13, suppose we have an algorithm which calculates a binary string of known length L for this number, one by one, from left to right.
(I need to do this computation for calculating the Morton Key ordering for particles(co-orindates given) which in turn in used in building octrees. I am creating
such binary strings for each of x,y,z dimensions)
Is it better/efficient to first create a character array of length L, and then convert this array into a string? i.e.
char ch[L];
for(i = 0; i < L; ++i)
{
// calculation of ch[i]
}
//convert array into string
Or is it better/efficient to start of with a empty string, and then concatenate a new calculated bit into the string on the fly. i.e.
string s = "";
for(i = 0; i < L; ++i)
{
// calculation of ch[i]
s = s + string(ch);
}
Why not do both?
std::string myStr(L);
for(i = 0; i < L; ++i)
{
// calculation of ch[i]
myStr[i] = ch;
}
This creates a std::string with a given size. You then just set each character. This will only work if you can know the size beforehand exactly.
Alternatively, if you want something that is safe, even if you have to add more than L characters:
std::string myStr;
myStr.reserve(L);
for(i = 0; i < L; ++i)
{
// calculation of ch[i]
myStr.push_back(ch);
}
std::string::reserve preallocates the storage, but push_back will allocate more if needs be. If you don't go past L characters, then you will only get the one initial allocation.
Can't you just use a string with a pre-allocated length?
string s(L, '\0');
for(i = 0; i < L; ++i)
{
// calculation of ch[i]
}
I'm not sure I fully understand the conversion happening, but we have objects for a reason. If you use std::string::reserve() first, the performance should be minuscule, and it's obvious what the intent is.
string s;
s.reserve(L);
for(i = 0; i < L; ++i)
{
// calculation of ch[i]
string.push_back(ch);
}
If speed is absolutely necessary, you can instead initialize the string as length L, and bypass length checks:
string s(L,'\0');
for(i = 0; i < L; ++i)
{
// calculation of ch[i]
string[i] = ch;
}
Personally, i am probably out of date, but i use
sprintf ( char * str, const char * format, ... );
to create strings from numbers
sprintf ( outString,"%f", floatingPointNumber);
Use the latter, but also call s.reserve(L) before entering the loop. This is almost as efficient as direct array assignment, but still easier to grok.
EDIT: Other answers suggest using push_back(). Vote for them.
Sidebar: I'm not sure what you are computing, but if you just want to generate a string representation of the number, I'd suggest you simply call sprintf(), or insert the number into a std::stringstream.
If you want the C++ way, use ostringstream. This is generally cleaner code, less error-prone, and easier to read:
float f = ... // whatever you set it to
std::ostringstream s;
s << f;
std::string stringifiedfloat = s.str();
// now you have the float in a string.
Alternately, you can use the C way, sprintf. This is generally more error-prone, and harder to read, but faster performance:
float f = ... // whatever you set it to
char* buffer = new char[L];
sprintf(buffer, "%f", f);
// now you have the float in a string.
Or, you could even use boost's lexical_cast. This has better performance than ostringstream, and better readability than sprintf, but it gives you a dependency on boost:
float f = ... // whatever you set it to
std::string stringified = boost::lexical_cast<std::string>(f);
// now you have the float in a string.