The strpbrk function provides a simpler method of calling strchr when searching for any one of several characters in a given string. It searches one string for the first occurrence of any character in another string.

char *
strpbrk(const char *s1, const char *s2);

The return value is a pointer to the found character, or NULL if no characters were found.

Suppose you wanted to split a string on any one of a set of characters, but you wanted to know whether you could perform a split before doing the work (e.g. before calling strtok). strpbrk will return the pointer to the split location assuming you wanted to split on whichever of your characters came first, then you could do you more advanced logic of breaking the string into two pieces.

char *phrase = "Goodbye, cruel world";
char *set = "abc";

if (strpbrk(phrase, set))
{
    /* Advanced split logic */
}

You may think that this sounds very similar to strcspn and that's because it is! The difference is that strcspn indicates a length between the start of the string and the first match while strpbrk returns a pointer to the first match.

Local Variables

We'll be utilizing the strcspn function to do the core work and we want to store the return value so we can index into the string being searched.

size_t idx = 0;

Error Checking

Since strcspn can indicate an error by returning 0, we wouldn't know whether a character was found in the first location or if an error occurred, so we need to validate s1 before passing it to strcspn. Normally we would want to check for a NULL s2 pointer, however strcspn will do that for us so we can skip that check.

if (!s1)
{
    return NULL;
}

Implementation

We'll leverage strcspn to find the length to the first match which allows us to validate no match if the length is equal to the length of our string being searched. In that case we return NULL, otherwise we return a pointer to the matched character which is the string pointer plus the length returned by strcspn.

idx = strcspn(s1, s2);
if ('\0' == s1[idx])
{
    return NULL;
}

return (char *)(s1 + idx);

Testing

Due to the similar functionality, the testing is nearly identical to that of strcspn. We'll test input validation and then cases where there are no matches and then matches at the beginning, middle, and end of the string being searched.

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

    do
    {
        /* NULL search string */
        if (NULL != strpbrk(NULL, "abcd"))
        {
            ret = 2;
            break;
        }

        /* NULL search set */
        if (NULL != strpbrk(str, NULL))
        {
            ret = 3;
            break;
        }

        /* Empty search string */
        if (NULL != strpbrk("", "abcd"))
        {
            ret = 4;
            break;
        }

        /* Empty search set */
        if (NULL != strpbrk(str, ""))
        {
            ret = 5;
            break;
        }

        /* Characters not in search string */
        if (NULL != strpbrk(str, "hijk"))
        {
            ret = 6;
            break;
        }

        /* First character matches */
        if (str != strpbrk(str, "hijAklm"))
        {
            ret = 7;
            break;
        }

        /* Last character matches */
        if ((str + strlen(str) - 1) != strpbrk(str, "hijgklm"))
        {
            ret = 8;
            break;
        }

        /* Match in middle */
        if (strchr(str, 'a') != strpbrk(str, "hijaklm"))
        {
            ret = 9;
            break;
        }

        ret = 0;
    } while (0);

    return ret;
}

Conclusion

The name wasn't immediately obvious to me but strpbrk stands for "string pointer break" which likely comes from the SNOBOL language which had a BREAK() function to perform the same task (and it had a SPAN() function that mirrors strcspn). Due to the commanality with strcspn, you could swap which function does the main work and then implement strcpsn by calling this function instead.

char *
strpbrk(const char *s1, const char *s2)
{
    size_t idx = 0;

    if (!s1)
    {
        return NULL;
    }

    idx = strcspn(s1, s2);
    if ('\0' == s1[idx])
    {
        return NULL;
    }

    return (char *)(s1 + idx);
}
comments powered by Disqus