Signature
strcspn(
string $string,
string $characters,
int $offset = 0,
?int $length = null
): intPurpose
strcspn() counts how many bytes at the start of a string pass before the first byte that appears in a set of forbidden characters. The name stands for "complement span": it spans the characters that are not in the mask. It is a quick way to find where a token ends without a regular expression and without building a temporary array.
The function never returns the matching character or its position as a separate value. It returns a number, and that number is both the length of the safe prefix and the offset of the first forbidden byte (or the length of the examined text when no forbidden byte exists).
Signature
Parameters and defaults
$string: the text to examine.$characters: a string whose individual bytes are all treated as stop characters. Order does not matter, and repeated bytes make no difference.$offset: where to start. The default is0. A negative value counts from the end of the string.$length: how much of the string to examine. The defaultnullmeans everything from the offset to the end. A negative value stops that many characters before the end of the string.
When you pass $offset or $length, the result is the same as calling strcspn(substr($string, $offset, $length), $characters). This also means the returned count starts at the offset, not at the beginning of the original string.
Return value and failure cases
The function returns an int that is never negative. It does not return false when nothing matches: if no forbidden byte is found, you get the length of the examined segment. If the very first byte is forbidden, you get 0.
Passing a value of an unsuitable type, such as an array for $string, raises a TypeError. The function is binary-safe, so null bytes in the string are treated like any other byte.
Example 1: basic counts
The first call stops at the first a, the second stops immediately because b, a, and so on are all forbidden, and the third never meets a z, so the whole string length is returned.
$results = [
strcspn('banana', 'a'),
strcspn('banana', 'abcd'),
strcspn('banana', 'z'),
];
echo json_encode($results), "\n";[1,0,6]
Example 2: offset and length
In abcdhelloabcd, an offset of -9 starts at helloabcd. The first a there is five bytes in. Adding a length of -5 trims the examined part to hell, which contains no a, so the full segment length of 4 is returned.
$text = 'abcdhelloabcd';
echo json_encode([
'from_negative_offset' => strcspn($text, 'a', -9),
'with_negative_length' => strcspn($text, 'a', -9, -5),
'positive_offset' => strcspn($text, 'o', 2),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"from_negative_offset":5,"with_negative_length":4,"positive_offset":6}
Example 3: taking the first token
Pairing strcspn() with substr() is a compact way to cut text at the first delimiter. If the delimiter is missing, the whole string is returned. If the text starts with a delimiter, the result is an empty string.
function firstToken(string $input): string
{
$length = strcspn($input, " \t,;");
return substr($input, 0, $length);
}
echo json_encode([
firstToken('alpha,beta'),
firstToken("tab\tseparated"),
firstToken('single'),
firstToken(',leading'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";["alpha","tab","single",""]
Edge cases
- Empty subject: an empty string yields
0. - Empty mask: nothing is forbidden, so the whole examined segment is counted. Before PHP 8.4.0, an empty
$charactersmade the search incorrectly stop at the first null byte in the string; PHP 8.4 fixed this. - Offset past the end: the examined segment is empty, so the result is
0, as withsubstr(). - Nullable length: since PHP 8.0.0,
$lengthmay benullexplicitly. - Bytes, not characters: a multibyte UTF-8 character occupies several bytes, so counts and offsets are in bytes. Use multibyte functions when you need character counts.
Common mistakes
- Treating the mask as a substring.
strcspn('hello', 'll')stops at the firstlbyte; it does not look for the sequencell. - Expecting ranges. A mask such as
'a-z'contains just the bytesa,-andz. Write out every character you want to forbid. - Reading the result as an absolute position when an offset is used. The count begins at the offset, so add the offset back if you need a position in the original string.
- Checking for
false. The function always returns an integer; compare against the length of the examined text to detect "no forbidden byte found". - Confusing it with
strspn().strspn()counts bytes that are in the mask, whilestrcspn()counts bytes that are not.
Related functions
strspn()measures the initial segment made only of allowed characters.substr()defines how$offsetand$lengthbehave and extracts the segment.strpos()andstrpbrk()are alternatives when you want a position or the remainder of the string instead of a count.
Recap
strcspn() returns the number of leading bytes in a string that avoid every byte in a mask. Offset and length behave like substr(), the result is relative to the offset, and the function is binary-safe. Use it for simple delimiter scanning, and remember that the mask is a set of single bytes rather than a pattern.
Tested examples
$results = [
strcspn('banana', 'a'),
strcspn('banana', 'abcd'),
strcspn('banana', 'z'),
];
echo json_encode($results), "\n";
[1,0,6]
$text = 'abcdhelloabcd';
echo json_encode([
'from_negative_offset' => strcspn($text, 'a', -9),
'with_negative_length' => strcspn($text, 'a', -9, -5),
'positive_offset' => strcspn($text, 'o', 2),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"from_negative_offset":5,"with_negative_length":4,"positive_offset":6}
function firstToken(string $input): string
{
$length = strcspn($input, " \t,;");
return substr($input, 0, $length);
}
echo json_encode([
firstToken('alpha,beta'),
firstToken("tab\tseparated"),
firstToken('single'),
firstToken(',leading'),
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
["alpha","tab","single",""]