Signature
mb_ereg_search_pos(?string $pattern = null, ?string $options = null): array|falsePurpose
mb_ereg_search_pos() runs a multibyte-aware regular expression against a string that you registered earlier with mb_ereg_search_init(). It does not return the matched text. It returns where the next match is: a byte offset and a byte length. Each successful call moves an internal cursor past the match, so repeated calls walk through the subject string one match at a time.
The function belongs to the mb_ereg_* family, which uses the Oniguruma engine rather than PCRE. Patterns are written without delimiters.
Signature
Parameters and defaults
$pattern: the regular expression, without delimiters. If you passnull(the default), the pattern given tomb_ereg_search_init()is reused.$options: option letters that control matching, such as case-insensitivity. They are the same letters accepted bymb_regex_set_options(). Withnull, the current options stay in effect.
The subject string is not a parameter. It comes from the earlier mb_ereg_search_init() call, and if you do not initialise a new one, the previous string is used.
The character encoding is the one set with mb_regex_encoding(), or the internal encoding if you have not set one. Setting it to UTF-8 explicitly makes your code easier to reason about.
Return value and failure
On success you get a two-element array: [offset, length]. Both numbers are measured in bytes, not characters. The offset is relative to the start of the whole subject string.
On failure the function returns false. In practice this is what you see when there are no more matches, and it is also what you get when an error occurs. Always compare with !== false, because [0, 3] is a valid, non-empty array but code that tests loosely can still go wrong in other places.
Example 1: a first match in UTF-8 text
The string below contains ë and é, which take two bytes each in UTF-8. The word café therefore starts at byte 11, not at character 10, and is five bytes long.
mb_regex_encoding('UTF-8');
mb_ereg_search_init('Zoë loves café');
$pos = mb_ereg_search_pos('café');
echo json_encode($pos, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[11,5]
Example 2: walking through all matches
Because the cursor advances after each hit, a loop can collect every match. Passing no arguments reuses the pattern from mb_ereg_search_init(). The loop ends when the function returns false.
mb_regex_encoding('UTF-8');
mb_ereg_search_init('a1 bb22 ccc333', '\\d+');
$found = [];
while (($m = mb_ereg_search_pos()) !== false) {
$found[] = $m;
}
echo json_encode($found, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";[[1,1],[5,2],[11,3]]
Example 3: converting bytes to characters, and a miss
If you need a character position, count the characters in the part of the string before the byte offset. A pattern that does not occur produces false.
mb_regex_encoding('UTF-8');
$text = 'été à Paris';
mb_ereg_search_init($text);
$pos = mb_ereg_search_pos('Paris');
$chars = mb_strlen(substr($text, 0, $pos[0]), 'UTF-8');
mb_ereg_search_init('abc');
$missing = mb_ereg_search_pos('x');
echo json_encode(['charOffset' => $chars, 'missing' => $missing], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";{"charOffset":6,"missing":false}
Edge cases
- Cursor state. The position is global state for the mbstring regex search functions. Calling
mb_ereg_search_init()again resets it, so initialise before each independent scan. - Empty matches. A pattern that can match an empty string, such as
x*, may return a length of0. Guard against loops that never make progress. - No match is also
false. The return value does not distinguish a clean end of matches from other failures, so do not rely on it to detect an invalid pattern. - Options. Passing
$optionschanges the matching behaviour for that call. Checkmb_regex_set_options()for the letters it accepts.
Common mistakes
- Treating the numbers as characters. The offset and length are in bytes. With accented letters or CJK text they differ from
mb_strlen()counts. Usemb_strcut()orsubstr()with these values, notmb_substr(). - Forgetting to call
mb_ereg_search_init(). Without it there is no subject string, or you accidentally search a stale one from earlier code. - Adding delimiters. Write
'\\d+', not'/\\d+/'. The slashes would be treated as literal characters. - Using a loose check in a loop. Use
!== falseto end the loop. - Expecting the matched text. If you want the text itself, use
mb_ereg_search_regs()ormb_ereg_search(), or cut the string using the returned pair.
Related functions
mb_ereg_search_init()sets the subject string and optionally the pattern.mb_ereg_search()only tells you whether a match exists.mb_ereg_search_regs()returns the matched text and groups.mb_ereg_search_getpos()andmb_ereg_search_setpos()read and change the cursor.mb_regex_encoding()andmb_regex_set_options()control encoding and options.
Recap
mb_ereg_search_pos() finds the next match of a multibyte regex in the string prepared by mb_ereg_search_init() and returns [byteOffset, byteLength], or false when nothing more matches. Remember that both numbers count bytes, that the cursor advances after each success, and that you should reset it by re-initialising before a fresh scan.
Tested examples
mb_regex_encoding('UTF-8');
mb_ereg_search_init('Zoë loves café');
$pos = mb_ereg_search_pos('café');
echo json_encode($pos, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
[11,5]
mb_regex_encoding('UTF-8');
mb_ereg_search_init('a1 bb22 ccc333', '\\d+');
$found = [];
while (($m = mb_ereg_search_pos()) !== false) {
$found[] = $m;
}
echo json_encode($found, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
[[1,1],[5,2],[11,3]]
mb_regex_encoding('UTF-8');
$text = 'été à Paris';
mb_ereg_search_init($text);
$pos = mb_ereg_search_pos('Paris');
$chars = mb_strlen(substr($text, 0, $pos[0]), 'UTF-8');
mb_ereg_search_init('abc');
$missing = mb_ereg_search_pos('x');
echo json_encode(['charOffset' => $chars, 'missing' => $missing], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
{"charOffset":6,"missing":false}