Files
musrfit/src/tests/strToNum/strToNum.cpp
T
suter_a 07f9c744b3 PStringUtils::IsInt: accept an optional leading sign
IsInt now recognises (possibly signed) integers such as "-5" or "+42",
making it slightly more permissive than TString::IsDigit(). A lone sign,
a double sign, or a sign following a digit are still rejected. strToNum
test expectations updated accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 14:14:11 +02:00

305 lines
12 KiB
C++

/***************************************************************************
strToNum.cpp
Author: Andreas Suter
e-mail: andreas.suter@psi.ch
Little stand-alone test driver for the PStringUtils class. It exercises
Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase /
ContainsNoCase / BeginsWithNoCase and reports a pass/fail summary.
Usage:
strToNum -> run the built-in test suite
strToNum <string> -> show what PStringUtils makes of the given
string(s) on the command line (ad-hoc checks)
strToNum -i -> interactive mode: type a string at the prompt
and see the result of every PStringUtils method
(empty line or 'quit' to leave)
***************************************************************************/
/***************************************************************************
* Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <cmath>
#include <iostream>
#include <string>
#include <vector>
#include "PStringUtils.h"
//--------------------------------------------------------------------------
// tiny test bookkeeping
//--------------------------------------------------------------------------
static int gPassed = 0;
static int gFailed = 0;
static void check(const std::string &what, bool ok)
{
if (ok) {
++gPassed;
std::cout << " [ ok ] " << what << std::endl;
} else {
++gFailed;
std::cout << " [FAIL] " << what << std::endl;
}
}
//--------------------------------------------------------------------------
static std::string vecToStr(const std::vector<std::string> &v)
{
std::string s = "{";
for (std::vector<std::string>::size_type i = 0; i < v.size(); ++i) {
s += "'" + v[i] + "'";
if (i + 1 < v.size())
s += ", ";
}
s += "}";
return s;
}
//--------------------------------------------------------------------------
static void testSplit()
{
std::cout << "Split:" << std::endl;
std::vector<std::string> r = PStringUtils::Split("alpha beta gamma", " ");
std::cout << " input: 'alpha beta gamma', delim: ' '" << std::endl;
check("3 tokens on single space delimiter",
r.size() == 3 && r[0] == "alpha" && r[1] == "beta" && r[2] == "gamma");
std::cout << "----" << std::endl;
r = PStringUtils::Split(" , 1 , 2 ,, 3 ,", " ,");
std::cout << " input: ' , 1 , 2 ,, 3 ,', delim: ' ,'" << std::endl;
std::cout << " -> ";
for (auto i: r)
std::cout << i << ", ";
std::cout << std::endl;
check("mixed/repeated delimiters, empty tokens skipped",
r.size() == 3 && r[0] == "1" && r[1] == "2" && r[2] == "3");
std::cout << "----" << std::endl;
r = PStringUtils::Split("", " ");
check("empty input -> no tokens", r.empty());
r = PStringUtils::Split(" ", " ");
check("delimiters only -> no tokens", r.empty());
r = PStringUtils::Split("nodelim", ",;");
check("no delimiter present -> single token",
r.size() == 1 && r[0] == "nodelim");
}
//--------------------------------------------------------------------------
static void testIsInt()
{
std::cout << "IsInt:" << std::endl;
check("'12345' is int", PStringUtils::IsInt("12345"));
check("' 42 ' (surrounding ws) is int", PStringUtils::IsInt(" 42 "));
check("'-5' is int (negative)", PStringUtils::IsInt("-5"));
check("'+42' is int (positive sign)", PStringUtils::IsInt("+42"));
check("'' is not int", !PStringUtils::IsInt(""));
check("' ' (ws only) is not int", !PStringUtils::IsInt(" "));
check("'-' (sign only) is not int", !PStringUtils::IsInt("-"));
check("'+-5' (double sign) is not int", !PStringUtils::IsInt("+-5"));
check("'5-3' (sign after digit) is not int", !PStringUtils::IsInt("5-3"));
check("'3.14' is not int", !PStringUtils::IsInt("3.14"));
check("'12a' is not int", !PStringUtils::IsInt("12a"));
}
//--------------------------------------------------------------------------
static void testIsFloat()
{
std::cout << "IsFloat:" << std::endl;
check("'3.14' is float", PStringUtils::IsFloat("3.14"));
check("'-1.2e-3' is float", PStringUtils::IsFloat("-1.2e-3"));
check("'+42' is float", PStringUtils::IsFloat("+42"));
check("'.5' is float", PStringUtils::IsFloat(".5"));
check("' 6.022e23 ' (surrounding ws) is float",
PStringUtils::IsFloat(" 6.022e23 "));
check("'' is not float", !PStringUtils::IsFloat(""));
check("'nan' is not float", !PStringUtils::IsFloat("nan"));
check("'inf' is not float", !PStringUtils::IsFloat("inf"));
check("'1.2.3' is not float", !PStringUtils::IsFloat("1.2.3"));
check("'12abc' is not float", !PStringUtils::IsFloat("12abc"));
}
//--------------------------------------------------------------------------
static void testToInt()
{
std::cout << "ToInt:" << std::endl;
bool ok = false;
check("'42' -> 42, ok", PStringUtils::ToInt("42", &ok) == 42 && ok);
check("' -7' -> -7, ok", PStringUtils::ToInt(" -7", &ok) == -7 && ok);
check("'123abc' -> 123, ok (trailing ignored)",
PStringUtils::ToInt("123abc", &ok) == 123 && ok);
check("'abc' -> 0, !ok", PStringUtils::ToInt("abc", &ok) == 0 && !ok);
check("'' -> 0, !ok", PStringUtils::ToInt("", &ok) == 0 && !ok);
check("'99999999999999999999' -> out of range, !ok",
(PStringUtils::ToInt("99999999999999999999", &ok), !ok));
check("null ok pointer is tolerated", PStringUtils::ToInt("17") == 17);
}
//--------------------------------------------------------------------------
static void testToDouble()
{
std::cout << "ToDouble:" << std::endl;
bool ok = false;
check("'3.14' -> 3.14, ok",
std::fabs(PStringUtils::ToDouble("3.14", &ok) - 3.14) < 1e-12 && ok);
check("' +1.5e2' -> 150, ok",
std::fabs(PStringUtils::ToDouble(" +1.5e2", &ok) - 150.0) < 1e-9 && ok);
check("'2.5xyz' -> 2.5, ok (trailing ignored)",
std::fabs(PStringUtils::ToDouble("2.5xyz", &ok) - 2.5) < 1e-12 && ok);
check("'abc' -> 0.0, !ok", PStringUtils::ToDouble("abc", &ok) == 0.0 && !ok);
check("'' -> 0.0, !ok", PStringUtils::ToDouble("", &ok) == 0.0 && !ok);
check("'1e400' -> out of range, !ok",
(PStringUtils::ToDouble("1e400", &ok), !ok));
check("null ok pointer is tolerated",
std::fabs(PStringUtils::ToDouble("0.25") - 0.25) < 1e-12);
}
//--------------------------------------------------------------------------
static void testCaseHelpers()
{
std::cout << "IsEqualNoCase / ContainsNoCase / BeginsWithNoCase:" << std::endl;
check("'Fit' == 'fIT' (no case)", PStringUtils::IsEqualNoCase("Fit", "fIT"));
check("'Fit' != 'Fits'", !PStringUtils::IsEqualNoCase("Fit", "Fits"));
check("'' == ''", PStringUtils::IsEqualNoCase("", ""));
check("'Hello World' contains 'LO WO' (no case)",
PStringUtils::ContainsNoCase("Hello World", "LO WO"));
check("empty needle is contained",
PStringUtils::ContainsNoCase("abc", ""));
check("needle longer than haystack is not contained",
!PStringUtils::ContainsNoCase("ab", "abc"));
check("'THEORY' begins with 'the' (no case)",
PStringUtils::BeginsWithNoCase("THEORY", "the"));
check("'THEORY' does not begin with 'ory'",
!PStringUtils::BeginsWithNoCase("THEORY", "ory"));
}
//--------------------------------------------------------------------------
static void inspect(const std::string &str)
{
std::cout << "Inspecting '" << str << "':" << std::endl;
std::cout << " Split(' \\t,;') = "
<< vecToStr(PStringUtils::Split(str, " \t,;")) << std::endl;
std::cout << " IsInt = " << (PStringUtils::IsInt(str) ? "true" : "false") << std::endl;
std::cout << " IsFloat = " << (PStringUtils::IsFloat(str) ? "true" : "false") << std::endl;
bool ok = false;
int i = PStringUtils::ToInt(str, &ok);
std::cout << " ToInt = " << i << " (ok=" << (ok ? "true" : "false") << ")" << std::endl;
double d = PStringUtils::ToDouble(str, &ok);
std::cout << " ToDouble = " << d << " (ok=" << (ok ? "true" : "false") << ")" << std::endl;
}
//--------------------------------------------------------------------------
static void usage(const char *prog)
{
std::cout
<< "usage: " << prog << " [options] [string ...]\n"
<< "\n"
<< "Little stand-alone test driver for the PStringUtils class. It exercises\n"
<< "Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase /\n"
<< "ContainsNoCase / BeginsWithNoCase.\n"
<< "\n"
<< "options:\n"
<< " -h, --help show this help and exit\n"
<< " -i, --interactive interactive mode: type a string at the prompt and\n"
<< " see the result of every PStringUtils method\n"
<< " (empty line, 'quit'/'exit' or Ctrl-D leaves)\n"
<< "\n"
<< "arguments:\n"
<< " string ... one or more strings to inspect; the result of every\n"
<< " PStringUtils method is printed for each of them\n"
<< "\n"
<< "with no options and no arguments the built-in test suite is run; the exit\n"
<< "code is 0 if all checks pass and 1 otherwise.\n"
<< "\n"
<< "examples:\n"
<< " " << prog << " run the built-in test suite\n"
<< " " << prog << " \" -42xyz\" 1.5e-3 inspect the given strings\n"
<< " " << prog << " -i interactive mode\n";
}
//--------------------------------------------------------------------------
static void interactive()
{
std::cout << "==== PStringUtils interactive mode ====" << std::endl;
std::cout << "Enter a string to test the methods on it." << std::endl;
std::cout << "An empty line or 'quit' leaves." << std::endl << std::endl;
std::string line;
while (true) {
std::cout << "strToNum> " << std::flush;
if (!std::getline(std::cin, line)) // EOF (e.g. Ctrl-D)
break;
if (line.empty() || line == "quit" || line == "exit")
break;
inspect(line);
std::cout << std::endl;
}
}
//--------------------------------------------------------------------------
int main(int argc, char *argv[])
{
if (argc > 1) {
const std::string arg1 = argv[1];
if (arg1 == "-h" || arg1 == "--help") {
usage(argv[0]);
return 0;
}
if (arg1 == "-i" || arg1 == "--interactive") {
interactive();
return 0;
}
// ad-hoc mode: inspect the strings given on the command line
for (int i = 1; i < argc; ++i) {
inspect(argv[i]);
std::cout << std::endl;
}
return 0;
}
std::cout << "==== PStringUtils test suite ====" << std::endl << std::endl;
testSplit();
testIsInt();
testIsFloat();
testToInt();
testToDouble();
testCaseHelpers();
std::cout << std::endl
<< "==== summary: " << gPassed << " passed, "
<< gFailed << " failed ====" << std::endl;
return (gFailed == 0) ? 0 : 1;
}