2

How can I split a 9 character array with 3-2-2-2 cadence and convert it to 4 different int variables?

example:

char s[9] = {'9', '8', '7', '6', '5', '4', '3', '2', '1'}
a=987;
b=65;
c=43;
d=21;
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93

3 Answers3

3

You can use sscanf() with caution for this job:

#include <stdio.h>

int main() {
    char s[9] = { '9', '8', '7', '6', '5', '4', '3', '2', '1' };
    int a, b, c, d;

    {  // check that the input data starts with 9 digits
        int n = 0;
        sscanf(s, "%9*[0-9]%n", &n);
        if (n != 9)
            return 1;
    }
    // parse the input data into 4 integers composed of 3, 2, 2 and 2 digits
    if (sscanf(s, "%3d%2d%2d%2d", &a, &b, &c, &d) == 4) {
        printf("%d, %d, %d, %d\n", a, b, c, d);
    }
    return 0;
}

Output: 987, 65, 43, 21

The conversion specifiers in scanf() support a numeric argument specifying the maximum number of characters to consume from the source: %3d means convert a decimal integer number consuming at most 3 characters. 4 consecutive conversions, each with a specified length of 3 or 2 characters implement your goal precisely. Note however that initial white space consumed before each of the conversions is not counted so you must ensure that the input string starts with 9 digits for the above code to work, which can be performed with another sscanf() unless the input is known to have the proper contents.

Alternately, if you know the input contains only digits, you can parse them on the fly with a simple function:

#include <stdio.h>

int get_int(char **pp, int n) {
    int num = 0;
    while (n --> 0) {
        num = num * 10 + (*(*pp)++ - '0');
    }
    return num;
}

int main() {
    char s[9] = { '9', '8', '7', '6', '5', '4', '3', '2', '1' };
    char *p = s;
    int a = get_int(&p, 3);
    int b = get_int(&p, 2);
    int c = get_int(&p, 2);
    int d = get_int(&p, 2);
    printf("%d, %d, %d, %d\n", a, b, c, d);
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
1

You can use sscanf to parse even a character array that does not have a string terminator, such as yours, as long as you give maximum field widths in all your conversion specifiers, such that sscanf does not attempt to consume data past the end of the array. For example:

char s[9] = {'9', '8', '7', '6', '5', '4', '3', '2', '1'}
int a, b, c, d;

sscanf(s, "%3d%2d%2d%2d", &a, &b, &c, &d);

Do note that the field widths are not sufficient by themselves to ensure that the array bounds are not overrun, because %d directives cause (s)scanf to consume leading whitespace, too. If the character data (not a string because it is not terminated) comes from an external source, then you really ought to verify its contents before parsing.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

Here's one way:

#include <assert.h>
#include <stdio.h>

static int char_to_digit(char const ch) {
  assert(ch >= '0' && ch <= '9');
  return ch - '0';
}

static int arr_to_int(char const* arr, int const sz) {
  int res = 0;
  for (int i = 0; i < sz; ++i)
    res = res * 10 + char_to_digit(arr[i]);
  return res;
}

int main() {
  char const s[9] = {'9', '8', '7', '6', '5', '4', '3', '2', '1'};

  int const a = arr_to_int(s, 3);
  int const b = arr_to_int(s + 3, 2);
  int const c = arr_to_int(s + 5, 2);
  int const d = arr_to_int(s + 7, 2);

  printf("%d, %d, %d, %d\n", a, b, c, d);
}

The job is split between two small functions with descriptive names.

Live

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • Never use assert. If the code happens to ever be compiled with `NDEBUG` defined, `assert()` is a no-op. And aborting the process is likely overkill in the first place. – Andrew Henle Feb 21 '21 at 14:00
  • @AndrewHenle I disagree. This is the intended use case for `assert`. Asserts should be used to make the interface explicit during debug time, not to enforce it in release builds. – Aykhan Hagverdili Feb 21 '21 at 14:04
  • 1
    Instead of repeatedly computing powers of ten, use this: `static int arr_to_int(char const *arr, int sz) { int res = 0; for (int i = 0; i < sz; ++i) res = res * 10 + (arr[i] - '0'); return res; }` – chqrlie Feb 21 '21 at 16:33
  • There are better ways to enforce interface constraints than `assert()`. Do you really think it's OK to relax those constraints in release builds? "Yeah, we develop and test with this version but then we relax all the constraints in release. What? It failed without a log? That's UNPOSSIBLE! We tested that. Oh, yeah, that's right. We used `assert()` and didn't actually develop and test what we released..." And the proper response to getting improper input from the user is not `abort()`. Throwing away all the error checking in release builds is not proper either. – Andrew Henle Feb 21 '21 at 16:36
  • Tricycles are intended to ride, too. That doesn't make them the proper tool for adult transportation. `assert()` is a tricycle. – Andrew Henle Feb 21 '21 at 16:40
  • @chqrlie that's a great idea. Thank you! – Aykhan Hagverdili Feb 21 '21 at 16:44
  • @AndrewHenle Failing an assert is like derefencing a null pointer - It should _never_ happen in a release build. I am not validating user input in this code, I am assuming this data is safe and tested, so I _assert_ it. See [Is assert evil?](https://stackoverflow.com/q/1854302/10147399) – Aykhan Hagverdili Feb 21 '21 at 18:54
  • @AyxanHaqverdili *It should never happen in a release build.* That's your standard? Since it should never happen, you ignore the possibility? That's adorable. I'm glad corrupt input data never happens in your world so it's OK to crash the process. I prefer to write code to a higher standard than that. – Andrew Henle Feb 21 '21 at 19:02
  • @AndrewHenle imagine `memcpy` checked for nullptr and logged it somewhere... Data corruption should be tested elsewhere, not in the middle of computation. `assert` isn't an error handling method, it's a sanity-check. Please refer to the link I provided for further discussion. – Aykhan Hagverdili Feb 21 '21 at 19:05
  • @AyxanHaqverdili The fact that your link that you use to try supporting your claims starts off with a big box that contains **We expect answers to be supported by facts, references, or expertise** is quite telling. Crashing your entire process because you got bad data or having your data validation elided entirely because someone compiled with `NDEBUG` defined are both poor responses to real-world conditions and the product of a development process with low standards for robustness and reliability. Full stop. – Andrew Henle Feb 21 '21 at 19:11
  • "I'm sorry, but the NOAA bouy out in the middle of the ocean must have a bad salinity sensor and every now and then it sends some weird data. Yeah, it crashes the entire data collection process every time that happens. Sorry, but some internet page said `assert()` was a good idea." That's what you're saying is acceptable, and that's exactly why I say it's bad code produced by low standards. Code I write handles that situation. You're code won't. – Andrew Henle Feb 21 '21 at 19:13