strcspn
Returns the length of the initial run of a string that contains none of the given characters.
strcspn(string $string, string $characters, int $offset = 0, ?int $length = null): int- Added in PHP 4.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | The string to examine. |
$characters | string | A string listing every disallowed character. |
$offsetoptional | int= 0 | Position in string to start scanning from. Negative values count from the end. |
$lengthoptional | ?int= null | How many characters of string to examine. Negative values stop that many characters from the end. null means to the end of the string. |
Return value
intThe length of the initial segment of string that contains none of the characters in characters.
Changelog
- PHP 8.4.0When characters is an empty string, the scan no longer stops incorrectly at the first null byte.
- PHP 8.0.0The length parameter is nullable.
strcspn walks a string from the start and stops at the first character that appears in the $characters mask, returning how many characters it got through. Think of it as "length until any of these". Its mirror image, strspn, counts the run of characters that are in the mask.
When to reach for it
It shines when you want to find whichever of several delimiters comes first. Splitting key=value, key:value or key value with one call, trimming a line at the first # or ; comment marker, or grabbing the token before any whitespace or punctuation. Doing the same with strpos means one call per delimiter plus a min() over the results.
It is also a compact validator: if strcspn($s, $forbidden) === strlen($s), then none of the forbidden characters occur anywhere in $s.
What trips people up
The second argument is a set of characters, not a substring. strcspn($s, 'ab') stops at the first a or b, not at the sequence "ab".
When a forbidden character sits at the very start you get 0, which is a valid answer rather than a failure. And when $offset is set, the returned length is counted from that offset, not from the beginning of the string.
Example
When to use it
- Split a config line at the first of several possible delimiters (=, : or space)
- Cut a line at the first comment marker such as # or ;
- Verify a filename contains no characters from a forbidden set
Frequently asked questions
What is the difference between strcspn and strspn?
strcspn counts the leading characters that are NOT in the mask, stopping at the first match; strspn counts the leading characters that ARE in the mask, stopping at the first non-match. The c stands for complement.
Does strcspn search for a substring?
No. The second argument is a set of individual characters, so strcspn($s, 'ab') stops at the first 'a' or the first 'b', whichever comes first. Use strpos to search for a literal substring.
Why does strcspn return 0?
Because the very first character examined is in the forbidden set. That is a normal result, not an error, so do not treat 0 as a failure value.
More examples
Splitting on the first of several delimiters
Each line prints host => local, port => 5432, debug => true. A single strcspn call handles all three delimiter styles, where strpos would need one call per delimiter plus a min().
<?php
foreach (['host=local', 'port: 5432', 'debug true'] as $line) {
$n = strcspn($line, '=: ');
$key = substr($line, 0, $n);
$val = ltrim(substr($line, $n), '=: ');
echo "$key => $val\n";
}Validating a string against a forbidden set
report.pdf is valid because the scan reaches the end of the string; my:file.txt is invalid because the colon stops the scan at position 2, which is shorter than the string length.
<?php
$bad = "/\\?%*:|\"<>";
foreach (['report.pdf', 'my:file.txt'] as $name) {
$ok = strcspn($name, $bad) === strlen($name);
echo $name, ': ', $ok ? 'valid' : 'invalid', "\n";
}Using offset and length
The offset moves the starting point and the returned count restarts from there, which is the detail people misread. Limiting length caps the answer at the window size even if no forbidden character appears.
<?php
$s = 'abc-def-ghi';
echo strcspn($s, '-'), "\n"; // 3
echo strcspn($s, '-', 4), "\n"; // 3 - counted from offset 4
echo strcspn($s, '-', 4, 2), "\n"; // 2 - only 'de' examined
echo strcspn($s, '-', -3), "\n"; // 3 - last three chars, no dash
Discussion