Skip to content

Commit

Permalink
Validate numeric input using regex and simplify code.
Browse files Browse the repository at this point in the history
  • Loading branch information
bcoconni committed Feb 9, 2025
1 parent 210c71f commit 7c647c6
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 14 deletions.
21 changes: 11 additions & 10 deletions src/input_output/string_utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ INCLUDES
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <regex>
#ifdef __APPLE__
#include <xlocale.h>
#else
Expand Down Expand Up @@ -84,26 +85,26 @@ struct CNumericLocale
*/
double atof_locale_c(const string& input)
{
string v = input;
trim(v);
static const std::regex number_format(R"(^\s*[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?\s*$)");
const char* first = input.c_str();

if (v.empty()) {
InvalidNumber e("Expecting a numeric attribute value, but got an empty string");
while (*first) {
if (!isspace(*first)) break;
first++;
}

if (!*first) {
InvalidNumber e("Expecting a numeric attribute value, but only got spaces");
cerr << e.what() << endl;
throw e;
}

if (v.find_first_not_of("+-.0123456789Ee") != std::string::npos) {
if (!std::regex_match(input, number_format)) {
InvalidNumber e("Expecting a numeric attribute value, but got: " + input);
cerr << e.what() << endl;
throw e;
}

const char* first = v.c_str();

//Ignoring the leading '+' sign
if (*first == '+') ++first;

CNumericLocale numeric_c;
errno = 0; // Reset the error code
double value = strtod_l(first, nullptr, numeric_c.Locale);
Expand Down
54 changes: 50 additions & 4 deletions tests/unit_tests/StringUtilitiesTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,14 +89,60 @@ class StringUtilitiesTest : public CxxTest::TestSuite
}

void testAtofLocaleC() {
TS_ASSERT_EQUALS(atof_locale_c("+1 "), 1.0);
// Test legal numbers
TS_ASSERT_EQUALS(atof_locale_c("0.0"), 0.0);
TS_ASSERT_EQUALS(atof_locale_c("+1"), 1.0);
TS_ASSERT_EQUALS(atof_locale_c("1"), 1.0);
TS_ASSERT_EQUALS(atof_locale_c("-1"), -1.0);
TS_ASSERT_EQUALS(atof_locale_c(" 123.4"), 123.4);
TS_ASSERT_EQUALS(atof_locale_c("123.4 "), 123.4);
TS_ASSERT_EQUALS(atof_locale_c(" 123.4 "), 123.4);
TS_ASSERT_EQUALS(atof_locale_c(".25"), 0.25);
TS_ASSERT_EQUALS(atof_locale_c("1.e1"), 10.);
TS_ASSERT_EQUALS(atof_locale_c("1e1"), 10.);
TS_ASSERT_EQUALS(atof_locale_c(".1e1"), 1.);
TS_ASSERT_EQUALS(atof_locale_c("+.1e1"), 1.);
TS_ASSERT_EQUALS(atof_locale_c("-.1e1"), -1.);
TS_ASSERT_EQUALS(atof_locale_c("31.4e1"), 314);
TS_ASSERT_EQUALS(atof_locale_c("+3.14e+2"), 314);
TS_ASSERT_EQUALS(atof_locale_c("+3.14e2"), 314);
TS_ASSERT_EQUALS(atof_locale_c("+3.14e-2"), 0.0314);
TS_ASSERT_EQUALS(atof_locale_c("3.14e+2"), 314);
TS_ASSERT_EQUALS(atof_locale_c("3.14e2"), 314);
TS_ASSERT_EQUALS(atof_locale_c("3.14e-2"), 0.0314);
TS_ASSERT_EQUALS(atof_locale_c("-3.14e+2"), -314);
TS_ASSERT_EQUALS(atof_locale_c("-3.14e2"), -314);
TS_ASSERT_EQUALS(atof_locale_c("-3.14e-2"), -0.0314);
TS_ASSERT_EQUALS(atof_locale_c("+3.14E+2"), 314);
TS_ASSERT_EQUALS(atof_locale_c("+3.14E2"), 314);
TS_ASSERT_EQUALS(atof_locale_c("+3.14E-2"), 0.0314);
TS_ASSERT_EQUALS(atof_locale_c("3.14E+2"), 314);
TS_ASSERT_EQUALS(atof_locale_c("3.14E2"), 314);
TS_ASSERT_EQUALS(atof_locale_c("3.14E-2"), 0.0314);
TS_ASSERT_EQUALS(atof_locale_c("-3.14E+2"), -314);
TS_ASSERT_EQUALS(atof_locale_c("-3.14E2"), -314);
TS_ASSERT_EQUALS(atof_locale_c("-3.14E-2"), -0.0314);
// Test rounded down numbers
TS_ASSERT_EQUALS(atof_locale_c("1E-999"), 0.0);
TS_ASSERT_EQUALS(atof_locale_c("-1E-999"), 0.0);
TS_ASSERT_EQUALS(atof_locale_c("0.0"), 0.0);
TS_ASSERT_THROWS(atof_locale_c("1E+999"), BaseException&);
TS_ASSERT_THROWS(atof_locale_c("-1E+999"), BaseException&);
// Test invalid numbers
TS_ASSERT_THROWS(atof_locale_c("1E+999"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("-1E+999"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("invalid"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("1.0.0"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("1E-"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("E-2"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("."), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c(".E"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c(".E2"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c(".E-2"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("1.2E"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("1.2E+"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("1.2E1.0"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("--1"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c("++1"), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c(""), InvalidNumber&);
TS_ASSERT_THROWS(atof_locale_c(" "), InvalidNumber&);
}

private:
Expand Down

0 comments on commit 7c647c6

Please sign in to comment.