tags:

views:

231

answers:

6

I have several possible occurrences to test with strstr.

if ((a = strstr(string, "FOO")) != NULL || (a = strstr(string, "BAR")) != NULL ||
    (a = strstr(string, "FOO2")) != NULL ||(a = strstr(string, "BAR2")) != NULL ||
    (a = strstr(string, "FOO3")) != NULL ||(a = strstr(string, "BAR3")) != NULL) // do something

and then based on the occurrence found I need to do

 var = strlen("THE_ONE_MATCHED_ABOVE");

What would be a good way to do this without using lots of if statements?

A: 

Are you willing to use the comma operator:

if ((lookfor = "FOO", a = strstr(string, lookfor)) != NULL ||
    (lookfor = "BAR", a = strstr(string, lookfor)) != NULL ||
    ...)
{
    var = strlen(lookfor);
}

The comma operator will allow you to evaluate multiple expression in left to right order. The value of the expression as a whole is the value of the rightmost esub-expression.

R Samuel Klatchko
Yeah, I think that does the job pretty well. Thanks
Mike
+1  A: 
int match_length( const char* string) {
/* return 0 if no match, otherwise strlen of match */

   static const char* const MATCHES[] = { "BAR2", "FOO2", "FOO3", "FOO", "BAR", "..." } ;
   // NB: MATCHES must be sorted in descending order of length (longest first).
   const char* r = 0;
   int i = 0 ;

   for( ; 
        i < sizeof(MATCHES) / sizeof(MATCHES[0]) 
        && ( r = strstr( string, MATCHES[i] ) ) == 0; 
        ++i );

   return r ? strlen( MATCHES[i] ) : 0 ;
}

Note: caf makes a very important point: "You need to arrange the MATCHES in descending length order - if strstr(x, "FOO2") is non-null, then so is strstr(x, "FOO"), so you need to find the former first." I've editted to reflect this. Depending on the use of this function, the sort might also be done at runtime.

tpdi
This method is extensible, however `null` should be `NULL`.
dreamlax
I've changed it to 0
tpdi
I think you are missing a closing parenthesis somewhere
bta
NULL is more idiomatic than 0 when talking about C. C++ is a different bag, however.
dreamlax
You need to arrange the `MATCHES` in descending length order - if `strstr(x, "FOO2")` is non-null, then so is `strstr(x, "FOO")`, so you need to find the former first.
caf
The first thing I noticed was that this code is somewhat hard to read. Using the assignment operator in the middle of the for loop is Not Right unless necessary. Just make it a standard for loop on i and in the middle of the loop put the strstr and return the length immediately if it matches.
clahey
Also, this code is super slow. You go through the entire string once for each search target, so searching for multiple strings can get really slow. It's true that premature optimization is bad, so if you only have like 3 strings or something, this is fine, but for lots of strings, you really want a state machine of some sort.
clahey
+1  A: 

If you have a lot of patterns to look for in subject string, the best choice is Aho-Corasick algorithm:

el.pescado
A: 

If you use that pattern frequently within your code, you should write a small helper function like this:

int FindFirstMatch(const char* stringItem, const char** stringList)
{
    int index = -1;
    int itemLength = strlen(stringItem);

    if (stringList == NULL)
    {
        return index;
    }

    for (; stringList[index] != NULL; index++)
    {
            if (  (strlen(stringList[index]) == itemLength)
               && (strstr(stringList[index], stringItem) != NULL))
        {
            break;
        }
    }
    return index;
}

The function takes a string and a NULL terminated string array as arguments and returns the index of the first occurence of the string in the list. You can then use the index to check fo the string length.

To do a check as you did in your example you would instead write:

const char* itemList[] = {"FOO", "FOO2", "FOO3", "BAR", "BAR2", "BAR3", NULL};
int itemLength = 0;

int index = FindFirstMatch("BAR3", itemList);
if (index != -1)
{
    itemLength = strlen(itemList[index]);
}
Frank Bollack
This will never find FOO2, because it'll find FOO first, but overall this code is much more readable than the other versions.
clahey
@clahey: Thanks, fixed that...
Frank Bollack
Sorry, but that doesn't fix it. All you've done is make it so that it only matches if the string is the same length as the match, but that doesn't help you find FOO3 in "asdf FOO3 fdsa". It was better before.
clahey
+1  A: 

I have a feeling that your example is oversimplified, but it's probably worth noting that it can be simplified. If the string "FOO" isn't found, then you know the string "FOO2" won't be found so you can eliminate all but the first two cases in your example.

bta
A: 

If you have a large varying set of strings you need to search for this way, you should probably use a regular expression library and compile a DFA to match (FOO|FOO2|FOO3|...). This is the optimal solution. You could also work out the tree structures to implement the same result yourself, especially if the strings to search for are constant and you can hard-code them efficiently.

R..