Live data from Hacker News

No way to parse integers in C (2022)

blog.habets.se

81–90 of 117 posts

Re: No way to parse integers in C (2022)

#81

Cant you just: for(int i = 0; i = 0) { ret = ret * 10 + characters[i] - 48; } else { return ERROR; } } return ret; Adjust until it actually works, but you get the picture.

Here's a readability tip for working with ASCII numbers: Treat adding and subtracting the ASCIIness as you would multiplying and dividing by a unit in physics. You can add '0' to convert a numeral to ASCII and subtract '0' to convert it back, and you can do direct comparisons between ASCII numerals.

    if(characters[i] = '0')
    {
      ret = ret * 10 + characters[i] - '0';
    }

Re: No way to parse integers in C (2022)

#82
This is not a hard thing to do without using a library. The code below is easily adapted to the unsigned case and/or arbitrary base rather than 10.

    #include 
    int main(int argc, char **argv) {
        if (argc != 2) {
            fprintf(stderr, "usage: require one numeric argument");
        }
        char *nump = argv[1];
        unsigned neg = 0;
        unsigned long long ures = 0;
        if (*nump == '-') {
            neg = 1;
            nump = nump + 1;
        }
        if (!*nump) {
            fprintf(stderr, "require non empty string\n");
            return 1;
        }
        char b;
        while (b = *nump++) {
            if (b >= '0' && b = ' ') {
                    fprintf(stderr, "invalid char '%c' in '%s'\n", b, argv[1]); 
                } else {
                    fprintf(stderr, "invalid byte '%d' in '%s'\n", b, argv[1]);
                }
                return 1;  
            }
        }
        long long res = (long long) ures;
        if (neg) {
            if (ures  0x7FFFFFFFFFFFFFFFULL) {
            fprintf(stderr, "overflow in '%s'\n", argv[1]);
            return 1;
        }
        fprintf(stdout, "result: %lld\n", res);
        return 0;
    }

Re: No way to parse integers in C (2022)

#83
There's no one correct way to parse integers. Do you want to support 0x prefixes? Is a leading zero an indicator or octal, a zero-padded decimal, or a syntax error? Are you willing to accept a leading "+"? Are leading whitespaces OK? Trailing ones? Is 0x0c a whitespace? What about all the weird Unicode ones? Do you allow exponential notation (1e1)? Etc, etc.

In every language, the standard library makes some assumptions about this. In JavaScript, an empty string parses to zero.

The standard C library, which dates back to the stone age, does the simplest thing you can do without range checking, because, well, that's kinda the C paradigm. If you want parsing that handles edge cases in a specific way, you do it yourself. It's just digits.

Re: No way to parse integers in C (2022)

#84
post #82

This is not a hard thing to do without using a library. The code below is easily adapted to the unsigned case and/or arbitrary base rather than 10. #include int main(int argc, char **argv) { if (argc != 2) { fprintf(stderr, "usage: require one numeric argument"); } char *nump = argv[1]; unsigned neg = 0; unsigned long long ures = 0; if (*nump == '-') { neg = 1; nump = nump + 1; } if (!*nump) { fprintf(stderr, "requir…

The bound on ures
    $ clang parseint.c -fsanitize=undefined -O0 -g -o parseint
    $ ./parseint -9223372036854775808
    parseint.c:38:23: runtime error: negation of -9223372036854775808 cannot be represented in type 'long long'; cast to an unsigned type to negate this value to itself
    result: -9223372036854775808
edit: this is just to show that getting undefined behavior right is hard!

Re: No way to parse integers in C (2022)

#85
post #35

I wasn't in this class myself, but one prof at my alma mater started his "Programming 201" class with the simplest assignment: write a C program that accepts two integers from the user and prints their sum. It actually was the only assignment for the rest of the semester, since he has a test suite that would humiliate the students gently at first, but would ultimately pipe a billion nines into stdin as the first argu…

Perfect is the enemy of good.

That's true for product development, but it's not true for mathy libraries. Perfect is achievable. For a released software that humans will decide to use or not, rapid iteration is great. But also: https://randomascii.wordpress.com/2014/01/27/theres-only-fou...

Precision and exactitude and formally proven correct software can exist in some problem domains, and it's kind of silly to not achieve that when it's achievable.

Re: No way to parse integers in C (2022)

#86
post #61

Earlier quoted context omitted.

Is their misbehavior part of the spec as well? If not, we can always add the correct behavior to the spec and let anyone who implemented a broken version deal with fixing every program compiled using it.

Fair enough. For strtoul and friends, maybe? 7.24.1 is pretty dense, but the key parts are "the expected form of the subject sequence is a sequence of letters and digits representing an integer with the radix specified by base, optionally preceded by a plus or minus sign […] If the correct value is outside the range of representable values […] ULONG_MAX […] is returned". So the "expected form" allows a minus sign, bu…

