Find a number inside a QString - c++

I have a QString with some number inside it, for example
first_34.33string
second-23.4string // In this case number is negative
How can I extract number from the string?
EDIT:
This function seems to work, using regexp in replies:
float getNumberFromQString(const QString &xString)
{
QRegExp xRegExp("(-?\\d+(?:[\\.,]\\d+(?:e\\d+)?)?)");
xRegExp.indexIn(xString);
QStringList xList = xRegExp.capturedTexts();
if (true == xList.empty())
{
return 0.0;
}
return xList.begin()->toFloat();
}

This should work for valid numbers: QRegExp("(-?\\d+(?:[\\.,]\\d+(?:e\\d+)?)?)")
edit: sorry, messed up with the brackets, now it should work.

I would write a simple function for that:
static double extractDouble(const QString &s)
{
QString num;
foreach(QChar c, s) {
if (c.isDigit() || c == '.' || c == '-') {
num.append(c);
}
}
bool ok;
double ret = num.toDouble(&ok);
if (ok) {
return ret;
} else {
throw "Cannot extract double value";
}
}

Related

Check if a string is a number without regex or try catch

I want to implement a simple is_number function that checks if it's an integer, float or an unsigned long int using this method:
bool isNumber(const std::string& str)
{
size_t idx = 0;
//Check if it's an integer
std::stoi(str,&idx);
if (idx == str.size())
return true;
//Check if it's a float
std::stof(str,&idx);
if (idx == str.size() || str[str.size()-1] == 'f' && idx == str.size()) //Cause I do have some float numbers ending with 'f' in the database
return true;
//Check if it's an unsigned long int
std::stoul(str,&idx);
if (idx == str.size())
return true;
return false;
}
But if I test it with a pure string like "test" or "nan", it will throw an error because I'm trying to change a pure string to an integer.
terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
However if I test it with "0nan" for example, stoi or the others will retrieve the first number and assign the index position of the first found number to the idx variable.
Is it possible to find a workaround for pure strings like "nan" or any other?
Or is there a better method to implement this without regex or try-catch?
std::stoi throws when it fails. Instead of using C i/o you can use C++ streams, try to read from the stream and check if there is something left in the stream:
#include <string>
#include <sstream>
#include <iostream>
enum Number {Float,Signed,Unsigned,NotANumber};
template <typename T>
bool is_only_a(const std::string& str){
std::stringstream ss(str);
T x;
return (ss >> x && ss.rdbuf()->in_avail() ==0);
}
Number isNumber(const std::string& str)
{
size_t idx = 0;
if (is_only_a<unsigned long>(str)) return Unsigned;
else if (is_only_a<int>(str)) return Signed;
else if (is_only_a<float>(str)) return Float;
return NotANumber;
}
int main() {
std::cout << isNumber("1.2") << "\n";
std::cout << isNumber("12") << "\n";
std::cout << isNumber("-12") << "\n";
std::cout << isNumber("asd") << "\n";
std::cout << isNumber("nan") << "\n";
}
Order is important, because 12 could be a float as well.
The link I posted in the comments is most probably what you need.
The only slight modification needed from the answers there is adding a +/- sign, and an optional (at most one) decimal point:
bool isNumber(const std::string &s) {
bool first_char = true;
bool saw_decpt = false;
for (const auto &it: s) {
if (std::isdigit(it)) { first_char = false; }
else if (it == '+' && first_char) { first_char = false; }
else if (it == '-' && first_char) { first_char = false; }
else if (it == '.' && !saw_decpt) { first_char = false; saw_decpt = true; }
else return false;
}
return true;
}

Trying to find isogram in a string c++

