How scan two strings separated by `/` using sscanf? - c++

I want to scan to separate strings separated by a / using sscanf but it doesn't work. It works fine with a space.
For example, I want to separate the string 50%/60% into two strings like 50% and 60%.
You can have a look at code here:
#include <iostream>
using namespace std;
int extract_break_rewrites(int *m, int *n, const char *arg)
{
char m_str[10];
char n_str[10];
int err;
int count = sscanf(arg, "%s %s", n_str, m_str);
printf("%s %s %d\n",n_str, m_str,count);
if (count == 0) {
count = sscanf(arg, "/%s", m_str);
if (count == 0) {
*m = 0;
*n = 0;
return -1;
}
if (sscanf(m_str, "%d%%", m) != 1)
return -1;
}
else if (count == 1) {
if (sscanf(n_str, "%d%%", n) != 1)
return -1;
}
else if (count==2) {
if (sscanf(n_str, "%d%%", n) != 1)
return -1;
if (sscanf(m_str, "%d%%", m) != 1)
return -1;
}
return 1;
}
int main() {
int n,m;
const char * command = "50% 60%";
if (extract_break_rewrites(&m,&n,command)!=-1)
cout<<"Successful. The values of m and n are "<<m<<" and "<<n<<", respectively.\n";
else
cout<<"There was error in processing, may be input was not in the correct format.\n";
return 0;
}
You don't need to worry about what the code does, the important lines are 10, 11 and main function.

Try the following (assuming from stdin):
scanf("%[^/]/%s");
Use sscanf(buf, ...); if reading from a buffer.
The issue is that %s for scanf assumes that the string is followed by a space. This approach instructs scanf to find a string delimited by /, and then match the rest as a separate string.
EDIT: accidentally dropped the / in the scan string

Use a scan set
char a[100];
char b[100];
scanf("%[^/]/%s", a, b);
This scans in everything until it gets a /, then it starts and reads in a string.

You can also use std::strings and their facilities to achieve the same result:
#include <iostream>
#include <string>
using std::string;
using std::cout;
using std::stoi;
bool extract_break_rewrites(int &m, int &n, const string &arg) {
// find position of %/ in the string
string::size_type pos_first = arg.find("%/");
// check if the return value is valid (the substring is present and there
// is something else first)
if ( pos_first == string::npos || !pos_first ) // wrong input
return false;
string::size_type pos_2 = pos_first + 2,
pos_last = arg.find("%", pos_2);
if ( pos_last == string::npos || pos_last == pos_2 )
return false;
try {
m = stoi(arg.substr(0, pos_first));
n = stoi(arg.substr(pos_2, pos_last - pos_2));
}
// invalid argument or out of range
catch (...) {
return false;
}
return true;
}
int main() {
int n = 0, m = 0;
string command = "150%/60%";
if ( extract_break_rewrites(m, n, command) )
cout << "Successful. The values of m and n are "
<< m << " and " << n << ", respectively.\n";
else
cout << "There was error in processing, "
<< "maybe input was not in the correct format.\n";
return 0;
}

Related

Palindrome but with a scentence

