Adding to String array and reference - c++

I have dynamic string's array:
string *acc = new string[2];
string some_string;
I would like to add two strings to this array, but there is problem with reference ( i think).
I have following example (very ugly, but it shows the problem):
for ( int i = 0; i < 2; i ++ ) {
if ( i == 0 )
some_string = "ab";
else
some_string = "cd";
acc[i] = some_string;
some_string = "";
}
return acc
Of course this code is without any sense, but my code is more complicated and it would hide the problem.
The point is that code is returning cd instead of abcd. In my opinion some_string = ""; make mess here. Am I right?
Is it possible to do it keeping logic of code?

I really don't think you should write code this way. And you don't need temporary some_string - assigning strings is well-defined and do exactly what it should. In thi case, use vector - it is much safer approach:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
vector<string> ac;
for(int i = 0; i < 2; ++i)
{
if(i == 0)
ac.push_back("ab");
else
ac.push_back("cd");
}
cout<<ac[0]<<ac[1]<<endl;
return 0;
}
Working sample: here.

It is not clear why you expect "abcd" as result.
You assign some_string the value "cd" instead of adding to "ab".
Maybe this is what you are trying:
string* func()
{
string *acc = new string[2];
string some_string;
for ( int i = 0; i < 2; i ++ )
{
if ( i == 0 )
some_string = "ab"; // "ab"
else
some_string = some_string + "cd"; // "ab" + "cd" -> "abcd"
acc[i] = some_string;
//some_string = "";
}
return acc;
}
int main(int argc, char** argv)
{
string* y = func();
cout << y[0] << endl; // First element "ab"
cout << y[1] << endl; // Second element "abcd"
// Other stuff
// ....
// Remember to delete y
return(0);
}
Output:
ab
abcd
But as others had said.... Use vector instead.
You could also use std::array like this:
array<string, 2> func2()
{
array<string, 2> res;
string some_string;
for ( int i = 0; i < 2; i ++ )
{
if ( i == 0 )
some_string = "ab";
else
some_string = some_string + "cd";
res[i] = some_string;
}
// Or just this instead of a for loop
// res[0] = "ab";
// res[1] = "abcd";
return res;
}
int main(int argc, char** argv)
{
array<string, 2> z = func2();
cout << z[0] << endl;
cout << z[1] << endl;
return(0);
}

No, you simply made a bug:
for ( int i = 0; i < 2; i ++ ) {
if ( i == 0 ) // == instead of = !!!!!!!!!!!!!!!!!!!!!!
some_string = "ab";
else
some_string = "cd";
acc[i] = some_string;
some_string = "";
}
return acc;
Think over using a std::vector or std::list instead of pointer too. With a vector, your code looks like:
std::vector<std::string> vs;
...and later...
vs.push_back("ab");
vs.push_back("cd");

Related

How to efficiently replace characters in an std::string with iterative values from another std::string?

