简体   繁体   中英

C++ check if a string contains numbers and . only [closed]

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 eg 0 - 9 and the full stop character eg only. For example string "123.45" should pass and strings "12jd", "12.4f" etc should fail.

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

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

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

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM