Multibyte strings7 min readExamples run on PHP 8.5.10

mb_ereg_search_regs()

Learn how mb_ereg_search_regs() returns the matched text and captured groups from a stateful multibyte regex search, with examples and pitfalls.

Signature

mb_ereg_search_regs(?string $pattern = null, ?string $options = null): array|false
Available since PHP 4.2.07.48.08.18.28.38.48.5All versions →

Purpose

mb_ereg_search_regs() runs a multibyte-aware regular expression against a string that you registered earlier and returns the text it found. Unlike mb_ereg(), which takes the subject on every call, this function belongs to a stateful search family. You prepare a string and a pattern once with mb_ereg_search_init(), then call the search functions repeatedly. Each successful call moves an internal position forward, so the next call continues after the previous match.

The return value is an array: element 0 is the whole match, element 1 is the first parenthesised group, element 2 is the second group, and so on. That makes it convenient for tokenising or scanning text that contains characters outside ASCII.

Signature

Parameters and defaults

  • $pattern is the search pattern, written without delimiters. It defaults to null, which means the pattern stored by the last mb_ereg_search_init() call is reused.
  • $options is a string of option letters, explained under mb_regex_set_options(). It defaults to null, which keeps the current options. For instance, 'i' requests case-insensitive matching.

Since PHP 8.0.0 both parameters are explicitly nullable.

The character encoding is the internal encoding, or whatever mb_regex_encoding() has set. Make sure it matches your string, usually UTF-8.

Return value and failure

On success you get a list with the matched substring first, followed by one entry per capture group. On error the function returns false. In practice you will also see false when the remaining part of the string contains no further match, which is what ends a scanning loop. A search must be initialised with a subject string before this function can work.

Example 1: first match and position

The first call finds the first number-and-word pair. Afterwards mb_ereg_search_getpos() reports where the next search will start. That position is a byte offset, not a character count.

Example
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('Order: 42 apples, 7 pears', '(\d+) (\w+)');
$m = mb_ereg_search_regs();
echo json_encode($m, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo mb_ereg_search_getpos(), "\n";
Output
["42 apples","42","apples"]
16

Example 2: scanning every match

Because each call continues where the last one stopped, a while loop visits all matches. The loop ends when the function returns false. The text below uses Japanese characters, so the capture groups hold multibyte strings.

Example
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('東京=13; 大阪=27; 京都=26', '([^=; ]+)=(\d+)');
$pairs = [];
while ($m = mb_ereg_search_regs()) {
    $pairs[$m[1]] = (int) $m[2];
}
echo json_encode($pairs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"東京":13,"大阪":27,"京都":26}

Example 3: pattern and options in the call

You may supply the pattern and options directly. Here the subject is registered without a pattern, and the call passes 'i' for case-insensitive matching. The optional middle group does not take part in the match, so its slot holds false.

Example
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('Cat');
$m = mb_ereg_search_regs('(c)(x)?(a)', 'i');
echo json_encode($m, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["Ca","C",false,"a"]

Edge cases

  • Non-participating groups. A group that did not match appears as false, not as an empty string. Compare with === before using the value.
  • Byte positions. The internal position is measured in bytes. Do not mix it with mb_substr() character offsets without converting.
  • Shared state. The string, pattern and position live in global per-request state. Starting another mb_ereg_search_init() replaces them, so two interleaved scans will disturb each other.
  • Empty matches. A pattern that can match an empty string is a poor fit for a scanning loop. Write patterns that consume at least one character.
  • Named groups. Oniguruma supports named groups. Be ready for extra keys in the result when you use them, and test the exact shape.

Common mistakes

  1. Forgetting to initialise. Calling the function before mb_ereg_search_init() leaves it with nothing to search.
  2. Using delimiters. Write '(\d+)', not '/(\d+)/'. The mb_ereg family does not use PCRE-style delimiters.
  3. Treating false only as an error. In a loop, false is the normal stop signal.
  4. Encoding mismatch. If the regex encoding differs from the string encoding, matches may be wrong or missing. Call mb_regex_encoding() explicitly.
  5. Expecting preg_match() semantics. There are no flags such as PREG_OFFSET_CAPTURE, and the subject is not passed on each call.
  • mb_ereg_search_init() sets the subject string, pattern and options.
  • mb_ereg_search() only reports whether a match exists.
  • mb_ereg_search_pos() returns the byte position and length of the match.
  • mb_ereg_search_getregs() retrieves the groups of the last match.
  • mb_ereg_search_getpos() and mb_ereg_search_setpos() read and move the position.
  • mb_regex_encoding() and mb_regex_set_options() control encoding and options.

Recap

Initialise the search with mb_ereg_search_init(), then call mb_ereg_search_regs() to receive the whole match plus capture groups as an array. Each success advances the internal byte position, false signals an error or no further match, and unmatched groups appear as false. Keep the regex encoding aligned with your data and avoid interleaving separate searches.

Tested examples

First match and search position
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('Order: 42 apples, 7 pears', '(\d+) (\w+)');
$m = mb_ereg_search_regs();
echo json_encode($m, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
echo mb_ereg_search_getpos(), "\n";
Output
["42 apples","42","apples"]
16
Scanning every match in multibyte text
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('東京=13; 大阪=27; 京都=26', '([^=; ]+)=(\d+)');
$pairs = [];
while ($m = mb_ereg_search_regs()) {
    $pairs[$m[1]] = (int) $m[2];
}
echo json_encode($pairs, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"東京":13,"大阪":27,"京都":26}
Pattern and options passed in the call
<?php
mb_regex_encoding('UTF-8');
mb_ereg_search_init('Cat');
$m = mb_ereg_search_regs('(c)(x)?(a)', 'i');
echo json_encode($m, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
["Ca","C",false,"a"]