I have the following strings:
std::string str1 = "1234567890";
std::string str2 = "B-XXXX_XXX_V-XX_X";
I want to loop through str2 and replace every occurrence of X with the subsequent value from str1, resulting in: B-1234_567_V-89_0.
I have a semblance of a solution below, but it's not very efficient (it worked at one point). In brief, I tried to loop through the characters in str2, and if the character equaled 'X', replace that character with an incrementing index from str1:
int ind = 0;
std::string pattern_char;
for (int i = 0; i < str2.size(); i++) {
pattern_char = str2[i];
if (pattern_char == "X") {
str2[i] = str1[x_ind];
x_ind++;
}
}
What is the most efficient way to perform this operation?
Your current implementation treats each individual unit of the string as a std::string rather than as a single char, which introduces some unnecessary overhead. Here's a rewrite that uses chars:
int x_ind = 0;
for (int i = 0; i < str2.size(); i++) {
if (str2[i] == 'X') { // Don't assign str2[i] to a char; use character literals
str2[i] = str1[x_ind];
x_ind++;
}
}
You can improve readability by using a range-based for loop, like this:
int x_ind = 0;
for (char& ch: str2) {
if (ch == 'X') {
ch = str1[x_ind];
x_ind++;
}
}
If by "not very efficient" you want to improve your current code, maybe the only thing to do is to rewrite your loop:
int idx = 0;
for (char& c : str2) {
if (c == 'X')
c = str1[idx++];
}
But if you want to write this by only using standard library, you can do the same thing by using std::transform():
#include <algorithm>
#include <iostream>
#include <string>
int main()
{
std::string str1 = "1234567890";
std::string str2 = "B-XXXX_XXX_V-XX_X";
int i = 0;
std::transform(str2.begin(), str2.end(), str2.begin(),
[&str1, &i](const char& c) -> char {
return c == 'X' ? str1[i++] : c;
});
std::cout << str2 << std::endl;
}
Here's another solution:
auto i = str1.begin();
for (char& ch: str2) {
if (ch == 'X')
ch = *i++;
The only problem I see with your code is that you unnecessary use std::string pattern_char;:
#include <string>
#include <iostream>
#include <stdexcept>
auto replace_with_pattern(std::string& str, char ch_to_replace, const std::string& pattern)
{
auto pattern_it = pattern.begin();
for (char& ch : str)
{
if (ch == ch_to_replace)
{
if (pattern_it == pattern.end())
throw std::invalid_argument{"ran out of pattern"};
ch = *pattern_it;
++pattern_it;
}
}
}
int main()
{
std::string str1 = "1234567890";
std::string str2 = "B-XXXX_XXX_V-XX_X";
replace_with_pattern(str2, 'X', str1);
std::cout << str2 << std::endl;
}

How to split a string by another string in Arduino?

I have a character array like below:
char array[] = "AAAA... A1... 3. B1.";
How can I split this array by the string "..." in Arduino? I have tried:
ptr = strtok(array, "...");
and the output is the following:
AAAA,
A1,
3,
B1
But I actually want output to be
AAAA,
A1,
3.B1.
How to get this output?
edit:
My full code is this:
char array[] = "AAAA... A1... 3. B1.";
char *strings[10];
char *ptr = NULL;`enter code here`
void setup()
{
Serial.begin(9600);
byte index = 0;
ptr = strtok(array, "..."); // takes a list of delimiters
while(ptr != NULL)
{
strings[index] = ptr;
index++;
ptr = strtok(NULL, "..."); // takes a list of delimiters
}
for(int n = 0; n < index; n++)
{
Serial.println(strings[n]);
}
}
The main problem is that strtok does not find a string inside another string. strtok looks for a character in a string. When you give multiple characters to strtok it looks for any of these. Consequently, writing strtok(array, "..."); is exactly the same as writing strtok(array, ".");. That is why you get a split after "3."
There are multiple ways of doing what you want. Below I'll show you an example using strstr. Unlike strtokthe strstr function do find a substring inside a string - just what you are looking for. But.. strstr is not a tokenizer so some extra code is required to print the substrings.
Something like this should do:
int main()
{
char array[] = "AAAA... A1... 3. B1...";
char* ps = array;
char* pf = strstr(ps, "..."); // Find first substring
while(pf)
{
int len = pf - ps; // Number of chars to print
printf("%.*s\n", len, ps);
ps = pf + 3;
pf = strstr(ps, "..."); // Find next substring
}
return 0;
}
You can implement your own split as strtok except the role of the second argument :
#include <stdio.h>
#include <string.h>
char * split(char *str, const char * delim)
{
static char * s;
char * p, * r;
if (str != NULL)
s = str;
p = strstr(s, delim);
if (p == NULL) {
if (*s == 0)
return NULL;
r = s;
s += strlen(s);
return r;
}
r = s;
*p = 0;
s = p + strlen(delim);
return r;
}
int main()
{
char s[] = "AAAA... A1... 3. B1.";
char * p = s;
char * t;
while ((t = split(p, "...")) != NULL) {
printf("'%s'\n", t);
p = NULL;
}
return 0;
}
Compilation and execution:
/tmp % gcc -g -pedantic -Wextra s.c
/tmp % ./a.out
'AAAA'
' A1'
' 3. B1.'
/tmp %
I print between '' to show the return spaces, because I am not sure you want them, so delim is not only ... in that case
Because you tagged this as c++, here is a c++ 'version' of your code:
#include <iostream>
using std::cout;
using std::endl;
#include <vector>
using std::vector;
#include <string>
using std::string;
class T965_t
{
string array;
vector<string> strings;
public:
T965_t() : array("AAAA... A1... 3. B1.")
{
strings.reserve(10);
}
~T965_t() = default;
int operator()() { return setup(); } // functor entry
private: // methods
int setup()
{
cout << endl;
const string pat1 ("... ");
string s1 = array; // working copy
size_t indx = s1.find(pat1, 0); // find first ... pattern
// start search at ---------^
do
{
if (string::npos == indx) // pattern not found
{
strings.push_back (s1); // capture 'remainder' of s1
break; // not found, kick out
}
// else
// extract --------vvvvvvvvvvvvvvvvv
strings.push_back (s1.substr(0, indx)); // capture
// capture to vector
indx += pat1.size(); // i.e. 4
s1.erase(0, indx); // erase previous capture
indx = s1.find(pat1, 0); // find next
} while(true);
for(uint n = 0; n < strings.size(); n++)
cout << strings[n] << "\n";
cout << endl;
return 0;
}
}; // class T965_t
int main(int , char**) { return T965_t()(); } // call functor
With output:
AAAA
A1
3. B1.
Note: I leave changing "3. B1." to "3.B1.", and adding commas at end of each line (except the last) as an exercise for the OP if required.
I looked for a split function and I didn't find one that meets my requirement, so I made one and it works for me so far, of course in the future I will make some improvements, but it got me out of trouble.
But there is also the strtok function and better use that.
https://www.delftstack.com/es/howto/arduino/arduino-strtok/
I have the split function
Arduino code:
void split(String * vecSplit, int dimArray,String content,char separator){
if(content.length()==0)
return;
content = content + separator;
int countVec = 0;
int posSep = 0;
int posInit = 0;
while(countVec<dimArray){
posSep = content.indexOf(separator,posSep);
if(posSep<0){
return;
}
countVec++;
String splitStr = content.substring(posInit,posSep);
posSep = posSep+1;
posInit = posSep;
vecSplit[countVec] = splitStr;
countVec++;
}
}
Llamada a funcion:
smsContent = "APN:4g.entel;DOMAIN:domolin.com;DELAY_GPS:60";
String vecSplit[10];
split(vecSplit,10,smsContent,';');
for(int i = 0;i<10;i++){
Serial.println(vecSplit[i]);
}
String input:
APN:4gentel;DOMAIN:domolin.com;DELAY_GPS:60
Output:
APN:4g.entel
DOMAIN:domolin.com
DELAY_GPS:60
RESET:true
enter image description here

XOR wrong result , escaping char

I am trying to xor a small string , it works . When I try to use the XORed string , I can't even compile it.
string str = "MyNameIsMila";
string str_xored = "2*&.8"'*"; //you can't escape this or the result be be different
//Enc:2*&.8"'*:
//Dec:MyNameIsMila:
I tried to escape the string , but then I have another result at the end.
Any good direction for this ?
Output after escaping:
//Enc:yamesila:
//Dec:2*&.8"'*:
Hoped to get MyNameIsMila back.
The function looks like :
string encryptDecrypt(string toEncrypt) {
char key = 'K'; //Any char will work
string output = toEncrypt;
for (int i = 0; i < toEncrypt.size(); i++)
output[i] = toEncrypt[i] ^ key;
return output;
}
I've 2 things to say:
1: The value of a string needs to be between 2 -> ""
string str_xored = 2*&.8"'*; //this is not a valid syntax = error
//valid
string str_xored = "2*&.8";
str += '"';
str += "'*";
2: In your case I would use iterators:
#include <iostream>
#include <string>
//please don't use "using namespace std;"
std::string encryptDecrypt(std::string toEncrypt) {
char key = 'K'; //Any char will work
std::string output = ""; //needs to be empty
for (auto i = toEncrypt.begin(); i != toEncrypt.end(); i++) {
output += *i ^ key; //*i holds the current character to see how
//an iterator works have a look at my link
}
return output;
}
int main() {
std::string str = encryptDecrypt("Hello...!");
std::cout << str << std::endl;
return 0;
}
Here have a look at the (string) iterator:
Link 1
Link 2
If you think iterators are too difficult then use your
for(int i = 0; i < str.size(); i++){
//CODE
}
for()-loop
You can't treat xored strings like normal strings!
value ^ same_value == 0
Treat them as normal containers instead.
Example:
#include <iostream>
#include <iterator>
#include <algorithm>
template<typename InputIterator, typename OutputIterator, typename Key>
void perform_xor(InputIterator begin, InputIterator end, OutputIterator out, Key const &key) {
std::transform(begin, end, out, [key](auto &&value) {
return value ^ key;
});
}
using namespace std;
int main() {
char test[] = "(2*&.8\"\'*";
perform_xor(begin(test), end(test), begin(test), '&');
copy(begin(test), end(test), ostream_iterator<int>(cout, " "));
cout << endl;
perform_xor(begin(test), end(test), begin(test), '&');
copy(begin(test), end(test), ostream_iterator<char>(cout));
return 0;
}
see: http://ideone.com/ryoNp5

Compare two strings containing float values

I am given two strings which contain a floating point number. I need to compare them. Can I directly compare the strings using std::string::compare and will this always give correct results? My current approach is to convert the string to float using std::stof, however I would prefer to avoid C++11 library functions.
simply comparing strings won't help you in cases like
a = "0.43"
b = "0.4300"
if you need to compare first parse them into float and then compare them
std::string s1 = "0.6"
std::wstring s2 = "0.7"
float d1 = std::stof(s1);
float d2 = std::stof(s2);
and then compare them
here is a full program
#include <iostream> // std::cout
#include <string> // std::string, std::stof
int main ()
{
std::string s1 = "0.6"
std::wstring s2 = "0.7"
float d1 = std::stof(s1);
float d2 = std::stof(s2);
if(d1 == d2)
std::cout << "Equals!";
else
std::cout << "Not Equals!";
return 0;
}
click here for more reading on stof
What about writing some ugly codes? It may not be good practice but ...
int compare (const string &str1, const string &str2) {
string *s1 = &str1, *s2 = &str2;
int isReverse = 1;
int len1, len2;
if (str1.length() > str2.length()) {
s1 = &str2;
s2 = &str1;
isReverse = -1;
}
len1 = s1->length();
len2 = s2->length();
if (!len1) {
if (!len2))
return 0;
else if ((*s2)[0] != '-')
return 1*isReverse;
return -1*isReverse;
}
int i = 0;
while(i < len1) {
if ((*s1)[i] > (*s2)[i])
return 1*isReverse;
else if ((*s1)[i] < (*s2)[i])
return -1*isReverse;
i++;
}
while (i < len2) {
if ((*s2)[i] != '0')
return -1*isReverse;
i++;
}
return 0;
}