I am trying to write a code that has two functions: one that determines whether the string is an isogram or not and another one to print the outcome (true or false) to the console (for the purpose of solving the task).
Some of the things are not working correctly though. And I wonder where I need to improve the code (probably all over...). I would appreciate any advice :)
#include <iostream>
#include<string>
#include<bits/stdc++.h>
#include<iomanip>
bool find_Isogram (std::string str)
{
std::sort(str.begin(), str.end()); //sorted the string for the for loop (e.g. eHllo)
int length = str.length();
for (int i = 0; i < length; i++)
{
if (str.at(i) == str.at(i+1))
{
return false;
break;
}
else
{
return true;
}
}
}
void print_result()
{
std::string str;
if (!find_Isogram (str))
{
std::cout << "false" << std::endl;
}
else
{
std::cout << "true" << std::endl;
}
}
int main()
{
find_Isogram ("gdtub");
print_result();
return 0;
};
````````````````````````````````````````````````````
There are some problems here:
1) You always check an empty string:
print_result will just check an empty string, but it's redundant anyway.
void print_result()
{
std::string str; // empty string
if (!find_Isogram (str)) // finding isogram on empty string
{
std::cout << "false" << std::endl;
}
...
}
It can be simplified with std::boolalpha that allows you to print a bool as "true" or "false" (instead of 1 or 0). main would become
int main()
{
std::cout << std::boolalpha << find_Isogram ("gdtub"); // prints true or false
};
2) Isogram check always ends after first character
Take a look at the condition in find_Isogram. It has a return-statement in the if and else, so you always return after checking the first character.
The idea to detect duplicate characters this way is correct (except for the off-by-one-error already mentioned by others). But you want to return true; only after checking all of the characters, e.g. outside the loop:
bool find_Isogram (std::string str)
{
std::sort(str.begin(), str.end()); //sorted the string for the for loop (e.g. eHllo)
int length = str.length();
for (int i = 0; i < length - 1; i++)
{
if (str.at(i) == str.at(i+1))
{
return false; // whoops duplicate char, stop here
}
}
return true; // no duplicates found, it's an isogram
}
For some further C++-magic, you could simplify it even more with standard library functions :D
bool find_Isogram (std::string str)
{
std::sort(str.begin(), str.end());
return std::unique(str.begin(), str.end()) == str.end();
}
The condition where you check the consecutive characters for equality is wrong. It will yield true for strings like ABAB. You instead need to use a map with count of each character that has appeared.
Something like:
std::map<char, int> map_of_chars;
for(int i = 0; i < length; i++) {
map_of_chars[str.at(i)] = map_of_chars[str.at(i)] + 1;
}
If any value in the map is more than 1 return false;
Another implementation would be using the return value of std::unique():
std::sort(str.begin(), str.end());
auto intial_size = str.size();
std::unique(str.begin(), str.end());
if(str.size() == initial_size) {
/is an isogram
}
else {
//is not an isogram
}

Natural sort of filenames with numbers issue c++

