How to get data from user input? - c++

I have a QTableWidget where the user inputs complex numbers in various styles.
For example, the complex number (-15 + 8.14i) can be written like:
-15 + 8.14i
-15+8.14j
-15 +j 8,14
-15+ i8,14
i can also be j!
Both values can be big (they are saved as double) and also negative. They can be written with "," and "." (so 3.14 and 3,14 are ment to be equal). There should be an error message when the user enters the number incorrectly.
CKomplex fromString(QString str) { // ckomplex is my custom class for complex numbers
double numReal, numImag;
QString strNew = "";
// delete all spaces
for (int i= 0; i< str.length(); i++) {
if (!str[i].isSpace()) {
strNew += str[i];
}
}
QString part1 = "";
int index;
// get the first number
for (int i= 0; i < strNew.length(); i++) { // iterate string
if (strNew[i] != '+' && strNew[i] != '-') {
part1 += strNew[i];
} else { // e.g.: 5 + 3j -> the loop is at the "+"
if (i != 0) {
index = i; // save index at "+" to start for next number
break;
}
}
}
numReal = part1.toDouble();
QString part2 = "";
// get the second number
for (int i= index; i < strNew.length(); i++) {
if (strNew[i].isDigit() || strNew[i] == '+' || strNew[i] == '-' || strNew[i] == '.' || strNew[i] == ',') { // ignore j or i
part2 += strNew[i];
}
}
numImag = part2.toDouble();
return CKomplex(numReal, numImag);
}
This does work for basic input. But is not very fast or readable or useful. And it does cover just few possibilities of input (stuff like "-3 - 5,14" doesnt work). Is there an easier way to convert the string into a complex number without having so much loops and variables?

A single regular expression could parse each of the lines:
#include <string>
#include <sstream>
#include <vector>
#include <iterator>
#include <regex>
#include <iostream>
#include <iomanip>
#include <exception>
class CKomplex {
public:
CKomplex(double numReal, double numImag) : numReal{numReal}, numImag{numImag} {}
double numReal;
double numImag;
};
auto input_text{
R"(-15 + 8.14i
-15+8.14j
-15 +j 8,14
-15+ i8,14
bad line here
+23.4-j24
-35+42.3j
+24i
+2.342j
+24.523-i 432,52
24.523-i 432,52
23.4-j24
35+42.3j
24i
2.342j)"};
CKomplex fromString(std::string str) {
double numReal{};
double numImag{};
std::regex r{R"(([+-]?) *([ij]?) *(\d+)[.,]?(\d*)([ij])?)"}; // 6 groups
std::istringstream iss(str);
auto it = std::sregex_iterator(str.begin(), str.end(), r);
auto end = std::sregex_iterator();
if(it == end || it->size() != 6)
throw std::runtime_error("Could not parse line containing the following text: " + str);
for(; it != end; ++it) {
auto match = *it;
auto sign = match[1].str();
auto iorj_pre = match[2].str();
auto decimal = match[3].str();
auto fraction = match[4].str();
auto iorj_post = match[5].str();
double val{sign == "-" ? -1.F : 1.F};
val *= std::stod(decimal + "." + fraction);
if(iorj_pre == "i" || iorj_pre == "j" || iorj_post == "i" || iorj_post == "j")
numImag += val;
else
numReal += val;
}
return{numReal,numImag};
}
std::ostream& operator<<(std::ostream& os, const CKomplex& complex_number)
{
os << std::showpos << "(" << complex_number.numReal << " " << complex_number.numImag << "i)";
return os;
}
int main()
{
std::istringstream input_stream{input_text};
for(std::string line{}; std::getline(input_stream, line);) {
try { std::cout << std::setw(20) << line << ": " << fromString(line) << '\n'; }
catch(std::exception& e) { std::cout << e.what() << '\n'; }
}
return 0;
}
Produces (live demo):
-15 + 8.14i: (-15 +8.14i)
-15+8.14j: (-15 +8.14i)
-15 +j 8,14: (-15 +8.14i)
-15+ i8,14: (-15 +8.14i)
Could not parse line containing the following text: bad line here
+23.4-j24: (+23.4 -24i)
-35+42.3j: (-35 +42.3i)
+24i: (+0 +24i)
+2.342j: (+0 +2.342i)
+24.523-i 432,52: (+24.523 -432.52i)
24.523-i 432,52: (+24.523 -432.52i)
23.4-j24: (+23.4 -24i)
35+42.3j: (+35 +42.3i)
24i: (+0 +24i)
2.342j: (+0 +2.342i)

