Sizzler Sizzler - 1 year ago 39
C Question

sscanf usage in very specific format

I'm trying to use sscanf with a string that is exactly in the following format:


integer1 integer2


so that i can store int1 and int2 into an array

I want sscanf to check if only two numbers are entered separated by only ONE space, with no leading or trailing whitespace at all.
Examples of what should and shouldn't work:

Should

4 7


Shouldn't

4 7
4 7[WHITESPACE]
4 7
4 7a


I've been trying to use it in the following way

sscanf(str,"%d %d",&value1,&value2);

if (*STRING CONTAINS INVALID CHARACTERS OR WHITESPACE*){
exit from parent function }

Answer Source

Step 1: read the line (note OP implies code is starting with a string so this step is illustrative.)

char buf[100];
if (fgets(buf, sizeof buf, stdin) == NULL) Handle_EOF_or_Error();
// strip potential \n
buf[strcspn(buf, "\n")] = '\0';

check if only two numbers are entered separated by only ONE space, with no leading or trailing whitespace at all

Step 2: Scan per format. "%d" allows for leading whitespace - so code needs to see what is before the numeric text. Use "%n" to store the scan offset for later whitespace detection. %*1[ ] to scan (not save) 1 and exactly 1 space.

int i[2];
int sep[3];
int cnt = sscanf(buf, "%n%d%*1[ ]%n%d%n", &sep[0], &i[0], &sep[1], &i[1], &sep[2]);

// 2 int            leading space?        leading space?        \0 ?
if (cnt != 2 || isspace(buf[sep[0]]) || isspace(buf[sep[1]]) || buf[sep[2]]) {
  Handle_Bad_Input();
} else {
  Success();
}

Simplification idea per @Jonathan Leffler

int i[2];
int sep[2];
int cnt = sscanf(buf, "%d%*1[ ]%n%d%n", &i[0], &sep[0], &i[1], &sep[1]);

// 2 int        leading space?     leading space?          \0 ?
if (cnt != 2 || isspace(buf[0]) || isspace(buf[sep[0]]) || buf[sep[1]]) { 

Note: pedantic code would cast the char with (unsigned char) to handle negative char as is...() functions are defined for the unsigned char range and EOF.

isspace(buf[...]) --> isspace((unsigned char) buf[...]) 

---

A more robust approach would call a helper function to assess the integer - which can be crafted per code's needs. Using strtol() is a good starting point for define-ness and overflow detection.

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>

// Return error status
bool scan_int(const char **input, int *dest) {
  errno = 0;
  const char *s = *input;

  if (isspace((unsigned char) *s) {
    // reject leading space
    return true;
  }

  char *endptr;
  long num = strtol(s, &endptr, 10);
  if (s == endptr) {
    // no conversion
    return true;
  }
  *input = (const char *) endptr;
  if (errno == ERANGE) {
    // Overflow
    return true;
  }
#if LONG_MAX > INT_MAX  || LONG_MIN < INT_MIN
  if (num > INT_MAX || num < INT_MIN) {
    errno = ERANGE;
    // Overflow
    return true;
  }
#endif
  *dest = (int) num;
  return false;
}

// Return error status - On success, return false
bool sz_int_space_int(const char *input, int *dest) {
  return scan_int(&input, &dest[0]) || *input++ != ' '
      || scan_int(&input, &dest[1]) || *input;
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download