2
\$\begingroup\$

Problem

Validate if a given string can be interpreted as a decimal or scientific number.

Examples:

"0" => true
" 0.1 " => true
"abc" => false
"1 a" => false
"2e10" => true
" -90e3   " => true
" 1e" => false
"e3" => false
" 6e-1" => true
" 99e2.5 " => false
"53.5e93" => true
" --6 " => false
"-+3" => false
"95a54e53" => false

Code

With some help, I've solved the valid number LeetCode problem using C++ regex and Python, which apparently seems to have a simple solution though. If you'd like to review the code and provide any change/improvement recommendations, please do so and I'd really appreciate that.

#include <iostream>
#include <regex> 

using namespace std;

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

int main() {
    vector<string> string_vector = {
        "   0  ","   0.1   ","  abc  ",
        "1 a","   2e10   ","  -90e3 ","1e",
        "  e3  ","  6e-1  ","  99e2.5   ",
        " 53.5e93 "," --6 ","  -+3  ",
        "   95a54e53  "
    };

    regex expression_one("^[+-]?(?:\\d*\\.\\d+|\\d+\\.\\d*|\\d+)[Ee][+-]?\\d+$|^[+-]?(?:\\d*\\.\\d+|\\d+\\.\\d*|\\d+)$|^[+-]?\\d+$");
    regex expression_two("^[+-]?(?:[0-9]*\\.[0-9]+|[0-9]+\\.[0-9]*|[0-9]+)[Ee][+-]?[0-9]+$|^[+-]?(?:[0-9]*\\.[0-9]+|[0-9]+\\.[0-9]*|[0-9]+)$|^[+-]?[0-9]+$");
    regex expression_three("^(?:(?:[+-]?(?:\\d*[.]\\d+|\\d+[.]\\d*|\\d+)[Ee][+-]?\\d+)|(?:[+-]?(?:\\d*[.]\\d+|\\d+[.]\\d*|\\d+))|[+-]?\\d+)$");

    for (const auto &input_string: string_vector) {
        auto trim_input_string = trim_copy(input_string);
        if (std::regex_match(trim_input_string, expression_two))
            cout << "1️⃣ Test Expression 1: '" << input_string << "' is a valid number." << endl;
        if (std::regex_match(trim_input_string, expression_two))
            cout << "2️⃣ Test Expression 2: '" << input_string << "' is a valid number." << endl;
        if (std::regex_match(trim_input_string, expression_two))
            cout << "3️⃣ Test Expression 3: '" << input_string << "' is a valid number." << endl;

    }
}

Output

1️⃣ Test Expression 1: '   0  ' is a valid number.
2️⃣ Test Expression 2: '   0  ' is a valid number.
3️⃣ Test Expression 3: '   0  ' is a valid number.
1️⃣ Test Expression 1: '   0.1   ' is a valid number.
2️⃣ Test Expression 2: '   0.1   ' is a valid number.
3️⃣ Test Expression 3: '   0.1   ' is a valid number.
1️⃣ Test Expression 1: '   2e10   ' is a valid number.
2️⃣ Test Expression 2: '   2e10   ' is a valid number.
3️⃣ Test Expression 3: '   2e10   ' is a valid number.
1️⃣ Test Expression 1: '  -90e3 ' is a valid number.
2️⃣ Test Expression 2: '  -90e3 ' is a valid number.
3️⃣ Test Expression 3: '  -90e3 ' is a valid number.
1️⃣ Test Expression 1: '  6e-1  ' is a valid number.
2️⃣ Test Expression 2: '  6e-1  ' is a valid number.
3️⃣ Test Expression 3: '  6e-1  ' is a valid number.
1️⃣ Test Expression 1: ' 53.5e93 ' is a valid number.
2️⃣ Test Expression 2: ' 53.5e93 ' is a valid number.
3️⃣ Test Expression 3: ' 53.5e93 ' is a valid number.    

RegEx Circuit

jex.im visualizes regular expressions:

enter image description here


RegEx Demo 1

RegEx Demo 2

If you wish to explore the expression, it's been explained on the top right panel of regex101.com. If you'd like, you can also watch in this link, how it would match against some sample inputs.


Source

\$\endgroup\$
2
\$\begingroup\$
  • Don't do using namespace std;.

  • inline is really only useful for defining non-template functions in header files. Modern compilers generally decide what functions to actually inline themselves.

  • To use std::isspace correctly we must cast the argument from a char to an unsigned char before passing it into the function.

  • std::regex_match has a version that takes iterators. So we don't need to copy or modify the input strings:

    auto const is_not_space = [] (unsigned char c) {
        return std::isspace(c) == 0;
    };
    
    for (auto const& s: string_vector) {
        auto const begin = std::find_if(s.begin(), s.end(), is_not_space);
        auto const end = std::find_if(s.rbegin(), std::reverse_iterator(begin), is_not_space).base();
    
        if (std::regex_match(begin, end, expression_two))
            ...
    
  • The test code checks expression_two three times, and doesn't use expression_one or expression_three!

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.