Passing a negative value to a function that is specifically for converting strings into unsigned numbers is pretty much an error. In the case of functions that return an unsigned number, at least, negative return values can represent errors.

It’s more fun when the result can be signed though. Maybe strcmp with the representation of the LONG_MAX, and if it doesn’t match, call strtol and watch for a LONG_MAX indicating an error.

C is a bit messy. Would be nicer to return a struct with a possible error and the desired value, Golang style.

Re: No way to parse integers in C (2022)

#87

I wasn't in this class myself, but one prof at my alma mater started his "Programming 201" class with the simplest assignment: write a C program that accepts two integers from the user and prints their sum. It actually was the only assignment for the rest of the semester, since he has a test suite that would humiliate the students gently at first, but would ultimately pipe a billion nines into stdin as the first argu…

Would be fun to write a program that arranges to send the input into dc(1) and just outsource the whole problem to Ken or Rob or whoever wrote it. :)

It would be fun, but were I the teacher I'd commend you for your ingenuity, and then ask you to return to your desk to complete the assignment.

Re: No way to parse integers in C (2022)

#89

I wasn't in this class myself, but one prof at my alma mater started his "Programming 201" class with the simplest assignment: write a C program that accepts two integers from the user and prints their sum. It actually was the only assignment for the rest of the semester, since he has a test suite that would humiliate the students gently at first, but would ultimately pipe a billion nines into stdin as the first argu…

It's a little awkward, because you'd need to parse the strings in reverse, but if all you need to do is sum, you can do it one digit at a time, while at any given moment only handling only one character from each input string, a carry byte, and one output character.

How do you know where the first string ends and the second starts? Did you miss the "stdin" part?

This is not

    ./program first_number second_number

Re: No way to parse integers in C (2022)

#90
> It is not OK to stop at the first sign of trouble, and return whatever maybe is right. “123timmy” is not a number, nor is the empty string.

None of the C functions referenced (atol, strtol, sscanf) are number-parsing functions per se. Rather, they're numeric-lexeme scanning+extraction functions.

These functions are all designed to avoid making any assumptions about the syntax of the larger document the numeric lexeme might be embedded in. You might, after all, be using a syntax where numbers can come with units on the end. Or you might be reading numbers as comma-separated values.

And, as a key point the author might be missing: C, in being co-designed with UNIX, offers primitives tuned for the context of:

- writing UNIX CLI tools that work with unbounded streams of input (i.e. piped output from other UNIX CLI tools),

- where, crucially, the stream is just text, and so carries no TLV-esque framing protocol to tell you the definitive length of a thing;

- and nor (especially in early memory-constrained systems) are you able to perform allocations of heap memory in order to employ an unbounded growable buffer for retaining the current lexeme until you do reach the end of it (which, if you could, would let you use a scanner state-machine that doubles as a parser/validator, returning either a parsed value or an error)

- but instead, to deal with the 1. unbounded input, 2. of textual encoding, 3. in constant memory, you must eagerly scan the input stream (i.e. synchronously reduce over each received byte, or at most each fixed-length N-byte chunk using a static or stack-allocated fixed-length buffer, discarding the original string bytes once reduced-over) to produce lexically-decoded (but not parsed/validated) lexemes; and then do this again, on a higher level, feeding your stream of lexemes into a fixed-sized sum-typed ring-buffer (i.e. an array-of-union-typed-lexeme-struct-type-entries), where you can then invoke a function that attempts to scan over + consume them (but unlike the original stream-parsing function, doesn't consume the buffer unless successful, and so isn't functioning as a scanner per se, but rather as an LR parser.)

If you're not writing UNIX CLI tools, direct use of the C-stdlib numeric-lexeme scan functions is operating on the wrong abstraction layer. What you want, if you have pre-framed strings that are "either valid numbers or parse errors", is to implement an actual parsing function... that can then invoke these numeric-lexer functions to do the majority of its work.

And if you're writing C, and yet you're not in UNIX-pipeline unbounded-text-stream land, but rather are parsing well-defined bounded-length "documents" (like, say, C source files)... then you probably want to use a real lexer-generator (like flex) to feed a parser-generator (like yacc/bison). Where:

- you'd validate the token in context, in the parsing phaase;

- and your lexing rules would make certain classes of input invalid at lexing time. (E.g. you can write your lexeme matching rules such that multi-digit numbers with leading zeroes, or floating-point values with no digits before/after the decimal place, simply aren't "numbers" from your lexer's perspective.)

...which means that, once again, you can "get away with" invokeing the regular C numeric-lexeme scanner functions; i.e. `yylval = atoi(yytext);` in bison terms. (And you'd want to, since doing so saves memory vs. keeping the numbers around as strings.)

Post reply on HN