How to convert char* array into std::string - c++

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.

Related

How can I copy a string variable to another character by character without defining size of target variable?

I was writing a program where I need to copy a string (length unknown initially) to another string character by character.
I write this code that runs perfectly.
string a = "Hello";
string b( a.length(), 'a');
for (int i = 0; i < a.length(); i++)
b[i] = a[i];
But as the size of the string in C++ is calculated dynamically as
string k = "Hello";
string l = "Hello World";
string m = k;
m = l;
won't give any error. So it is clear that size of a string variable is changing dynamically according to the requirement.
So I tried the same without defining the size of the variable b.
string a = "Hello";
string b;
for (int i = 0; i < a.length(); i++)
b[i] = a[i];
But in this case, my program is crashing and saying String subscript out of range.
Why not in this case the size of variable b is increasing?
What is the cause of the error and how can I achieve this without mentioning the size of any variable?
If you must, you can use push_back:
string a = "Hello";
string b;
for (int i = 0; i < a.length(); i++)
b.push_back(a[i]);
or even1
string a = "Hello";
string b;
for (auto c : a)
b.push_back(c);
But why?
Reference: http://en.cppreference.com/w/cpp/string/basic_string/push_back
1 But don't do this if a is changed in any way: the underlying iterators will be invalidated.
You can simply use the += operator:
for (auto el : a){
b += el;
}
The std::string::operator+= has an overload that accepts character.
Size of the string is dynamic, but it not changed automaticaly as you expect. operator[] only return i-th element, if current size of a string is less than i, it fill fail. It's up to you to make sure size of a string is big enough.
If you couldn't resize string in advance you can query size, increase it by one, ask string to resize, and set last char.
This is how push_back method works

error: variable length array of non-POD element type 'string' (aka 'basic_string<char>')

I know eventually I need to change trigram, whose one space contains 3 characters from the former string, into a dynamic array to solve this problem, but I tried to set my array's capacity large enough at first. However, when I compile my code, the error appears.
#error: variable length array of non-POD element type 'string' (aka 'basic_string<char>'#
Code:
//global variable
int CAPACITY = 1000;
int main()
{
//a string that reads in the language of the text
string language = "";
//a string that reads in the file name of the text
string filename = "text.txt";
//a string that reads in the original text characters
string original = "";
//a string that reads in the modified original array
string rid_of_spaces = "";
//an array with capacity that stores the trigrams
string trigrams[CAPACITY];
ifstream finput;
char c;
//the length of an array
int sLength = 0;
//the tracker for trigrams
int counter = 0;
cin >> language >> filename;
finput.open(filename.c_str());
while (finput.get(c)){
//to test if the character is alpha
if (isalpha(c)){
//change the alphabet to lowercase
c = tolower(c);
//store the modified letter in the array
original += c;
}
//change any other characters into a space
else original += ' ';
}
sLength = original.length();
//loop through the original array and change mutiple spaces into one
for (int i = 0; i < sLength; i++){
if (isalpha(original[i]))
rid_of_spaces += original[i];
else {
while (original[i] == ' ')
i++;
rid_of_spaces += ' ';
rid_of_spaces += original[i];
}
}
sLength = rid_of_spaces.length();
for (int i = 0; i < CAPACITY; i++)
trigrams[i] = 0;//initialize each element to 0
for (int i = 0; i < sLength - 2; i++){
trigrams[counter] += rid_of_spaces[i]
+ rid_of_spaces[i + 1]
+ rid_of_spaces[i + 2];
counter++;
}
cout << filename << endl;
cout << original << endl;
cout << rid_of_spaces << endl;
for (int i = 0; i < counter; i++)
cout << trigrams[i] << endl;
finput.close();
return 0;
}
The variable
int CAPACITY = 1000;
should be a constant
const int CAPACITY = 1000; // or with c++11 constexpr int CAPACITY = 1000;
for
string trigrams[CAPACITY];
because "ISO C++ forbids variable length array 'trigrams'" (g++ message)
And this
for (int i = 0; i < CAPACITY; i++)
trigrams[i] = 0;//initialize each element to 0
should be
for (int i = 0; i < CAPACITY; ++i)
trigrams[i] = "";//initialize each element to 0
You don't "initialize [strings] to 0" but with a zero length C-string. A zero length C-string is not an invalid 0-pointer, but a (valid) pointer to a char with value 0;
Generally, it's better not to use C-arrays if there are STL-means to avoid them; with c++11, std::array<std::string, CAPACITY> would be preferable here if you want to stay with the "capacity large enough" approach.
live at Coliru's
I took the liberty to change all i++ to ++i in the for-loops' heads while at it; see eg. What is the difference between ++i and i++ for the rationale behind that.
For a dynamic (without pre-defined bounds) array use std::vector<std::string> trigrams;,
push_back or emplace_back your strings into that vector,
and for i- iterate
for (std::size_t i = 0; i < trigrams.size(); ++i) {/* ... */}
Or use the iterator-interface of std::vector, e.g.
std::for_each(trigrams.begin(), trigrams.end(),
some_function_or_functor_that_does_the_job);
(see std::foreach here ),
or with c++11 just
for (auto& s : trigrams) {/* ... */}
unless you need to customize the iteration like you do it inside your second loop.
The variable CAPACITY is not a compile-time constant variable, and variable-length arrays are not in C++ (though some have it as an extension, but apparently not for all types).
There are two solutions to your problem:
Turn the variable into a compile-time constant, either by making it constexpr alternatively const (for older compilers), or by defining it as a preprocessor macro.
Use std::vector, like std::vector<std::string> trigram(CAPACITY);
My suggestion is that you use both solutions above, at least if you later need to resize the vector. If the size will be fixed at compile-time and never change, then use the first solution and use std::array instead of a C-style array:
constexpr std::size_t CAPACITY = 1000;
...
std::array<std::string, CAPACITY> trigram;
The size of a C++ array must be a constant expression. You declare it as int CAPACITY = 1000;, which is not a constant expression. Adding a const qualifier solves the issue: int const CAPACITY = 1000;.
However, you should avoid plain arrays. Try using std::array if you know the size at compile time, or std::vector if not.