Related

Remove the words from a string that start with a certain character

I have to create a function in C++ that would remove all the words from a string that start with a certain character inputted by a user. For example, if I were to have a string "She made up her mind to meet up with him in the morning" and a substring "m", I would like my string to be "She up her to up with him in the".
I believe I would need to find the occurrences of "m", erase it and all the characters after it till the space " ". Would that be the right approach and if so what would be the best methods to use in this case?
With your kind help I have altered and added code a little bit. The first function 'GetNextWord' seems to be working alright, however, there is definitely something wrong with my function, which is supposed to strip the words, as I am not getting any output. Here is the code:
string GetNextWord(string& s, size_t pos) {
string word;
char del = ' ';
int i = 0;
for (int i = 0; i < s.length(); i++) {
if (s[i] != del) {
word += s[i];
}
else break;
}
return word;
}
string StripWordsThatBeginWithLetter(string& s, char c) {
string result;
string word;
size_t pos = 0;
while (true)
{
word = GetNextWord(s, pos);
pos += word.size() + 1;
if (word.size() == 0)
{
break;
}
if (word[0] == c) {
size_t inx = 0;
inx = s.find(word[0]);
s.erase(inx, word.length());
}
else result = s;
}
return result;
}
Here's a hint. I'm guessing this is a homework problem. And I'm probably giving too much away.
std::string GetNextWord(const std::string &s, size_t pos)
{
std::string word;
// your code goes here to return a string that includes all the chars starting from s[pos] until the start of the next word (including trailing whitespace)
// return an empty string if at the end of the string
return word;
}
std::string StripWordsThatBeginWithLetter(const std::string& s, char c)
{
std::string result;
std::string word;
size_t pos = 0;
while (true)
{
word = GetNextWord(s, pos);
pos += word.size();
if (word.size() == 0)
{
break;
}
// your code on processing "word" goes here with respect
// to "c" goes here
}
return result;
}
Simple example in french. You are a gentleman and dont want to say "merde" too often, and so decided not to say any word starting with 'm'.
This program will help you :
"je suis beau merde je sais" becomes "je suis beau je sais"
#include <string>
#include <algorithm>
int main(){
std::string str ("je suis beau merde je le sais");
const auto forbiden_start ((const char) 'm');
std::cout << "initial rude string (word starting with \'" << forbiden_start << "\') : " << str << std::endl;
auto i (str.begin ());
auto wait (false);
std::for_each (str.begin (), str.end (), [&i, &forbiden_start, &wait] (const auto& c) {
if (wait) {
if (c == ' ') {
wait = false; return;
}
}
else {
if (c == forbiden_start) {
wait = true;
}
else *i++ = c;
}
});
if (i != str.end ()) str.erase (i, str.end ());
std::cout << "polite string : " << str << std::endl;
return 0;
}
All is not tested (separator is " "), but it is the idea

A better solution for comparing string patterns.?

