Forcing String to Int Function to Consume Entire String

Forcing String to int Function to Consume Entire String

Edit: In c++17 or later from_chars is preferred. See here for more: https://topanswers.xyz/cplusplus?q=724#a839


For a given string str there are several ways to accomplish this each with advantages and disadvantages. I've written a live example here: https://ideone.com/LO2Qnq and discuss each below:

strtol

As suggested here strtol's out-parameter can be used to get the number of characters read. strtol actually returns a long not an int so a cast is happening on the return.

char* size;
const int num = strtol(str.c_str(), &size, 10);

if(distance(str.c_str(), const_cast<const char*>(size)) == str.size()) {
cout << "strtol: " << num << endl;
} else {
cout << "strtol: error\n";
}

Note that this uses str.c_str() to refer to the same string. c_str Returns pointer to the underlying array serving as character storage not a temporary if you have C++11:

c_str() and data() perform the same function

Also note that the pointer returned by c_str will be valid between the strtol and distance calls unless:

  • Passing a non-const reference to the string to any standard library function
  • Calling non-const member functions on the string, excluding operator[], at(), front(), back(), begin(), rbegin(), end() and rend()

If you violate either of these cases you'll need to make a temporary copy of i's underlying const char* and perform the test on that.

sscanf

sscanf can use %zn to return the number of characters read which may be more intuitive than doing a pointer comparison. If base is important, sscanf may not be a good choice. Unlike strtol and stoi which support bases 2 - 36, sscanf provides specifiers for only octal (%o), decimal (%d), and hexadecimal (%x).

size_t size;
int num;

if(sscanf(str.c_str(), "%d%zn", &num, &size) == 1 && size == str.size()) {
cout << "sscanf: " << num << endl;
} else {
cout << "sscanf: error\n";
}

stoi

As suggested here stoi's output parameter works like sscanf's %n returning the number of characters read. In keeping with C++ this takes a string and unlike the C implementations above stoi throws an invalid_argument if the first non-whitespace character is not considered a digit for the current base, and this unfortunately means that unlike the C implementations this must check for an error in both the try and catch blocks.

try {
size_t size;
const auto num = stoi(str, &size);

if(size == str.size()) {
cout << "stoi: " << num << endl;
} else {
throw invalid_argument("invalid stoi argument");
}
} catch(const invalid_argument& /*e*/) {
cout << "stoi: error\n";
}

Checking string to int error without loops/conditionals

stoi is indeed what you'll want to use.

Given an input in string S one possible way to handle it would be:

try {
cout << stoi(S) << " is a number\n";
} catch(const invalid_argument& /*e*/) {
cout << S << " is not a number\n";
}

Live Example

The violation here is that stoi is only required to cosume the leading numeric part of the string not ensure that the entire string is an int. So "t3st" will fail cause it's not led by a number, but "7est" will succeed returning a 7. There are a handful of ways to ensure that the entire string is consumed, but they all require an if-check.

Powershell string to number functions?

Are you looking for M == MB or M == 1E6? If it is the former, PowerShell understands KB, MB, GB and TB e.g.:

C:\PS> Invoke-Expression "2MB"
2097152

Big caveat here with Invoke-Expression, if you're getting the string from a user, file, i.e. an untrusted source. You have to be careful about executing it. Say the string is "2MB; Remove-Item C:\ -Recurse -Force -Whatif -EA 0", you'd have a bad day using Invoke-Expression on that string. BTW, I'm being nice here by adding the -Whatif. :-)

If it is the latter, you could do a regex -replace followed by a coercion e.g.:

C:\PS> [long]("3.34 B" -replace '(\d+)\s*(B)','$1E9')
3340000000

Which is faster, int to String or String to int?

Actually, your gut may be wrong (and I emphasise may, see my comments below on measuring). To convert a string to an integer requires a series of multiply/add operations. To convert an integer to a string requires division/modulo. It may well be that the former is faster than the latter.

But I'd like to point out that you should measure, not guess! The landscape is littered with the corpses of algorithms that relied on incorrect assumptions.

