C++ Extracting the integer in a string with multiple delimiters - c++

I am trying to extract the integers from a string. What could be wrong here?
I only get the first value. How can I get it working even with zero's in the string?
string str="91,43,3,23,0;6,9,0-4,29,24";
std::stringstream ss(str);
int x;
while(ss >> x)
{
cout<<"GOT->"<<x<<endl;
char c;
ss >> c; //Discard a non space char.
if(c != ',' || c != '-' || c != ';')
{
ss.unget();
}
}

Look very closely at this line:
if(c != ',' || c != '-' || c != ';')
Note that this condition is always true, so you are always ungeting the punctuation character. The next read will then always fail as it reads punctuation when a number is expected. Changing the ||'s to &&'s should fix the problem.
Of course, your code assumes that str is formatted in a very particular way and might break when given a differently-formatted str value. Just be aware of that.

u can get this done with boost split.
int main() {
std::stringstream ss;
std::string inputString = "91,43,3,23,0;6,9,0-4,29,24";
std::string delimiters("|,:-;");
std::vector<std::string> parts;
boost::split(parts, inputString, boost::is_any_of(delimiters));
for(int i = 0; i<parts.size();i++ ) {
std::cout <<parts[i] << " ";
}
return 0;
}
Output (Just integers) :- 91 43 3 23 0 6 9 0 4 29 24

This will change the string into char and write off : , ; -
#include <iostream>
#include <string>
using namespace std;
int main(){
string str = "91,43,3,23,0;6,9,0-4,29,24";
str.c_str(); // ex: string a; --> char a[];
char a[99];
int j = 0;
int x;
for(int i = 0; i < str.length(); i++){
if (str[i]!=',' && str[i]!=';' && str[i]!='-'){
a[j] = str[i];
j++;
}
}
return 0;
}
Hope this will help you.

This suits my purpose where in I can extract the integers and also add the delimiters if necessary. Works with different formatted strings as well.
(I dont have boost lib, hence preferring this method. )
int main()
{
string str="2,3,4;0,1,3-4,289,24,21,45;2";
//string str=";2;0,1,3-4,289,24;21,45;2"; //input2
std::stringstream ss(str);
int x=0;
if( str.length() != 0 )
{
while( !ss.eof() )
{
if( ss.peek()!= ',' && ss.peek()!=';' && ss.peek()!='-') /*Delimiters*/
{
ss>>x;
cout<<"val="<<x<<endl;
/* TODO:store integers do processing */
}
ss.get();
}
}
}

You can also try:
vector<int> SplitNumbersFromString(const string& input, const vector<char>& delimiters)
{
string buff{""};
vector<int> output;
for (auto n : input)
{
if (none_of(delimiters.begin(), delimiters.end(), [n](const char& c){ return c == n; }))
{
buff += n;
}
else
{
if (buff != "")
{
output.push_back(stoi(buff));
buff = "";
}
}
}
if (buff != "") output.push_back(stoi(buff));
return output;
}
vector<char> delimiters = { ',', '-', ';' };
vector<int> numbers = SplitNumbersFromString("91,43,3,23,0;6,9,0-4,29,24", delimiters);

Related

changing case for first and last letter C++

