The strcspn function is like an advanced version of strchr - it determines the number of characters at the start of a string are not in the set of characters you specify.

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

The return value is that count of characters.

This function is somewhat confusing so it will help to show an example. Using strcspn allows you to answer a question like "Where does the first instance of a, b, or c occur in the string 'Goodbye, cruel world'?". C code which answers this question would look like so:

char *phrase = "Goodbye, cruel world";
char *set = "abc";
size_t loc = strcspn(phrase, set);
printf("The first instance is at index %u", len);

At this point, loc will either be equal to the length of the phrase, implying that a, b, or c does not exist in the string, or it will hold the index of the first occurrence of either of those characters. We can see that there is a 'b' in "Goodbye", so the message printed to the screen will be "The first instance is at index 4".

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

There are two error cases we need to account for and each requires a different return value. The first case is when the string being searched, s1, is NULL. If this is the case then the string has no length and the return value would be 0.

if (!s1)
{
    return 0;
}

The second case is when no search characters are provided, i.e. s2 is NULL. Since we don't have anything to search for then the return value is equal to the length of the string being searched.

if (!s2)
{
    return strlen(s1);
}

The order of these checks is important as well. The first case implies that we can't do any searching while the seconds implies that we don't need to do any searching.

Implementation

The method we'll use is to march along the first string and look for each subsequent character in the second string. We'll keep marching until we reach the end of the string being searched or we find a match. The hardest part here is finding matches but we can farm that work out to strchr since that's exactly what it's for! As long as we continue marching then we also continue updating the count being returned.

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

return len;

Testing

First, we'll test our parameter validation by passing a NULL pointer for each of the strings in sequence and similarly pass empty strings for each. Next, we'll search for characters that don't exist at all to ensure that we get the length of the search string back. Finally, we'll place the first-matched character at various positions in the string of characters being matched against.

int
strcspnTest(void)
{
    int ret = 1;
    char str[] = "ABCDEFGabcdefg";

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

        /* NULL span complement */
        if (strlen(str) != strcspn(str, NULL))
        {
            break;
        }

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

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

        /* Characters aren't in string */
        if (strlen(str) != strcspn(str, "hijkl"))
        {
            break;
        }

        /* First character is in the search set */
        if (0 != strcspn(str, "A"))
        {
            break;
        }

        /* Character at beginning of search set */
        if (3 != strcspn(str, "Dabcd"))
        {
            break;
        }

        /* Character in middle of search set */
        if (3 != strcspn(str, "abDcd"))
        {
            break;
        }

        /* Character at end of search set */
        if (3 != strcspn(str, "abcdD"))
        {
            break;
        }

        ret = 0;
    } while (0);

    return ret;
}

Conclusion

Although the description is somewhat confusing, strcspn is not difficult to implement. The name comes from "string complement span" because it computes the span of characters in a string that are in the set complementary to the one provided.

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

    if (!s1)
    {
        return 0;
    }

    if (!s2)
    {
        return strlen(s1);
    }

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

    return len;
}
comments powered by Disqus