Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using sscanf to extract an int from a string in C++

Tags:

c++

c

scanf

My function must process strings that look like say hello y(5) or data |x(3)|, and I need to be able to extract the integer shown and store it into a separate int variable called address. However, some strings passing through will not have any integers, and for these the address must default to 0. When a string contains an integer, it will always be in between parentheses. I've attempted to use sscanf, but, being very new to sscanf, I'm encountering problems.. For some reason, the address always reads as 0. Here's my code:

void process(string info)
{
int address = 0; // set to 0 in case info contains no digits
sscanf(info.c_str(), "%d", address);
.
.
.
// remainder of code makes other function calls using the address, etc
}

Any ideas as to why the sscanf fails to find the integer in between parentheses? Thanks!

like image 706
J. Shephard Avatar asked Nov 23 '15 04:11

J. Shephard


People also ask

How does sscanf work in C?

Description. The sscanf() function reads data from buffer into the locations that are given by argument-list. Each argument must be a pointer to a variable with a type that corresponds to a type specifier in the format-string.

Why is sscanf unsafe?

The reason why sscanf might be considered bad is because it doesnt require you to specify maximum string width for string arguments, which could result in overflows if the input read from the source string is longer.

What value does sscanf return?

Return Value The sscanf() function returns the number of fields that were successfully converted and assigned. The return value does not include fields that were read but not assigned. The return value is EOF when the end of the string is encountered before anything is converted.


2 Answers

why the sscanf fails to find the integer in between parentheses

The "%d" in sscanf(info.c_str(), "%d", address) will cause sscanf() to stop scanning once a non-numeric sequence detected. Text like "(5)" will simply stop scanning at the "(".

Instead code need to to skip over non-numeric text.


Pseudo-code

  in a loop
    search for any of "-+0123456789"
    if not found return 0
    convert from that point using sscanf() or strtol()
    if that succeeds, return number
    else advance to next character

Sample code

int address;
const char *p = info.c_str();
for (;;) {
  p += strcspn(p, "0123456789+-");
  if (*p == 0) return 0;
  if (sscanf(p, "%d", &address) == 1) {
    return address;
  }
  p++;
}

Notes:

The strcspn function computes the length of the maximum initial segment of the string pointed to by s1 which consists entirely of characters not from the string pointed to by s2. C11 7.24.5.3 2


If code wants to rely on " it will always be in between parentheses." and input like "abc()def(123)" does not occur which has preceding non-numeric data between ().:

const char *p = info.c_str();
int address;
if (sscanf(p, "%*[^(](%d", &address)==1) {
  return address;
}
return 0;

or simply

int address = 0;
sscanf(info.c_str(), "%*[^(](%d", &address);
return address;
like image 199
chux - Reinstate Monica Avatar answered Sep 18 '22 18:09

chux - Reinstate Monica


You could use something as simple as this where strchr finds the first occurrence of "(" then use atoi to return the integer which will stop at the first non-digit.

  char s1[] = "hello y(5)";
  char s2[] = "data [x(3)]";
  char s3[] = "hello";

  int a1 = 0;
  int a2 = 0;
  int a3 = 0;

  char* tok = strchr( s1, '(');
  if (tok != NULL)
    a1 = atoi(tok+1);

  tok = strchr( s2, '(');
  if (tok != NULL)
    a2 = atoi(tok+1);

  tok = strchr(s3,'(');
  if (tok != NULL)
    a3 = atoi(tok+1);

  printf( "a1=%d, a2=%d, a3=%d", a1,a2,a3);

  return 0;
like image 30
AndersK Avatar answered Sep 21 '22 18:09

AndersK