I would also like to point out that, unless your calculator is expected to do huge numbers of calculations each second (and I'm talking millions if not billions), the difference will be almost certainly be irrelevant.

In the vast majority of user-interactive applications, 99% of all computer time is spent waiting for the user to do something.

My advice is to do whatever makes your life easier as a developer and worry about performance if (and only if) it becomes an issue. And, just to clarify, I would suggest that storing them in native form (not as strings) would be easiest for a calculator.

PHP String-to-Integer Exception

And here's another hacky solution using the try {..} catch mechanism:

try {
new ReflectionClass('ReflectionClass' . ((int)$SS . "" !== $SS));
echo $SS;
} catch (Exception $e) {
echo "Bad StringBad String";
}

It works like this: if the string $SS doesn't actually resolve to an integer, the code will try to lookup the ReflectionClass1 class, which, in the end, will throw a ReflectionException.

The parsing works by simply casting $SS to int and then back to string and comparing it to the initial value.

String to space separated integer

Continuing from the comments and the additional answer, to parse and separate the string into a space separated series of integers decreasing by one, there are probably a number of differing approaches you can take. The biggest design question is whether you start with the length of the input string, cut it in half and then work backwards decreasing the number of digits you check for adjacent values by one -- or whether you start at the beginning and work toward the end incrementing the number of digits being considered along the way.

Regardless of the direction you choose, the twist is handling/checking adjacent values with a different number of digits. Your second example, 109876543, hits at the heart of this twist, where you must code a way to check the 2-digit value 10 against the next single-digit value in the series 9. There is just no pretty way to do this. One reasonable way is to simply compute the smallest number that can be represented by n-digits (e.g. 10, 100, 1000, ...). Essentially 10^(n-1) (where we let int expn = n - 1;). If your first value v1 is equal to 10^(n-1), then reduce the number of characters you consider for the next smallest values. Something like the following:

        while (expn--)                  /* loop to build 10 ^ (n-1)    */
x10 *= 10; /* compute 10 ^ (n-1), 10, 100 */
if (v1 == x10) /* compare against v1 */
n--; /* reduce t2 by 1-char/digit */

The remainder of the task is just basically a brute force check with a minimum number of validations necessary to protect array bounds, while handling adding values to your integer array (or however you want to store values until you validate or invalidate the remaining characters in the string) while you work your way through the remaining characters.

Putting all the pieces together, and noting there are many, many ways to code this, this example being only one, you could do something similar to the following. Note, the code simply handles the conversion from ASCII to int in the single-digit series case by subtracting '0' from the character value, for multi-digit conversions, strtol is used with a validation check of errno. The code works from beginning to end of the string incrementing the number of digits checked until the end of the string is reached. If a solution is found, a space-separated list of integers is output, otherwise, "no solution found." is output. The code is commented to help you work though it.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define MAXI 256

int main (int argc, char **argv) {

int a[MAXI] = {0}, i = 1, idx = 0, n = 1, len;
char *p = argc > 1 ? argv[1] : "9876543";

printf ("string : %s\n", p); /* original input string */

len = (int)strlen (p); /* get length */

while (i + n <= len && n < len) { /* loop until conditions met */
if (n >= MAXI) { /* protect int array bounds */
fprintf (stderr, "warning: array full, %d elements filled.\n", n);
break;
}
if (n == 1) { /* handle single digits series */
if (p[i - 1] == p[i] + 1) { /* previous equal current + 1? */
if (!idx) /* if array index == 0 */
a[idx++] = p[i - 1] - '0'; /* store first integer */
a[idx++] = p[i] - '0'; /* store current integer */
i++; /* increment string index */
}
else
n++, i = n, idx = 0; /* increment n-digits to check */
} /* set i = n, zero array index */
else { /* handle multi-digit values */
char t1[MAXI] = "", t2[MAXI] = ""; /* tmp strings for values */
int v1 = 0, v2 = 0, /* tmp for coverted values */
expn = n - 1, x10 = 1, /* 10 ^ expn for n-- test */
norig = n; /* n to restore on no match */

strncpy (t1, p + i - n, n); /* copy n-digits for 1st value */

errno = 0;
v1 = (int) strtol (t1, NULL, 10); /* convert to int/validate */
if (errno) {
fprintf (stderr, "error: failed conversion, i: %d, n: %d\n",
i, n);
return 1;
}

while (expn--) /* loop to build 10 ^ (n-1) */
x10 *= 10; /* compute 10 ^ (n-1), 10, 100 */
if (v1 == x10) /* compare against v1 */
n--; /* reduce t2 by 1-char/digit */

strncpy (t2, p + i, n); /* copy n-digits for 2nd value */

errno = 0;
v2 = (int) strtol (t2, NULL, 10); /* convert to int/validate */
if (errno) {
fprintf (stderr, "error: failed conversion, i: %d, n: %d\n",
i, n);
return 1;
}

if (v1 == v2 + 1) { /* check decreasing values */
if (!idx) /* if array index == 0 */
a[idx++] = v1; /* store first integer */
a[idx++] = v2; /* store current integer */
i += n; /* increment string index */
}
else {
n += n < norig ? 2 : 1; /* reset n if no match */
i = n; /* set string index to n */
idx = 0; /* reset array index to 0 */
}
}
}

if (idx && n < len) { /* if array has values, output */
printf ("integers :");
for (int j = 0; j < idx; j++)
printf (" %*d", n, a[j]);
putchar ('\n');
}
else
printf ("no solution found.\n");

return 0;
}

note: not all corner-cases have been evaluated and the input is presumed to contain only digits. (you are free to add the check for isdigit if you expect otherwise), further testing on your part should be done to satisfy yourself any odd-ball cases are sufficiently covered.

Example Use/Output

$ ./bin/intsepdecr
string : 9876543
integers : 9 8 7 6 5 4 3

$ ./bin/intsepdecr 109876543
string : 109876543
integers : 10 9 8 7 6 5 4 3

$ ./bin/intsepdecr 400399398397
string : 400399398397
integers : 400 399 398 397

$ ./bin/intsepdecr 400399398396
string : 400399398396
no solution found.

$ ./bin/intsepdecr 101176543
string : 101176543
no solution found.

Look things over and let me know if you have any further questions.

int from string in go

Import the strconv package (src/pkg/strconv), then use strconv.Atoi("10")



Related Topics



Leave a reply



Submit