Strings with whitespace in a list?

I have this function sentanceParse with a string input which returns a list. The input might be something like "Hello my name is Anton. What's your name?" and then the return value would be a list containing "Hello my name is Anton" and "What's your name?". However, this is not what happens. It seems as if the whitespaces in the sentences are treated like a separator and therefore the return is rather "Hello", "my", "name" etc instead of what I expected.
How would you propose I solve this?
As I am not a 100% sure the problem does not lie within my code, I will add that to the post as well:
Main:
list<string> mylist = sentanceParse(textCipher);
list<string>::iterator it;
for(it = mylist.begin(); it != mylist.end(); it++){
textCipher = *it;
cout << textCipher << endl; //This prints out the words separately instead of the entire sentances.
sentanceParse:
list<string> sentanceParse(string strParse){
list<string> strList;
int len = strParse.length();
int pos = 0;
int count = 0;
for(int i = 0; i < len; i++){
if(strParse.at(i) == '.' || strParse.at(i) == '!' || strParse.at(i) == '?'){
if(i < strParse.length() - 1){
while(i < strParse.length() - 1 && (strParse.at(i+1) == '.' || strParse.at(i+1) == '!' || strParse.at(i+1) == '?')){
if(strParse.at(i+1) == '?'){
strParse.replace(i, 1, "?");
}
strParse.erase(i+1, 1);
len -= 1;
}
}
char strTemp[2000];
int lenTemp = strParse.copy(strTemp, i - pos + 1, pos);
strTemp[lenTemp] = '\0';
std::string strAdd(strTemp);
strList.push_back(strAdd);
pos = i + 1;
count ++;
}
}
if(count == 0){
strList.push_back(strParse);
}
return strList;
}
Your implementation of sentence parse is wrong, here is a simpler correct solution.
std::list<std::string> sentence_parse(const std::string &str){
std::string temp;
std::list<std::string> t;
for(int x=0; x<str.size();++x){
if(str[x]=='.'||str[x]=='!'||str[x]=='?'){
if(temp!="")t.push_back(temp);//Handle special case of input with
//multiple punctuation Ex. Hi!!!!
temp="";
}else temp+=str[x];
}
return t;
}
EDIT:
Here is a full example program using this function. Type some sentences in your console, press enter and it will spit the sentences out with a newline separating them instead of punctuation.
#include <iostream>
#include <string>
#include <list>
std::list<std::string> sentence_parse(const std::string &str){
std::string temp;
std::list<std::string> t;
for(int x=0; x<str.size();++x){
if(str[x]=='.'||str[x]=='!'||str[x]=='?'){
if(temp!="")t.push_back(temp);//Handle special case of input with
//multiple punctuation Ex. Hi!!!!
temp="";
}else temp+=str[x];
}
return t;
}
int main (int argc, const char * argv[])
{
std::string s;
while (std::getline(std::cin,s)) {
std::list<std::string> t= sentence_parse(s);
std::list<std::string>::iterator x=t.begin();
while (x!=t.end()) {
std::cout<<*x<<"\n";
++x;
}
}
return 0;
}
// This function should be easy to adapt to any basic libary
// this is in Windows MFC
// pass in a string, a char and a stringarray
// returns an array of strings using char as the separator
void tokenizeString(CString theString, TCHAR theToken, CStringArray *theParameters)
{
CString temp = "";
int i = 0;
for(i = 0; i < theString.GetLength(); i++ )
{
if (theString.GetAt(i) != theToken)
{
temp += theString.GetAt(i);
}
else
{
theParameters->Add(temp);
temp = "";
}
if(i == theString.GetLength()-1)
theParameters->Add(temp);
}
}