So writing a palindrome with pointers and boolean. I have it working with a single word but then I began building it to work with a sentence. The problem is I am unsure how to keep the new modified sentence after making it lowercase and getting rid of the spaces for it to return whether it is or isn't a palindrome. It keeps returning the palindrome as false and when I went to check why I see that the program ignores the modification and kept the original string. I can't use "&" on the parameter as I tested it out. Any hints or takes on what I can do to keep the new modified string?
int main()
{
userInput();
return 0;
}
void userInput()
{
char str[90];
std::cout<<"Please enter a string to check if it is a palindrome: ";
std::cin.getline(str, 90);
modifyString(str);
}
void modifyString(char *string)
{
int count = 0;
for (int i=0; i<strlen(string); i++)
{
putchar(tolower(string[i]));
}
for (int i = 0; string[i]; i++)
{
if (string[i] != ' ')
{
string[count++] = string[i];
}
}
string[count] = '\0';
std::cout<<string<<std::endl;
results(string);
}
bool checkPalindrome(char *string)
{
char *begin;
char *end;
begin = string;
end = (string + strlen(string)-1);
while(begin != end)
{
if ((*begin) == (*end))
{
begin ++;
end--;
}
else
{
return false;
}
}
return true;
}
void results(char *string)
{
bool isItPalindrome;
isItPalindrome = checkPalindrome(string);
if( isItPalindrome == true)
{
std::cout<<"\nCongrats, the string is a palindrome!";
}
else
{
std::cout<<"\nThis string is not a palindrome.";
}
}
For starters this definition of main
int main()
{
userInput();
return 0;
}
does not make a sense. According to the function name main the function should perform the main task that is to output whether the entered sentence is a palindrome or not.
This for loop
for (int i=0; i<strlen(string); i++)
{
putchar(tolower(string[i]));
}
does nothing useful. It just outputs the string in the lower case.
This statement
end = (string + strlen(string)-1);
can invoke undefined behavior if an empty string was passed.
This while loop
while(begin != end)
{
if ((*begin) == (*end))
{
begin ++;
end--;
}
else
{
return false;
}
}
also can invoke undefined behavior for a string containing an even number ofo characters because after this if statement
if ((*begin) == (*end))
{
begin ++;
end--;
}
if the two adjacent characters are equal then begin after incrementing will be greater than end after its decrementing. And as a result the loop will continue its iteration.
In general the approach when the original string is changed is just a bad approach.
Your program has too many functions. It is enough to write one function that will determine whether the passed string is a palindrome or not.
Here is a demonstrative program.
#include <iostream>
#include <cstring>
#include <cctype>
bool checkPalindrome( const char *s )
{
const char *t = s + std::strlen( s );
do
{
while ( s != t && std::isspace( ( unsigned char )*s ) ) ++ s;
while ( s != t && std::isspace( ( unsigned char )*--t ) );
} while ( s != t &&
std::tolower( ( unsigned char )*s ) == tolower( ( unsigned char ) *t ) &&
++s != t );
return s == t;
}
int main()
{
const size_t N = 100;
char s[N] = "";
std::cout << "Please enter a string to check if it is a palindrome: ";
std::cin.getline( s, N );
std::cout << '\n';
if ( checkPalindrome( s ) )
{
std::cout << "Congrats, the string is a palindrome!\n";
}
else
{
std::cout << "This string is not a palindrome.\n";
}
return 0;
}
Its output might look like
Please enter a string to check if it is a palindrome: 1 23 456 6 54 321
Congrats, the string is a palindrome!
Okay, I solved it!
As one of the users on here brought up a point that my lowercase did not modify the string and only prints it out. I try my best to solve the problem and I think I found the solution and everything works perfectly fine. comment back to debug it if you like to see how it looks but what I did was create a for loop again for the lower case but made another pointer with it. here how it looks.
for (char *pt = string; *pt != '\0'; ++pt)
{
*pt = std::tolower(*pt);
++pt;
}
Now that definitely changes the string into a lower case and keeps it as a lower case.
so now the modified function looks like this and ready to take any sentence palindrome you give it. Example: A nUt fOr a jAr of tUNa. We make this all lowercase and take out space and boom palindrome and return true.
void modifyString(char *string)
{
int count = 0;
for (char *pt = string; *pt != '\0'; ++pt)
{
*pt = std::tolower(*pt);
++pt;
}
for (int i = 0; string[i]; i++)
{
if (string[i] != ' ')
{
string[count++] = string[i];
}
}
string[count] = '\0';
//take out the forward slash below to see how it looks after being modified
// std::cout<<std::endl<<string<<std::endl;
results(string);
}

