How to reduce or replace the else if statements in C language?

Viewed 157

I'm using the code as shown below

  if (strcmp(oid, ".1.3.6.1.4.1.53864.1.1.0") == 0) 
  {
      return 1;
  } 
  else if (strcmp(oid, ".1.3.6.1.4.1.53864.1.2.0") == 0) 
  {
      return 2;
  }
  ...
  else if (strcmp(oid, ".1.3.6.1.4.1.53864.1.n.0") == 0) 
  {
      return n;
  }

Now the problem is updated n value is 250. Can anyone please give me any idea to reduce this else if statement?

I tried to search on StackOverflow but those answers are related to the other languages and I want an idea that I can implement in the C language.

4 Answers

This should do the trick. Do note that it's not completely equivalent. It skips checking for the ending ".0", but that's trivial to add if you want it. I also omitted the error check for sscanf, but that's also easy to add.

char prefix[] = ".1.3.6.1.4.1.53864.1.";
if(memcmp(prefix, oid, sizeof prefix-1) == 0) {
    int n;
    sscanf(&oid[sizeof prefix-1], "%d", &n);
    return n;
}

If you are very certain the strings are in the correct format, you can skip the memcmp completely. Another approach, depending on your requirements is this:

sscanf(".%*d.%*d.%*d.%*d.%*d.%*d.%*d.%*d.%d.%*d", %n);

Do note that I'm using %*d for all but one integer. That means I'm reading them but not storing them.

You can use strncmp() to match the prefix (".1.3.6.1.4.1.53864.1."), extract the text value say by using strchr() and convert it with strtol() for the return, and match the suffix (".0"):

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

int oid_sub(const char *s) {
    const char prefix[] = ".1.3.6.1.4.1.53864.1.";
    if(strncmp(s, prefix, sizeof(prefix) - 1))
        return -1;
    char *end;
    // check n for under and overflow?
    int n = strtol(s + sizeof(prefix) - 1, &end, 10);
    const char suffix[] = ".0";
    if(end == s + sizeof(prefix) - 1 || strcmp(end, suffix))
        return -1;
    return n;
}

int main() {
    char *s = ".1.3.6.1.4.1.53864.1.42.0";
    printf("%d\n", oid_sub(s));
}

I would prefer a regex based solution:

#include <regex.h>

int oid_sub2(const char *s) {
    regex_t r;
    const char regex[] = "^\\.1\\.3\\.6\\.1\\.4\\.1\\.53864\\.1\\.([^.]+)\\.0$";
    if(regcomp(&r, regex, REG_EXTENDED))
        return -1;
    regmatch_t m[2];
    if(regexec(&r, s, 2, m, 0))
        return -1;
    return atoi(s + m[1].rm_so);
}

Note, atoi() fails with 0 on error, so you could tweak the regex to ensure it matches numbers or use strtol() as above (but no need to check the suffix as the regex already takes care of that).

Looks like you want the value in the 2nd last field of the string.

Here's a rough cut. You should add checking before considering it done...

char *tmp = strdup( old );
char *lastSep = strrchr( tmp, "." ); // want to go back one more.
*lastSep = '\0';
*lastSep = strrchr( tmp, "." ); // now left of desired field.
int n = atoi( lastSep + 1 );
free( tmp );
return n;

Or, if you want to "walk the string" left to right...

char *sep = old;
for( int i = 0; i < 9 && ( sep = strchr( sep, "." ) ) != NULL; i++ )
    sep += 1;
return sep ? atoi( sep + 1 ) : -1;

Or, simply count the separaters, stopping when you please.

for( int cnt = 0; *old && cnt < 9; )
    if( *old++ == '.' && ++cnt == 9 )
        return atoi( old );
return -1;

Here is a simple method using sscanf() to match the pattern and extract the number:

int get_id(const char *oid) {
    int n;
    char ch;
    if (sscanf(oid, ".1.3.6.1.4.1.53864.1.%d.0%c", &n, &ch) == 1 && n >= 0 && n <= 255)
        return n;
    else
        return -1;
}

This function will return n if the pattern has the expected form and the value is within [0 .. 255], otherwise it returns -1. Note however that it will accept potentially invalid strings such as:

  • leading spaces: .1.3.6.1.4.1.53864.1. 123.0
  • leading zeroes: .1.3.6.1.4.1.53864.1.0123.0
  • plus sign: .1.3.6.1.4.1.53864.1.+123.0
Related