I have a text file to sort, which contains the paths to the image files:
/images/calibrationImageRightCamera4.jpg
/images/calibrationImageLeftCamera1.jpg
/images/calibrationImageRightCamera5.jpg
/images/calibrationImageLeftCamera2.jpg
/images/calibrationImageLeftCamera4.jpg
/images/calibrationImageRightCamera6.jpg
/images/calibrationImageLeftCamera3.jpg
/images/calibrationImageRightCamera3.jpg
/images/calibrationImageRightCamera2.jpg
/images/calibrationImageLeftCamera5.jpg
/images/calibrationImageRightCamera1.jpg
/images/calibrationImageLeftCamera7.jpg
/images/calibrationImageLeftCamera6.jpg
/images/calibrationImageRightCamera7.jpg
I use this code to sort the file:
QFile imageListFile;
QStringList sortedImageListFile;
QString filePath;
filePath= "C:/Users/Desktop/textstream/sorted.xml";
imageListFile.setFileName(filePath);
if (!imageListFile.open(QFile::ReadOnly))
{
cout<<"Error opening file"<<endl;
}
else
{
while(!imageListFile.atEnd())
{
sortedImageListFile.append(imageListFile.readLine());
}
imageListFile.close();
}
// Sort the image file
qSort(sortedImageListFile.begin(), sortedImageListFile.end(), naturalSortCallback);
QTextStream stream(&imageListFile);
// Save sorted image in the same file
if(imageListFile.open((QIODevice::ReadWrite | QIODevice::Truncate)))
{
for(QStringList::Iterator it= sortedImageListFile.begin(); it!= sortedImageListFile.end(); ++it)
{
stream << *it;
}
}
imageListFile.close();
inline int findNumberPart(const QString &sIn)
{
QString s= "";
int i= 0;
bool isNum= false;
while(i< sIn.length())
{
if(isNum)
{
if(!sIn[i].isNumber())
break;
s += sIn[i];
}
else
{
if(sIn[i].isNumber())
s += sIn[i];
}
++i;
}
if(s == "")
return 0;
return s.toInt();
}
static bool naturalSortCallback(const QString &s1, const QString &s2)
{
int idx1= findNumberPart(s1);
int idx2= findNumberPart(s2);
return(idx1 < idx2);
}
And I get as result:
/cameraCalibrationFiles/calibrationImageLeftCamera1.jpg
/cameraCalibrationFiles/calibrationImageRightCamera1.jpg
/cameraCalibrationFiles/calibrationImageLeftCamera2.jpg
/cameraCalibrationFiles/calibrationImageRightCamera2.jpg
/cameraCalibrationFiles/calibrationImageLeftCamera3.jpg
/cameraCalibrationFiles/calibrationImageRightCamera3.jpg
/cameraCalibrationFiles/calibrationImageRightCamera4.jpg
/cameraCalibrationFiles/calibrationImageLeftCamera4.jpg
/cameraCalibrationFiles/calibrationImageLeftCamera5.jpg
/cameraCalibrationFiles/calibrationImageRightCamera5.jpg
/cameraCalibrationFiles/calibrationImageRightCamera6.jpg
/cameraCalibrationFiles/calibrationImageLeftCamera6.jpg
/cameraCalibrationFiles/calibrationImageLeftCamera7.jpg
/cameraCalibrationFiles/calibrationImageRightCamera7.jpg
The numbers at the and are sorted correctly but not the filenames. sometimes the words "Left" and "Right" are mixed. The order should look like:
..LeftCamera1.jpg
..RightCamera1.jpg
..LeftCamera2.jpg
..RightCamera2.jpg
.
.
.
Where is my mistake? Thank you for any help.
You problem is the way you are comparing the strings.
inline int findNumberPart(const QString &sIn)
Is just giving you the ending number from the string. So any string ending in 1 will come before any other string. This is why the right and left cameras file names are getting mixed together and in no particular order. What you need to do is compare the number part and if that is equal then compare the string part. If not then just compare the number part.
You should compare number part and then string part on equal number part.
inline int findStringPart(const QString &s)
{
...
return stringPart; //Left or Right
}
static bool naturalSortCallback(const QString &s1, const QString &s2)
{
int numberPart1 = findNumberPart(s1);
int numberPart2 = findNumberPart(s2);
QString stringPart1 = findStringPart(s1);
QString stringPart2 = findStringPart(s2);
if(numberPart1 == numberPart2)
return stringPart1 < stringPart2; //"Left" < "Right"
return numberPart1 < numberPart2;
}

most effective & easiest solution for expression evaluation c++

many programs needs to evaluate expressions like:
input: (T(TF)) ---> output: false (T as true, and F as false)
OR
input (a (b c 2) 3)$ ---> output: abcbcabcbcabcbc
in other words,how to deal with expressions that contain braces?
I used to push to stack until I reach ')'.
then, pop until I reach '(', and so on.
but that would take a long execution time and missy code!
and if I tried to take good of #Henrik code to solve arithmetic expression, I end up with a wrong answer
#include <iostream>
using namespace std;
const char * expressionToParse = "(T(TT))";
char peek()
{
return *expressionToParse;
}
char get()
{
return *expressionToParse++;
}
bool expression();
bool number()
{
bool result = get() - '0';
//even if I remove -'0'
cout<<"number() result:"<<result<<endl;
while (peek() == 'T' || peek() == 'F')
{
if (peek()=='T' && get()=='T') {
result=true;
}
else{
result=false;
}
}
return result;
}
bool factor()
{
if (peek() == 'T' && peek() <= 'F')
return number();
else if (peek() == '(')
{
get(); // '('
bool result = expression();
get(); // ')'
return result;
}
return 0; // error
}
bool expression()
{
bool result = factor();
return result;
}
int main()
{
bool result = expression();
if(result){
cout<<"true";
}
else{
cout<<"false";
}
return 0;
}
Thanks in Advance
This is an expression in postfix form. The easiest way to evaluate a postfix expression can be done using a stack. Search for "postfix expression evaluation" tutorials.