Task : Create a function that returns true if two strings share the same letter pattern, and false otherwise.
I found a way to solve this task but I think it could be more simple and short. I converted all same letters to a specific char character for 2 strings. Then end of the process checked whether they are same or not. Any ideas for simpler solutions ?
#include <iostream>
#include <string>
using namespace std;
bool LetterPattern(string str1, string str2) {
// Controlling whether they have same size or not
if (str1.length() != str2.length()) {
return false;
}
else {
// Checking for ABC XYZ format type
int counter = 0;
for (int i = 0; i < str1.length()-1; i++) {
for (int k = i+1; k < str1.length(); k++) {
if (str1[i] == str1[k]) {
counter++;
}
}
}
int counter2 = 0;
for (int i = 0; i < str2.length() - 1; i++) {
for (int k = i + 1; k < str2.length(); k++) {
if (str2[i] == str2[k]) {
counter2++;
}
}
}
if (counter == 0 && counter2 == 0) {
return true;
}
// I added the above part because program below couldn't return 1 for completely different letter formats
// like XYZ ABC DEF etc.
//Converting same letters to same chars for str1
for (int i = 0; i < str1.length()-1; i++) {
for (int k = i+1; k < str1.length(); k++) {
if (str1[i] == str1[k]) {
str1[k] = (char)i;
}
}
str1[i] = (char)i;
}
}
//Converting same letters to same chars for str1
for (int i = 0; i < str2.length() - 1; i++) {
for (int k = i + 1; k < str2.length(); k++) {
if (str2[i] == str2[k]) {
str2[k] = (char)i;
}
}
str2[i] = (char)i;
}
if (str1 == str2) { // After converting strings, it checks whether they are same or not
return true;
}
else {
return false;
}
}
int main(){
cout << "Please enter two string variable: ";
string str1, str2;
cin >> str1 >> str2;
cout << "Same Letter Pattern: " << LetterPattern(str1, str2);
system("pause>0");
}
Examples:
str1
str2
result
AABB
CCDD
true
ABAB
CDCD
true
AAFFG
AAFGF
false
asdasd
qweqwe
true
As you want to see if one string is a Caesar cipher of the other, you might do:
bool LetterPatternImpl(const std::string& str1, const std::string& str2) {
if (str1.length() != str2.length()) { return false; }
std::array<std::optional<char>, 256> mapping; // char has limited range,
// else we might use std::map
for (std::size_t i = 0; i != str1.length(); ++i) {
auto index = static_cast<unsigned char>(str1[i]);
if (!mapping[index]) { mapping[index] = str2[i]; }
if (*mapping[index] != str2[i]) { return false; }
}
return true;
}
bool LetterPattern(const std::string& str1, const std::string& str2) {
// Both ways needed
// so ABC <-> ZZZ should return false.
return LetterPatternImpl(str1, str2) && LetterPatternImpl(str2, str1);
}
By 1 iteration on strings create key-value pairs That define corresponding characters.
In the second iteration check whether each character in the first/second string is compatible with the character with equal index in the second/second string. If there is no incompatibility return true, otherwise false.
First, as you did we can compare the size of 2 strings.
If they are equal we continue.
By iterating on 1 of the strings we can fill a map. Keys of the map are characters seen in the first string and its value is the corresponding character in the second string.
By reaching the nth character we check that whether we have a key or the same as this character or not.
If yes: Check the value that is equal to the nth character of the second string.
If no: we add a new key-value to the map. (the key is the nth character of the first string and the value is the nth character of the second string)
1.
After doing this we should do this again for another string. I mean for example if in the first step characters of the first string were keys, In the second step we should replace the string in the way that characters of second string become keys.
If both of them give true the answer is true. Otherwise false.
2.
Rather than replacing strings and repeat the iteration, we can prevent repetitive values to be added to the map.
To understand paragraph 1 and 2 imagine 1 iteration on strings of "ABC" and "ZZZ".
Notice that arrays can be used instead of map.
And, last but not least, an additional solution using "counting".
If we read the requirement, then you are only interested in a boolean result. That means, as soon as we have a 2nd association for a letter in the first string, then the result is false.
Example: If we have an 'a' and in the 2nd string at the same position a 'b', and then in some next position of the first string again an 'a' but then in the same position of the 2nd string a 'c', then we have 2 different associations for the letter a. And that is false.
If there is only one association per letter, then everything is ok.
How to accomplish "association" and "counting". For the "association, we will use an associative container, a std::unordered_map. And, we associate a letter from the first string, with a std::set of the already processed letters (from the 2nd string). The std::sets iinsert function will not add double letters from the secondt string. So, if there is again a 'b' associated with an 'a', that is completly fine.
But if there is a different associated letter, then the std::set will contain 2 elements. That is an indicator for a false result.
In such case, we stop evaluation characters immediately. This leads to a very compact and fast code.
Please see:
#include <iostream>
#include <string>
#include <unordered_map>
#include <utility>
#include <set>
bool letterPattern(const std::string& s1, const std::string& s2) {
// Here we will store the result of the function
bool result{ s1.length() == s2.length() };
// And here all associations
std::unordered_map<char, std::set<char>> association{};
// Add associations. Stop if result = false
for (size_t index{}; result && index < s1.length(); ++index)
if (const auto& [iter, ok] {association[s1[index]].insert(s2[index])}; ok)
result = association[s1[index]].size() == 1;
return result;
}
// Some driver test code
int main() {
std::vector<std::pair<std::string,std::string>> testData{
{"AABB", "CCDD"},
{"ABAB", "CDCD"},
{"AAFFG", "AAFGF"},
{"asdasd", "qweqwe"}
};
for (const auto& p : testData)
std::cout << std::boolalpha << letterPattern(p.first, p.second) << "\t for: '" << p.first << "' and '" << p.second << "'\n";
return 0;
}
Not sure about better, but a C++17 solution that builds a regular expression based on the first string's letters and matches it against the second:
#include <iostream>
#include <sstream>
#include <string>
#include <unordered_map>
#include <tuple>
#include <regex>
bool match(const std::string &pattern, const std::string &s) {
std::unordered_map<char, int> indexes;
std::ostringstream builder;
int ref = 1;
for (char c : pattern) {
if (auto backref = indexes.find(c); backref != indexes.end()) {
builder << '\\' << backref->second;
} else {
if (ref > 1) {
builder << "(?!";
for (int n = 1; n < ref; n += 1) {
if (n != 1) {
builder << '|';
}
builder << '\\' << n;
}
builder << ')';
}
builder << "(.)";
indexes.emplace(c, ref++);
}
}
// std::cout << builder.str() << '\n';
return std::regex_match(s, std::regex{builder.str()});
}
int main() {
std::tuple<std::string, std::string, bool> tests[] = {
{"AABB", "CCDD", true},
{"ABAB", "CDCD", true},
{"AAFFG", "AAFGF", false},
{"asdasd", "qweqwe", true},
{"abc", "zzz", false}
};
std::cout << std::boolalpha;
for (const auto &[s1, s2, expected] : tests) {
if (match(s1, s2) == expected) {
std::cout << s1 << " => " << s2 << " = " << expected << ": PASS\n";
} else {
std::cout << s1 << " => " << s2 << " = " << (!expected) << ": FAIL\n";
}
}
return 0;
}
A simple (maybe not very efficient) approach:
#include<iostream>
#include<unordered_map>
using namespace std;
int main(void) {
string s1, s2;
unordered_map<string, char> subs;
cout<<"Enter the strings: ";
cin >> s1 >> s2;
if (s1.length() != s2.length())
cout<<"False"<<endl;
else {
for (int i=0; i<s1.length(); ++i) {
string key(1, s2[i]);
subs[key] = s1[i];
}
string s1_2 = "";
for (int i=0; i<s2.length(); ++i) {
string key(1, s2[i]);
s1_2 += subs[key];
}
if (s1 == s1_2)
cout<<"True"<<endl;
else
cout<<"False"<<endl;
}
return 0;
}
Time Complexity O(n); Space Complexity O(n)
If I understood right and:
AABB - CCDD = true
AAFFG - AAFGF = false
asdasd - qweqwe = true
That's not pattern, it's check if second string is result of encryption by substitution of first. You can do it in simpler way, by attempting to built substitution table. If it fails, i.e. there are more than one association between source and result, the outcome is false.
Simplest case is that we have to check whole string. If we would need to find that if any substring is substitution of pattern contained in second string, that squares the complexity:
#include <string>
#include <vector>
#include <map>
#include <optional>
#include <limits>
bool is_similar (const std::string& s1, const std::string& s2)
{
if(s1.length() != s2.length()) return false;
using TCh = std::decay_t<decltype(s1)>::value_type;
// for non-unicode characters can use an array
//std::optional<TCh> table[ std::numeric_limits<TCh>::max ];
// std::optional used for clarity, in reality may use `TCh`
// and compare with zero char
std::map< TCh, std::optional<TCh>> table;
for (size_t it = 0; it < s1.length(); ++it)
{
if( table[s1[it]].has_value() && table[s1[it]] != s2[it] ) return false;
if( table[s2[it]].has_value() && table[s2[it]] != s1[it] ) return false;
table[s1[it]] = s2[it];
//table[s2[it]] = s1[it]; if symmetric
}
return true;
}
If we find a new character, we will make it equal to the same position as the other string characters. Next time, if we found it again, we will check based on it.
Suppose we have 'aa' and 'cd'.
1st iteration: 'a'='c'
2nd iteration: already 'a'='c'(1st iteration), so we must need 'c' in our 2nd string.
But in our 2nd string, it is 'd'. so simply it will return false.
#include <bits/stdc++.h>
using namespace std;
// if you want to use map
bool LetterPattern_with_map(string str1,string str2)
{
if(str1.size()!=str2.size()) return false;
map<char,char> mp;
for(int i=0;i<str1.size();i++)
{
if(!mp[str1[i]]) { mp[str1[i]]=str2[i]; continue; }
if(mp[str1[i]]!=str2[i]) return false;
}
return true;
}
// if you want to use array instead of map
bool LetterPattern_with_array(string str1,string str2)
{
if(str1.size()!=str2.size()) return false;
int check[128]={0};
for(int i=0;i<str1.size();i++)
{
if(!check[str1[i]-'A'+1]) { check[str1[i]-'A'+1]=(int)(str2[i]-'A'+1); continue; }
if(check[str1[i]-'A'+1]!=(int)(str2[i]-'A'+1)) return false;
}
return true;
}
int main()
{
cout << "Please enter two string variable: ";
string str1, str2;
cin >> str1 >> str2;
cout << "Same Letter Pattern: " << LetterPattern_with_map(str1, str2)<<'\n';
cout << "Same Letter Pattern: " << LetterPattern_with_array(str1, str2);
}