How do I make a recursive string detector in c++ using only 2 parameters

int charFrequency( string str, char c,int subscript) {
if (subscript >= str.length())
{
return 0;
}
else if (str[subscript] == c)
{
return 1 + charFrequency(str, c, subscript+ 1);
}
else
{
return charFrequency(str, c, subscript+ 1);
}
}
this is my recursive function it is working nothing wrong with it, however I have to modify this to make it work with only two parameters the string and the char c I can't have the extra subscript parameter in the function but I can figure out how to make it work, I guess I'm mental blocked from doing it this way. Any help would be appreciated thank you.
I think you mean something like this:
#include <iostream>
using namespace std;
int charFreq(const char * str, char c) {
if (str[0] == '\0') {
return 0;
} else if (str[0] == c) {
return 1 + charFreq(str + 1, c);
} else {
return charFreq(str + 1, c);
}
}
int main()
{
string smt = "Hello World";
cout << "freq: " << charFreq(smt.c_str(), 'l') << "\n";
return 0;
}
With the str + 1 you move the pointer in char array which is accessible from std::string trough .c_str(). Bevare of the ending '\0' character which are standard part of c-style strings. But if you supply some regular char array it may lead to infinite loop or program crash.
Updated version with string:
int charFreq(string str, char c) {
if (str.length() == 0) {
return 0;
} else if (str[0] == c) {
return 1 + charFreq(str.substr(1), c);
} else {
return charFreq(str.substr(1), c);
}
}

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

trying to implement a recursive version of detecting a palindrome within a string using C++. having some trouble here