Convert string vector to buffer

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().

Parsing a string down into an array of c_strings

I wrote some code in order to turn a string (read in with getline()) into an array of c_strings. The problem I'm having is that the items I'm reading is not being stored in the array properly. I originally parsed the input based on the number of spaces in between them, and then going on from there, but that also got me the same problem. So I changed my parsing into what's below me, and I'm getting the same exact problem, suggesting to me that my parsing works, but somewhere in the process of reading what's parsed into the char* array, something is going wrong.
My code:
int i = 0;
unsigned inputSize = input.size();
unsigned int prev = 0; //prev as in previous space position
while((prev = input.find(' ', prev)) < inputSize) {
++i; ++prev;
}
char* charArray[i + 2];
memset(charArray, '\0', i + 2);
stringstream ss(input);
string buffer;
for(int a = 0; ss >> buffer; ++a) {
charArray[a] = const_cast<char*>(buffer.c_str());
}
What I'm doing is that I'm counting the number of spaces of my input, and making a char* array of that number + 2 (+2 because I need to end it with NULL). After that, I parse my input and read it into the array. I am using ss >> buffer as my termination clause because I will not end up allocating memory outside the allocated memory for charArray. buffer.c_str gets me a const char*, so I const_cast it in order for me to store it into the (non-const) array of char*. I use memset to set all elements to NULL because I know it'll be written over, except the last element, which I want to remain NULL.
Test:
Input: Why hello world
Output: Junk
What's going wrong inside my program?
The pointer returned by buffer.c_str() is valid only as long as the string stored in buffer is not modified. If you need to modify buffer, you have to copy its contents beforehand, if you need the old content later on.
See Right way to split an std::string into a vector [duplicate].
Live Example
#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
using namespace std;
int main(int argc, char *argv[]) {
std::string input = "Why hello world";
std::stringstream ss(input);
std::vector<std::string> vstrings(std::istream_iterator<std::string>(ss),
std::istream_iterator<std::string>{}); // C++11 brace syntax
char** charArray = new char*[vstrings.size()];
for (unsigned int i = 0; i < vstrings.size(); i++)
charArray[i] = (char*)vstrings[i].c_str();
for (unsigned int i = 0; i < vstrings.size(); i++)
std::cout << charArray[i] << "\n";
delete[] charArray;
}

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. ;)