Adding Space to String at certain places in C++

I am having trouble figuring out the process to add a space in a string at capital letters in C++. If I have a string "HelloWorld", how do I convert that to "Hello World"?
I've tried using substrings and the isupper function but I can't get anything to work.
Edit: this is the code I have. I don't understand why in = newname is not valid code.
string breakStringAtCaps(string in) {
string newname[10];
int j = 0;
for (int i = 0; i < in.size(); i++) {
if (isupper(in[i]) && i != 0) {
newname[j] = " ";
j++;
}
newname[j] = in[i];
j++;
}
in = newname;
return in;
}
You can do it like this:
string breakStringAtCaps(const string& in)
{
string newname;
for(int i = 0; i < in.size(); i++)
{
if(isupper(in[i]) && i != 0)
newname += " ";
newname += in[i];
}
return newname;
}
You are thinking right in thinking substr, but you implementation is a bit off. If creating an new string containing the contents of the original and inserting a ' ' (space) before each upper-case letter (not including the first), you can seed the new string with the first character of the original using substr(0,1) and then use an auto ranged for loop and substr(1) to evaluate each character after the first.
The loop along with a check of isupper() is basically all you need, e.g.
#include <iostream>
#include <string>
#include <cctype>
int main (int argc, char **argv) {
std::string s = argc > 1 ? argv[1] : "HelloWorld",
snew = s.substr (0,1);
if (s.size() > 0)
for (auto& c : s.substr(1)) {
if (std::isupper (c))
snew += " ";
snew += c;
}
std::cout << snew << '\n';
}
(the program will use "HelloWorld" by default if no string is given as an argument on the command line, otherwise the string given on the command line is used)
Example Use/Output
$ ./bin/spacebeforeupper
Hello World
Or with a string given as an argument:
$ ./bin/spacebeforeupper MyDogHasFleas
My Dog Has Fleas
You can iterate through the strin characters, check if it is a cap and insert a ' ' before if it is one:
It should look like:
for(int i=0; i < str.length(); i++)
{
if (str[i]>='A' && str[i]<='Z')
{
if (i != 0)
cout << " ";
cout << str[i];
}
else
{
cout << str[i];
}
}
I just give a implementation, maybe not the best solution:
#include <string>
#include <ctype.h>
void breakStringAtCaps(std::string& in) {
std::string::const_iterator it = in.begin();
while(it != in.end()) {
if(it != in.begin() && isupper(*it)) {
in.insert(it, ' ');
it += 2;
}
else
++it;
}
}
//
#include <iostream>
int main(int argc, char** argv) {
std::string str("HelloWorld;");
breakStringAtCaps(str);
std::cout << str.c_str() << std::endl;
return 0;
}
and in your code,
string newname[10];
here 'newname' is a string array's name. you should not assign the name of array to a string instance.
and newname[i] means the i-th string of the array, not the i-th char of a string named 'newname' as you desired.

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

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