The strspn function is the opposite of strcspn in that it computes the number of characters at the start of a string that are in the set of characters you specify.

size_t
strspn(const char *s1, const char *s2);

The return value is that count of characters.

While strcspn is nice for finding the first occurence of any of a given set of characters, strspn's use is more for finding the occurence of the first character not in a given set. This description may seem backwards, but an example will help.

char *item_nums = { "AA-BC-1234", "AA-321" };
char *set = "ABC-";
size_t loc = strspn(item_nums[0], set);
printf("Serial Number 1: %s\n", loc);
size_t loc = strspn(item_nums[1], set);
printf("Serial Number 2: %s\n", loc);

In this scenario there is an item numbering scheme which consists of the letters A, B, and C, hyphens, and a serial number. The letters will always preface the numbers but the number of letters can change. Using strspn allows you to easily index to the number.

As a bonus, how else might you solve this problem (hint: strrchr)?

Local Variables

There isn't much to keep track of since we're only calculating a length, so we'll create a variable of type size_t to hold that information which is also the return value.

size_t len = 0;

Error Checking

The only error conditions we need to explicitly check for are NULL pointers, in both cases the return value will be 0 so they can be handled together.

if (!s1 || !s2)
{
    return 0;
}

You might consider empty strings to be another error condition, but the loop logic that follows will handle both situations automatically.

Implementation

The main loop is nearly identical to strcspn except we want to find a character with strchr so we directly use its return value as a loop condition. The idea is to march along the string being searched and check whether each character is in the set of characters to find. When we find a character that isn't in the set then we end our search and return the number of characters that were found.

while (*s1 &&
       strchr(s2, *s1++))
{
    len++;
}

return len;

If s1 were empty then the loop body would never execute and 0 would be returned. If s2 were empty then strchr would return NULL and the loop body wouldn't execute either.

Testing

These tests closely mimic those for strcspn with slight modifications to error condition checking.

int
strspnTest(void)
{
    int ret     = 1;
    char str[]  = "ABCDEFG";
    char str2[] = "ABCD";

    do
    {
        /* NULL source */
        if (0 != strspn(NULL, "ABC"))
        {
            break;
        }

        /* NULL span set */
        if (0 != strspn(str, NULL))
        {
            break;
        }

        /* Empty search string */
        if (0 != strspn("", "ABCD"))
        {
            break;
        }

        /* Empty search set */
        if (0 != strspn(str, ""))
        {
            break;
        }

        /* Missing characters only */
        if (0 != strspn(str, "hijkl"))
        {
            break;
        }

        /* Short set of existing characters only */
        if (strlen(str2) != strspn(str, str2))
        {
            break;
        }

        /* Existing characters only */
        if (strlen(str) != strspn(str, str))
        {
            break;
        }

        /* Missing character at beginning of search set */
        if (strlen(str) != strspn(str, "aABCDEFG"))
        {
            break;
        }

        /* Missing character in middle of search set */
        if (strlen(str) != strspn(str, "ABCDdEFG"))
        {
            break;
        }

        /* Missing character at end of search set */
        if (strlen(str) != strspn(str, "ABCDEFGg"))
        {
            break;
        }

        ret = 0;
    } while (0);

    return ret;
}

Conclusion

As seen with the serial number example, strspn can be used to tell you two pieces of information about a string: how long the initial set of matching characters is and at what index non-matching characters start.

size_t
strspn(const char *s1, const char *s2)
{
    size_t len = 0;

    if (!s1 || !s2)
    {
        return 0;
    }

    while (*s1 &&
           strchr(s2, *s1++))
    {
        len++;
    }

    return len;
}
comments powered by Disqus