Home > Enterprise >  C check if a string contains numbers and . only
C check if a string contains numbers and . only

Time:01-03

I want to know if there is a lambda or a clean short format in order to find if a single string contains only numeric values e.g 0 - 9 and the full stop character e.g . only. For example string "123.45" should pass and strings "12jd", "12.4f" etc should fail.

CodePudding user response:

This would be the code for your check, the constexpr stuff makes the function evaluatable at compile time. And the static_assert checks the output at compile time. Basically doing a unit test at compile time.

string_view is a nice wrapper for string literals and makes the string literal more easy to pass into the function and allows the use of a range based for loop to loop over all the characters.

#include <cassert>
#include <string_view>

constexpr bool is_positive_number(const std::string_view number_string)
{
    std::size_t number_of_points{ 0ul };

    // loop over all characters in string
    for (const auto character : number_string)
    {
        // a '.' is valid but there should only be one
        if (character == '.')
        {
            number_of_points  ;
            if (number_of_points > 1) return false;
        }
        else
        {
            // if character is not a '.' then it must be betweern '0'-'9'
            if ((character < '0') || (character > '9')) return false;
        }
    }

    return true;
}

int main()
{
    static_assert(is_positive_number("1"));
    static_assert(is_positive_number("12"));
    static_assert(is_positive_number("123"));
    static_assert(is_positive_number("1."));
    static_assert(is_positive_number("1.2"));
    static_assert(is_positive_number("12.34"));
    static_assert(is_positive_number("007"));

    static_assert(!is_positive_number("12.3.4"));
    static_assert(!is_positive_number("-123"));
    static_assert(!is_positive_number("abc"));

    //auto lambda = [](const char* number_string)
    auto lambda = [](const std::string& number_string)
    {
        return is_positive_number(number_string);
    };

    auto is_ok = lambda("123");
    assert(is_ok);


    return 0;
}

CodePudding user response:

This can be done with a straightforward scan of the text:

bool is_valid(const std::string& str) {
    int dots = 0;
    for (char c: str) {
        if (c == '.')
              dots;
        if (1 < dots || !std::isdigit(c))
            return false;
    }
    return true;
}

CodePudding user response:

Using std::regex produces a two-liner if you dont count the static pattern

#include <string>
#include <regex>
#include <cassert>

bool is_positive_number( const std::string& str ) {
    static std::regex rx("^(\\ ?)(0|([1-9][0-9]*))(\\.[0-9]*)?$");             // Getting the regex object 
    std::smatch match;
    return std::regex_match(str,match,rx);
}

int main()
{
    assert(is_positive_number("1"));
    assert(is_positive_number("12"));
    assert(is_positive_number("123"));
    assert(is_positive_number("1."));
    assert(is_positive_number("1.2"));
    assert(is_positive_number("12.34"));
    assert(is_positive_number(" 12.34"));
    assert(!is_positive_number("007"));

    assert(!is_positive_number("123.23.23"));
    assert(!is_positive_number("-123"));
    assert(!is_positive_number("abc"));

    return 0;
}

CodePudding user response:

Another way would be to use the open-source compile-time regex library:

#include <ctre.hpp>

constexpr bool is_positive_number(std::string_view const s) noexcept {
    return static_cast<bool>(ctre::match<R"(\d (?:\.\d )?)">(s));
}

int main() {
    static_assert(is_positive_number("1.2"));
    static_assert(!is_positive_number("1..2"));
    static_assert(!is_positive_number("1e2"));
}

See Online

CodePudding user response:

This does not check all the cases like repeated dots but it's a one-liner

  bool ok = str.find_first_not_of( "0123456789." )==std::string::npos;

Use as in

#include <algorithm>
#include <string>

constexpr bool is_positive_number( const std::string_view& str ) {
    return str.find_first_not_of( "0123456789." )==std::string::npos;
}

int main()
{
    static_assert(is_positive_number("1"));
    static_assert(is_positive_number("12"));
    static_assert(is_positive_number("123"));
    static_assert(is_positive_number("1."));
    static_assert(is_positive_number("1.2"));
    static_assert(is_positive_number("12.34"));
    static_assert(is_positive_number("007"));

    static_assert(!is_positive_number("-123"));
    static_assert(!is_positive_number("abc"));

    return 0;
}

CodePudding user response:

I don't think there is a built-in function that does exactly what you were looking for. There is std::stod that converts a string into a double and tells you how many characters were converted successfully. However, there are many non-conforming numbers that will work, such as -1, NAN, 10E 3.


One way you could do is first remove all numbers(0~9), then check against "" and ".":

constexpr bool is_num(std::string str)
{
    std::erase_if(str, [](unsigned char c){ return std::isdigit(c);};
    return str == "" || str == ".";
}

Note erase_if is from C 20, for pre-C 20, you would do:

constexpr bool is_num(std::string str)
{
    str.erase(std::remove_if(str.begin(), str.end(), 
              [](unsigned char c){ return std::isdigit(c); }), str.end());
    return str == "" || str == ".";
}

demo

  • Related