C++ detecting if input is Int or String

I'm a C++ newbie who came from Java, so I need some guidance on some really basic issues I'm stumbling upon as I go.
I'm reading lines from a file, and each line consists of 6 strings/ints, which will be sent as parameters to a temporary variable.
Example:
Local1,Local2,ABC,200,300,asphalt
However, there are two subtypes of variable. One has a string as the last parameter (like 'asphalt' in the example above). The other one has an int instead. I have a method that reads each parameter and sends it to a variable, but how do I detect if the last bit of string is an integer or a string beforehand, so I know if I should send it to a Type1 variable or a Type2 one?
Many thanks!
Since you want to determine the type of the last column, then this ought to work:
#include <iostream>
#include <string>
#include <cstdlib>
#include <vector>
#include <sstream>
#include <cctype>
#include <algorithm>
enum Types {
NONE,
STRING,
INTEGER,
DOUBLE
};
struct Found {
std::string string_val;
int integer_val;
double double_val;
enum Types type;
};
//copied verbatim from:
//http://stackoverflow.com/a/2845275/866930
inline bool isInteger(const std::string &s) {
if(s.empty() || ((!std::isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false;
char * p ;
std::strtol(s.c_str(), &p, 10);
return (*p == 0);
}
//modified slightly for decimals:
inline bool isDouble(const std::string &s) {
if(s.empty() || ((!std::isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) return false ;
char * p ;
std::strtod(s.c_str(), &p) ;
return (*p == 0);
}
bool isNotAlpha(char c) {
return !(std::isalpha(c));
}
//note: this searches for strings containing only characters from the alphabet
//however, you can modify that behavior yourself.
bool isString (const std::string &s) {
std::string::const_iterator it = std::find_if(s.begin(), s.end(), isNotAlpha);
return (it == s.end()) ? true : false;
}
void determine_last_column (const std::string& str, Found& found) {
//reset found:
found.integer_val = 0;
found.double_val = 0;
found.string_val = "";
found.type = NONE;
std::string temp;
std::istringstream iss(str);
int column = 0;
char *p;
while(std::getline(iss, temp, ',')) {
if (column == 5) {
//now check to see if the column is an integer or not:
if (isInteger(temp)) {
found.integer_val = static_cast<int>(std::strtol(temp.c_str(), &p, 10));
found.type = INTEGER;
}
else if (isDouble(temp)) {
found.double_val = static_cast<double>(std::strtod(temp.c_str(), &p));
found.type = DOUBLE;
}
else if (isString(temp)) {
found.string_val = temp;
found.type = STRING;
}
}
++column;
}
if (found.type == INTEGER) {
std::cout << "An integer was found: " << found.integer_val << std::endl;
}
else if(found.type == DOUBLE) {
std::cout << "A double was found: " << found.double_val << std::endl;
}
else if(found.type == STRING) {
std::cout << "A string was found: " << found.string_val << std::endl;
}
else {
std::cout << "A valid type was not found! Something went wrong..." << std::endl;
}
}
int main() {
std::string line_t1 = "Local1,Local2,ABC,200,300,asphalt";
std::string line_t2 = "Local1,Local2,ABC,200,300,-7000.3";
Found found;
determine_last_column(line_t1, found);
determine_last_column(line_t2, found);
return 0;
}
This outputs and correctly assigns the appropriate value:
A string was found: asphalt
An integer was found: -7000.3
This version works on int, double, string; does not require boost; and, is plain vanilla C++98.
REFERENCES:
UPDATE:
This version now supports both positive and negative numbers that are integers or doubles, in addition to strings.
First, create an array that can store both strings and integers:
std::vector<boost::variant<std::string, int>> items;
Second, split the input string on commas:
std::vector<std::string> strings;
boost::split(strings, input, boost::is_any_of(","));
Last, parse each token and insert it into the array:
for (auto&& string : strings) {
try {
items.push_back(boost::lexical_cast<int>(string));
} catch(boost::bad_lexical_cast const&) {
items.push_back(std::move(string));
}
}