i need ThE FirsT AnD LasT letter of every word to be uppercase and the rest lowercase but function convertFirstAndLastLetter only does the first and last of the sentence.
void convertFirstAndLastLetter(char wrd[])
{
size_t last = strlen(wrd) - 1;
size_t first = 0;
wrd[first] = toupper(wrd[first]);
wrd[last] = toupper(wrd[last]);
for(int i = first + 1 ; i < last - 1; i++)
{
wrd[i] = tolower(wrd[i]);
}
}
int main ()
{
float val;
cout << "ent ";
cin >> val;
if (val == 4)
{
char wrd[256];
cin.ignore();
cin.getline(wrd,256);
convertFirstAndLastLetter(wrd);
cout << wrd;
return 0;
}
}
Right now you're passing a whole line to your function, so the first and last characters are all that is being changed.
To fix that you need to split the line up into words which could be done by reading a word at a time using std::cin >> word, or reading the whole line and then using a std::stringstream to split it up. Both are about the same, but I used a stringstream.
Next, if I'm reading your statement correctly, you want to have the first and last "letter" of each word uppercase and the rest lowercase. That means we need to find the first and last letter in case there is punctuation before or after the word.
Once the first and last are determined it's easy to uppercase them and lowercase the rest.
#include <iostream>
#include <string>
#include <sstream>
#include <cctype>
void convertFirstAndLastLetter(std::string& word)
{
if(!word.empty())
{
size_t first = 0;
size_t last = word.size() - 1;
//Find first alpha character
while(first < word.size() && !std::isalpha(word[first]))
{
++first;
}
//If the position is valid, uppercase it
if(first < word.size())
{
word[first] = static_cast<char>(std::toupper(word[first]));
}
//Find last alpha character
while(last > first && !std::isalpha(word[last]))
{
--last;
}
//If the position is valid, uppercase it
if(last > first)
{
word[last] = static_cast<char>(std::toupper(word[last]));
}
for(size_t i = first + 1; i < word.size() && i < last; ++i)
{
if(std::isalpha(word[i]))
{
word[i] = static_cast<char>(std::tolower(word[i]));
}
}
}
}
int main()
{
float val;
std::cout << "ent ";
std::cin >> val;
std::cin.ignore();
if(val == 4)
{
std::string line;
if(std::getline(std::cin, line))
{
std::string word;
std::stringstream ss(line);
while(ss >> word)
{
convertFirstAndLastLetter(word);
std::cout << word << " ";
}
std::cout << "\n";
}
}
return 0;
}
Keep in mind this will discard any extra spaces at the beginning and end of each line as well as any extra spaces between words. The way I am printing them out will also add an extra space at the end of each line. You didn't mention any concern about those things in your question, so I didn't worry about them. They are all solvable with a little extra effort.
Now with less duplicated code:
#include <algorithm>
#include <iostream>
#include <string>
template<typename It>
void upperFirstAlpha(It begin, It end)
{
auto first = std::find_if(begin, end, isalpha);
if (first != end)
*first = toupper(*first);
}
int main()
{
for (std::string s; std::cin >> s;) {
std::transform(s.begin(), s.end(), s.begin(), tolower);
upperFirstAlpha(s.begin(), s.end());
upperFirstAlpha(s.rbegin(), s.rend());
std::cout << s << ' ';
}
}
As #Retired Ninja pointed out the first version wouldn't work for punctuation, whether this version is what you are looking for depends on what your input looks like.
This function
void convertFirstAndLastLetter(char wrd[])
{
size_t last = strlen(wrd) - 1;
size_t first = 0;
wrd[first] = toupper(wrd[first]);
wrd[last] = toupper(wrd[last]);
for(int i = first + 1 ; i < last - 1; i++)
{
wrd[i] = tolower(wrd[i]);
}
}
is wrong. For example if character array is empty that is it has only the terminating zero then expression strlen(wrd) - 1 will give you the maximum value for an object of type size_t and in the next statement
wrd[last] = toupper(wrd[last]);
you will try to change memory beyond the array.
Also if you use standard function strlen then you have to include header <cstring>.
In fact there is no any need to use function strlen. The code can be written simpler
#include <cctype>
//...
void convertFirstAndLastLetter( char wrd[] )
{
if ( *wrd )
{
*wrd = std::toupper( *wrd );
if ( *++wrd )
{
while ( *( wrd + 1 ) )
{
*wrd = std::tolower( *wrd );
++wrd;
}
*wrd = std::toupper( *wrd );
}
}
}
#include "stdafx.h"
#include <string>
#include <iostream>
using namespace std;
void convertFirstAndLastLetter(char wrd[])
{
size_t last = strlen(wrd) - 1;
size_t first = 0;
wrd[first] = toupper(wrd[first]);
wrd[last] = toupper(wrd[last]);
for(int i = first + 1 ; i < last; i++)
{
wrd[i] = tolower(wrd[i]);
}
}
int _tmain(int argc, _TCHAR* argv[])
{
float val;
cout << "ent ";
cin >> val;
if (val == 4)
{
char wrd[256];
cin.ignore();
cin.getline(wrd,256);
convertFirstAndLastLetter(wrd);
cout << wrd;
return 0;
}
}

Split a string in C++ using 2 delimiters '+' & '-"