Having trouble trying to implement a recursive version for detecting a palindrome. Cannot get correct output :(
#include <iostream>
#include <string>
using namespace std;
bool testPalindrome( string, unsigned int length, int begin );
int main()
{
string test;
cout << "Enter what you wish to test for a palindrome: ";
cin >> test;
unsigned int len = test.length(); // acquire length of string
int beginning = 0; // set variable to point to beginning of string
if ( testPalindrome( test, len, beginning ) )
cout << "PALINDROME!" << endl;
else
cout << "NOTHING" << endl;
}
The code above is the main function I am using to test the palindrome function I am implementing. Below, I have also provided the code I wrote to detect a palindrome.
// implemented recursive function to check for a palindrome
bool testPalindrome( string name, unsigned int len, int begin )
{
// conditional to determine if beginning char position is equal to last char
if ( begin >= len )
return true; // if so, return true
// check if characters are equal, if not return false
else if ( name[ begin ] != name[ len ] )
return false;
// general case, call function with positions of characters being tested
// shifted by one slot each, respectively
else
return testPalindrome( name, ( len - 1 ), ( begin + 1 ) );
}
You haven't done a good job explaining what exactly your problem is, but I suspect your issue is that you're indexing len into name instead of len - 1. Indexing into strings is zero-indexed and not one-indexed so the index len is invalid.
#include <iostream>
#include <string>
using namespace std;
bool isPalindrome(string S, int len, int index) {
while (index <= len / 2) {
return (S[index] == S[len - index - 1]) && isPalindrome(S, len, index + 1);
}
return true;
}
int main() {
string S = "racecar";
if (isPalindrome(S, S.size(), 0)) {
cout << "Is a Palindrome\n";
} else {
cout << "Not a Palindrome\n";
}
return 0;
}
This should do it!
In the code that you wrote, while calling the function, use :
testPalindrome(name, len - 1, 0);
instead of:
testPalindrome(name, len, 0);
And since you are passing len - 1 as the argument, change type of len from unsigned int to int to prevent errors with NULL strings.
And add a condition checker :
if (len == -1) {
return true;
}
NULL strings are palindromes. without this condition your code will through SEGMENTATION FAULT for NULL strings.
Change the center line in testPalindrome() to
else if ( name[ begin ] != name[ len - 1 ] )
because name[len] reads a character behind the end of your string.

Complex algorithm to extract numbers/number range from a string

I am working on a algorithm where I am trying the following output:
Given values/Inputs:
char *Var = "1-5,10,12,15-16,25-35,67,69,99-105";
int size = 29;
Here "1-5" depicts a range value, i.e. it will be understood as "1,2,3,4,5" while the values with just "," are individual values.
I was writing an algorithm where end output should be such that it will give complete range of output as:
int list[]=1,2,3,4,5,10,12,15,16,25,26,27,28,29,30,31,32,33,34,35,67,69,99,100,101,102,103,104,105;
If anyone is familiar with this issue then the help would be really appreciated.
Thanks in advance!
My initial code approach was as:
if(NULL != strchr((char *)grp_range, '-'))
{
int_u8 delims[] = "-";
result = (int_u8 *)strtok((char *)grp_range, (char *)delims);
if(NULL != result)
{
start_index = strtol((char*)result, (char **)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
while(NULL != result)
{
end_index = strtol((char*)result, (char**)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
while(start_index <= end_index)
{
grp_list[i++] = start_index;
start_index++;
}
}
else if(NULL != strchr((char *)grp_range, ','))
{
int_u8 delims[] = ",";
result = (unison_u8 *)strtok((char *)grp_range, (char *)delims);
while(result != NULL)
{
grp_list[i++] = strtol((char*)result, (char**)&end_ptr, 10);
result = (int_u8 *)strtok(NULL, (char *)delims);
}
}
But it only works if I have either "0-5" or "0,10,15". I am looking forward to make it more versatile.
Here is a C++ solution for you to study.
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int ConvertString2Int(const string& str)
{
stringstream ss(str);
int x;
if (! (ss >> x))
{
cerr << "Error converting " << str << " to integer" << endl;
abort();
}
return x;
}
vector<string> SplitStringToArray(const string& str, char splitter)
{
vector<string> tokens;
stringstream ss(str);
string temp;
while (getline(ss, temp, splitter)) // split into new "lines" based on character
{
tokens.push_back(temp);
}
return tokens;
}
vector<int> ParseData(const string& data)
{
vector<string> tokens = SplitStringToArray(data, ',');
vector<int> result;
for (vector<string>::const_iterator it = tokens.begin(), end_it = tokens.end(); it != end_it; ++it)
{
const string& token = *it;
vector<string> range = SplitStringToArray(token, '-');
if (range.size() == 1)
{
result.push_back(ConvertString2Int(range[0]));
}
else if (range.size() == 2)
{
int start = ConvertString2Int(range[0]);
int stop = ConvertString2Int(range[1]);
for (int i = start; i <= stop; i++)
{
result.push_back(i);
}
}
else
{
cerr << "Error parsing token " << token << endl;
abort();
}
}
return result;
}
int main()
{
vector<int> result = ParseData("1-5,10,12,15-16,25-35,67,69,99-105");
for (vector<int>::const_iterator it = result.begin(), end_it = result.end(); it != end_it; ++it)
{
cout << *it << " ";
}
cout << endl;
}
Live example
http://ideone.com/2W99Tt
This is my boost approach :
This won't give you array of ints, instead a vector of ints
Algorithm used: (nothing new)
Split string using ,
Split the individual string using -
Make a range low and high
Push it into vector with help of this range
Code:-
#include<iostream>
#include<vector>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
int main(){
std::string line("1-5,10,12,15-16,25-35,67,69,99-105");
std::vector<std::string> strs,r;
std::vector<int> v;
int low,high,i;
boost::split(strs,line,boost::is_any_of(","));
for (auto it:strs)
{
boost::split(r,it,boost::is_any_of("-"));
auto x = r.begin();
low = high =boost::lexical_cast<int>(r[0]);
x++;
if(x!=r.end())
high = boost::lexical_cast<int>(r[1]);
for(i=low;i<=high;++i)
v.push_back(i);
}
for(auto x:v)
std::cout<<x<<" ";
return 0;
}
You're issue seems to be misunderstanding how strtok works. Have a look at this.
#include <string.h>
#include <stdio.h>
int main()
{
int i, j;
char delims[] = " ,";
char str[] = "1-5,6,7";
char *tok;
char tmp[256];
int rstart, rend;
tok = strtok(str, delims);
while(tok != NULL) {
for(i = 0; i < strlen(tok); ++i) {
//// range
if(i != 0 && tok[i] == '-') {
strncpy(tmp, tok, i);
rstart = atoi(tmp);
strcpy(tmp, tok + i + 1);
rend = atoi(tmp);
for(j = rstart; j <= rend; ++j)
printf("%d\n", j);
i = strlen(tok) + 1;
}
else if(strchr(tok, '-') == NULL)
printf("%s\n", tok);
}
tok = strtok(NULL, delims);
}
return 0;
}
Don't search. Just go through the text one character at a time. As long as you're seeing digits, accumulate them into a value. If the digits are followed by a - then you're looking at a range, and need to parse the next set of digits to get the upper bound of the range and put all the values into your list. If the value is not followed by a - then you've got a single value; put it into your list.
Stop and think about it: what you actually have is a comma
separated list of ranges, where a range can be either a single
number, or a pair of numbers separated by a '-'. So you
probably want to loop over the ranges, using recursive descent
for the parsing. (This sort of thing is best handled by an
istream, so that's what I'll use.)
std::vector<int> results;
std::istringstream parser( std::string( var ) );
processRange( results, parser );
while ( isSeparator( parser, ',' ) ) {
processRange( results, parser );
}
with:
bool
isSeparator( std::istream& source, char separ )
{
char next;
source >> next;
if ( source && next != separ ) {
source.putback( next );
}
return source && next == separ;
}
and
void
processRange( std::vector<int>& results, std::istream& source )
{
int first = 0;
source >> first;
int last = first;
if ( isSeparator( source, '-' ) ) {
source >> last;
}
if ( last < first ) {
source.setstate( std::ios_base::failbit );
}
if ( source ) {
while ( first != last ) {
results.push_back( first );
++ first;
}
results.push_back( first );
}
}
The isSeparator function will, in fact, probably be useful in
other projects in the future, and should be kept in your
toolbox.
First divide whole string into numbers and ranges (using strtok() with "," delimiter), save strings in array, then, search through array looking for "-", if it present than use sscanf() with "%d-%d" format, else use sscanf with single "%d" format.
Function usage is easily googling.
One approach:
You need a parser that identifies 3 kinds of tokens: ',', '-', and numbers. That raises the level of abstraction so that you are operating at a level above characters.
Then you can parse your token stream to create a list of ranges and constants.
Then you can parse that list to convert the ranges into constants.
Some code that does part of the job:
#include <stdio.h>
// Prints a comma after the last digit. You will need to fix that up.
void print(int a, int b) {
for (int i = a; i <= b; ++i) {
printf("%d, ", i);
}
}
int main() {
enum { DASH, COMMA, NUMBER };
struct token {
int type;
int value;
};
// Sample input stream. Notice the sentinel comma at the end.
// 1-5,10,
struct token tokStream[] = {
{ NUMBER, 1 },
{ DASH, 0 },
{ NUMBER, 5 },
{ COMMA, 0 },
{ NUMBER, 10 },
{ COMMA, 0 } };
// This parser assumes well formed input. You have to add all the error
// checking yourself.
size_t i = 0;
while (i < sizeof(tokStream)/sizeof(struct token)) {
if (tokStream[i+1].type == COMMA) {
print(tokStream[i].value, tokStream[i].value);
i += 2; // skip to next number
}
else { // DASH
print(tokStream[i].value, tokStream[i+2].value);
i += 4; // skip to next number
}
}
return 0;
}