i am trying to split a string with 2 delimiters '+' and '-' in C++
using a string find a delimiter...
can anyone give me a go around...
Using
str.find(delimiter)
example :
a+b-c+d
Output Required:
a
b
c
d
Thanks in advance
Using std::string::substr and std::string::find
std::vector<std::string> v ; //Use vector to add the words
std::size_t prev_pos = 0, pos;
while ((pos = str.find_first_of("+-", prev_pos)) != std::string::npos)
{
if (pos > prev_pos)
v.push_back(str.substr(prev_pos, pos-prev_pos));
prev_pos= pos+1;
}
if (prev_pos< str.length())
v.push_back(str.substr(prev_pos, std::string::npos));
Or if you use boost it will be lot easier
#include <boost/algorithm/string.hpp>
std::vector<std::string> v;
boost::split(v, line, boost::is_any_of("+-"));
you can do this for variable delimiters as well
void main void()
{
char stringToUpdate[100] , char delimeters[4];
/*
write code to assign strings and delimeter
*/
replaceDelimeters(stringToUpdate, delimeters, ' ');
}
void replaceDelimeters(char* myString, char* delimeters, char repChar)
{
for (int i = 0; delimeters[i] != '\0'; i++)
{
for(int j=0; stringtoUpdate[j] != '\0'; j++)
{
if(stringtoUpdate[j] == delimeters[i])
{
stringtoUpdate[j] = repChar;
}
}
}
}
Use the function "char* strtok(char* src, const char* delimiters)"
http://en.cppreference.com/w/cpp/string/byte/strtok
char* s = "a+b-c+d";
char* p = strtok(s, "+-");
while (p != NULL)
{
// do something with p
p = strtok (NULL, "+-");
}

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;
}

Extracting integer values from string elements and adding up

I have the following lines of code:
vector<string> c;
string a;
for(int i=0;i<4;i++){
cin>>a;
c.push_back(a);
}
If I provide input as:
120$,132$,435$,534$
How can I extract the integer values separately and add them up to get the total value?
You can use e.g. std::getline with a custome "line" separator using the comma, strip the last character from the string (the '$') and use std::stoi to convert to an integer:
std::vector<int> c;
for (int i = 0; i < 4; i++)
{
std::string a;
std::getline(std::cin, a, ',');
a = a.substr(a.length() - 1); // Remove trailing dollar sign
c.push_back(std::stoi(a));
}
Edit: Using std::accumulate:
int sum = std::accumulate(c.begin(), c.end(), 0);
Edit 2: Using std::strtol instead of std::stoi:
The function std::stoi is new in the latest C++ standard (C++11) and it not supported in all standard libraries yet. Then you can use the older C function strtol:
c.push_back(int(std::strtol(a.c_str(), 0, 10)));
You can use regex and streams:
#include <regex>
#include <iostream>
#include <sstream>
const std::string Input("120$,132$,435$,534$");
int main(int argc, char **argv)
{
const std::regex r("[0-9]+");
int Result = 0;
for (std::sregex_iterator N(Input.begin(), Input.end(), r); N != std::sregex_iterator(); ++N)
{
std::stringstream SS(*N->begin());
int Current = 0;
SS >> Current;
Result += Current;
std::cout << Current << '\n';
}
std::cout << "Sum = " << Result;
return 0;
}
Output:
120
132
435
534
Sum = 1221
If you must ensure that the number is followed by a '$' then change the regex to: "[0-9]+\\$" the stringstream part will ignore the trailing '$' in the number conversion:
#include <regex>
#include <iostream>
#include <sstream>
const std::string Input("120$,132$,435$,534$,1,2,3");
int main(int argc, char **argv)
{
const std::regex r("[0-9]+\\$");
int Result = 0;
for (std::sregex_iterator N(Input.begin(), Input.end(), r); N != std::sregex_iterator(); ++N)
{
std::stringstream SS(*N->begin());
int Current = 0;
SS >> Current;
Result += Current;
std::cout << Current << '\n';
}
std::cout << "Sum = " << Result;
return 0;
}
Output:
120
132
435
534
Sum = 1221
If the input isn't too large (and particularly if it comes as a single
line), the simplest solution is to pack it all into a string, and parse
that, creating a std::istringstream to convert each of the numeric
fields (or using boost::lexical_cast<>, if by some odd chance it has
the appropriate semantics—it normally does when translating a
string to a built-in numeric type). For something this simple, it's
possible, however, to read directly from a stream, however:
std::istream&
ignoreDollar( std::istream& stream )
{
if ( stream.peek() == '$' ) {
stream.get();
}
return stream;
}
std::istream&
checkSeparator( std::istream& stream )
{
if ( stream.peek() == ',' ) {
stream.get();
} else {
stream.setstate( std::ios_base::failbit );
}
return stream;
}
std::vector<int> values;
int value;
while ( std::cin >> value ) {
values.push_back( value );
std::cin >> ignoreDollar >> checkSeparator;
}
int sum = std::accumulate( values.begin(), values.end(), 0 );
(In this particular case, it might be even simpler to just do everything
in the while loop. The manipulators are a generally useful technique,
however, and can be used in a wider context.)
A simple version:
int getIntValue(const std::string& data)
{
stringstream ss(data);
int i=0;
ss >> i;
return i;
}
int getSum(std::vector<std::string>& c)
{
int sum = 0;
for (auto m = c.begin(); m!= c.end(); ++m)
{
sum += getIntValue(*m);
}
return sum;
}
Done

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