preg_replace_callback

(PHP 4 >= 4.0.5, PHP 5, PHP 7, PHP 8)

preg_replace_callback使用回调执行正则表达式搜索和替换

描述

preg_replace_callback(
    string|array $pattern,
    callable $callback,
    string|array $subject,
    int $limit = -1,
    int &$count = null,
    int $flags = 0
): string|array|null

此函数的行为与 preg_replace() 几乎相同,区别在于它不使用 replacement 参数,而是指定 callback

参数

pattern

要搜索的模式。它可以是字符串或包含字符串的数组。

callback

将被调用并传递 subject 字符串中匹配元素的数组的回调函数。回调函数应返回替换字符串。这是回调函数签名

handler(array $matches): string

您通常只需要在一个地方为 preg_replace_callback() 使用 callback 函数。在这种情况下,您可以在 preg_replace_callback() 的调用中使用 匿名函数 来声明回调函数。通过这种方式,您可以将所有调用信息集中在一个地方,并且不会用在其他地方未使用的回调函数名称来污染函数命名空间。

示例 #1 preg_replace_callback() 和匿名函数

<?php
/* 一个类 Unix 的命令行过滤器,用于将段落开头的大写字母转换为小写字母 */
$fp = fopen("php://stdin", "r") or die("can't read stdin");
while (!
feof($fp)) {
$line = fgets($fp);
$line = preg_replace_callback(
'|<p>\s*\w|',
function (
$matches) {
return
strtolower($matches[0]);
},
$line
);
echo
$line;
}
fclose($fp);
?>

subject

要搜索和替换的字符串或包含字符串的数组。

limit

每个模式在每个 subject 字符串中执行的最大替换次数。默认为 -1(无限制)。

count

如果指定,此变量将填充执行的替换次数。

flags

flags 可以是 PREG_OFFSET_CAPTUREPREG_UNMATCHED_AS_NULL 标志的组合,这些标志会影响匹配数组的格式。有关详细信息,请参阅 preg_match() 中的说明。

返回值

preg_replace_callback() 如果 subject 参数是数组,则返回数组,否则返回字符串。发生错误时,返回值为 null

如果找到匹配项,则返回新的主题,否则返回 subject,保持不变。

错误/异常

如果传递的正则表达式模式无法编译为有效的正则表达式,则会发出 E_WARNING 错误。

变更日志

版本 描述
7.4.0 添加了 flags 参数。

示例

示例 #2 preg_replace_callback() 示例

<?php
// 此文本用于 2002 年
// 我们想将其更新到 2003 年
$text = "April fools day is 04/01/2002\n";
$text.= "Last christmas was 12/24/2001\n";
// 回调函数
function next_year($matches)
{
// 惯例:$matches[0] 是完整匹配
// $matches[1] 是第一个子模式的匹配
// 括在 '(...)' 中,依此类推
return $matches[1].($matches[2]+1);
}
echo
preg_replace_callback(
"|(\d{2}/\d{2}/)(\d{4})|",
"next_year",
$text);

?>

上面的示例将输出

April fools day is 04/01/2003
Last christmas was 12/24/2002

示例 #3 preg_replace_callback() 使用递归结构来处理封装的 BB 代码

<?php
$input
= "plain [indent] deep [indent] deeper [/indent] deep [/indent] plain";

function
parseTagsRecursive($input)
{

$regex = '#\[indent]((?:[^[]|\[(?!/?indent])|(?R))+)\[/indent]#';

if (
is_array($input)) {
$input = '<div style="margin-left: 10px">'.$input[1].'</div>';
}

return
preg_replace_callback($regex, 'parseTagsRecursive', $input);
}

$output = parseTagsRecursive($input);

echo
$output;
?>

参见

添加备注

用户贡献的备注 22 个备注

Richard
11 年前
使用 "use" 关键字是将多个参数传递给回调函数的最简单方法。

[这比使用全局变量更好,因为它即使在函数内部也能正常工作。]

在这个例子中,回调函数是一个匿名函数,它接收一个参数 $match,该参数由 preg_replace_callback() 提供。额外的
"use ($ten)" 将 $ten 变量引入函数作用域。

<?php
$string
= "Some numbers: one: 1; two: 2; three: 3 end";
$ten = 10;
$newstring = preg_replace_callback(
'/(\\d+)/',
function(
$match) use ($ten) { return (($match[0] + $ten)); },
$string
);
echo
$newstring;
#输出 "Some numbers: one: 11; two: 12; three: 13 end";
?>
Sjon at hortensius dot net
17 年前
当 pcre.backtrack_limit 达到时,preg_replace_callback 返回 NULL;这种情况有时比你预期的更快。也不会引发任何错误;所以不要忘记自己检查 NULL。
Yuri
11 年前
如果你想在你的类内部调用非静态函数,你可以这样做。

对于 PHP 5.2,使用第二个参数,例如 array($this, 'replace')
<?php
class test_preg_callback{

private function
process($text){
$reg = "/\{([0-9a-zA-Z\- ]+)\:([0-9a-zA-Z\- ]+):?\}/";
return
preg_replace_callback($reg, array($this, 'replace'), $text);
}

private function
replace($matches){
if (
method_exists($this, $matches[1])){
return @
$this->$matches[1]($matches[2]);
}
}
}
?>

对于 PHP 5.3,使用第二个参数,例如 "self::replace"
<?php
class test_preg_callback{

private function
process($text){
$reg = "/\{([0-9a-zA-Z\- ]+)\:([0-9a-zA-Z\- ]+):?\}/";
return
preg_replace_callback($reg, "self::replace", $text);
}

private function
replace($matches){
if (
method_exists($this, $matches[1])){
return @
$this->$matches[1]($matches[2]);
}
}
}
?>
carlos dot ballesteros at softonic dot com
15 年前
一个简单的函数,用于替换字符串中的完整单词或术语列表(适用于 PHP 5.3 及更高版本,因为有闭包)。

<?php
function replace_words($list, $line, $callback) {
return
preg_replace_callback(
'/(^|[^\\w\\-])(' . implode('|', array_map('preg_quote', $list)) . ')($|[^\\w\\-])/mi',
function(
$v) use ($callback) { return $v[1] . $callback($v[2]) . $v[3]; },
$line
);
}
?>

用法示例
<?php
$list
= array('php', 'apache web server');
$str = "php and the apache web server work fine together. php-gtk, for example, won't match. apache web servers shouldn't too.";

echo
replace_words($list, $str, function($v) {
return
"<strong>{$v}</strong>";
});
?>
Drake
14 年前
PhpHex2Str 类的良好版本
<?php
class PhpHex2Str
{
private
$strings;

private static function
x_hex2str($hex) {
$hex = substr($hex[0], 1);
$str = '';
for(
$i=0;$i < strlen($hex);$i+=2) {
$str.=chr(hexdec(substr($hex,$i,2)));
}
return
$str;
}

public function
decode($strings = null) {
$this->strings = (string) $strings;
return
preg_replace_callback('#\%[a-zA-Z0-9]{2}#', 'PhpHex2Str::x_hex2str', $this->strings);
}
}

// 例如
$obj = new PhpHex2Str;

$strings = $obj->decode($strings);
var_dump($strings);
?>
matt at mattsoft dot net
18 年前
使用 preg_replace_callback 函数比使用带有 e 修饰符的 preg_replace 函数在性能和最佳实践方面要好得多。

function a($text){return($text);}

// 运行 50000 次需要 2.76 秒
preg_replace("/\{(.*?)\}/e","a('\\1','\\2','\\3',\$b)",$a);

// 运行 50000 次需要 0.97 秒
preg_replace_callback("/\{(.*?)\}/s","a",$a);
Fredow
9 年前
<?php
// 将字符串转换为大写,同时保留 HTML 实体的小巧函数。
public static function strtoupper_entities($str) {

$patternMajEntities = '/(\&([A-Z])(ACUTE|CEDIL|CARON|CIRC|GRAVE|ORN|RING|SLASH|TH|TILDE|UML)\;)+/';
$str = preg_replace_callback ($patternMajEntities,
function (
$matches) {
return
"&" . $matches[2] . strtolower($matches[3]) . ";";
},
strtoupper($str));

return
$str;
}
T-Soloveychik at ya dot ru
11 年前
文本行编号
<?PHP
// 多行文本:
$Text = "
Some
Multieline
text
for
numeration"
;

// 用于计数:
$GLOBALS["LineNUMBER"] = 1;

// 将行首替换为数字:
PRINT preg_replace_callback("/^/m",function ()
{
return
$GLOBALS["LineNUMBER"]++." ";
},
$Text);

?>

1
2 Some
3 Multieline
4 text
5 for
6 numeration
development at HashNotAdam dot com
12 年前
从 PHP 5.3 开始,你可以使用匿名函数将局部变量传递到回调函数中。

<?php

public function replace_variables( $subject, $otherVars ) {
$linkPatterns = array(
'/(<a .*)href=(")([^"]*)"([^>]*)>/U',
"/(<a .*)href=(')([^']*)'([^>]*)>/U"
);

$callback = function( $matches ) use ( $otherVars ) {
$this->replace_callback($matches, $otherVars);
};

return
preg_replace_callback($this->patterns, $callback, $subject);
}

public function
replace_callback($matches, $otherVars) {
return
$matches[1] . $otherVars['myVar'];
}
?>
kkatpki
11 年前
请注意,从 PHP 5.3 开始,命名子模式现在也包含在匹配数组中,使用它们的命名键和数值键。

为了构建 Chris 之前的示例,从 PHP 5.3 开始,你可以这样做

<?php

preg_replace_callback
('/(?<char>[a-z])/', 'callback', 'word');

function
callback($matches) {
var_dump($matches);
}

?>

并期望在你的函数中获得 $matches['char']。* 但是只有在 PHP 5.3 中 *

如果你打算支持 PHP 5.2,请注意这一点。
Florian Arndt
12 年前
这个小程序允许 PHP 用户读取包含 include 语句的 JSON 文件。例如,include {{{ "relative/to/including.json" }}} 被替换为位于 "relative/to/including.json" 的 json 文件的内容。

<?php
/**
* 处理包含 include 的 JSON 文件
* 目的:通过包含 "includes" 来处理更大的 JSON 文件
*
* @author Florian Arndt
*/
class JWI {
/**
* 解析 JSON 文件并返回其内容
* @param String $filename
*/
static function read($filename) {
if(!
file_exists($filename))
throw new
Exception('<b>JWI Error: JSON file <tt>'.$filename.'</tt> not found!</b>');
$content = join('', file($filename));
$dir = dirname($filename);
/**
* 替换
* include 语句
* 为
* 要包含的文件的内容
* 递归
*/
$content = preg_replace_callback(
'/{{{\s*"\s*(.+)\s*"\s*}}}/', // >include file< - 模式
create_function(
'$matches', // 回调参数
sprintf(
'$fn = "%s/".$matches[1];'.
'return JWI::read($fn);',
realpath(dirname($filename))
)
),
$content
);
return
$content;
}
}
chris at ocproducts dot com
14 年前
pcre.backtrack_limit 选项(在 PHP 5.2 中添加)可能会触发 NULL 返回,并且没有错误。默认的 pcre.backtrack_limit 值为 100000。如果你的匹配超过了这个限制的一半左右,它就会触发 NULL 响应。
例如,我的限制是 100000,但 500500 触发了 NULL 响应。我没有运行 unicode,但我 *猜测* PCRE 以 utf-16 运行。
Anonymous
14 年前
我创建了这个来获取锚标签的链接和名称。我在将 HTML 邮件清理为文本时使用它。不建议使用正则表达式处理 HTML,但就这个目的而言,我认为没有问题。这不是为嵌套锚设计的。

需要注意的一点
我主要关心的是有效的 HTML,因此如果属性不使用 ' 或 " 来包含值,则需要调整它。
如果你可以编辑它使其工作得更好,请告诉我。
<?php
/**
* 用文本替换锚标签
* - 将搜索字符串并用文本替换所有锚标签(不区分大小写)
*
* 工作原理:
* - 搜索字符串以查找锚标签,检查以确保它符合标准
* 锚点搜索标准:
* - 1 - <a (必须具有锚标签的开头)
* - 2 - 在 href 属性之前和之后可以有任意数量的空格或其他属性
* - 3 - 必须关闭锚标签
*
* - 一旦检查通过,它将用字符串替换来替换锚标签
* - 字符串替换可以自定义
*
* 已知问题:
* - 这不适用于不使用 ' 或 " 来包含属性的锚点。
* (例如:<a href=http: //php.net>PHP.net</a> 将不会被替换)
*/
function replaceAnchorsWithText($data) {
/**
* 必须修改 $regex 以便它可以发布到网站... 所以我将它分成 6 部分。
*/
$regex = '/(<a\s*'; // 锚标签的开头
$regex .= '(.*?)\s*'; // 可能存在也可能不存在的任何属性或空格
$regex .= 'href=[\'"]+?\s*(?P<link>\S+)\s*[\'"]+?'; // 获取链接
$regex .= '\s*(.*?)\s*>\s*'; // 关闭标签之前可能存在也可能不存在的任何属性或空格
$regex .= '(?P<name>\S+)'; // 获取名称
$regex .= '\s*<\/a>)/i'; // 关闭锚标签之间的任意数量的空格(不区分大小写)

if (is_array($data)) {
// 这是将替换链接的内容(根据你的喜好修改)
$data = "{$data['name']}({$data['link']})";
}
return
preg_replace_callback($regex, 'replaceAnchorsWithText', $data);
}

$input = 'Test 1: <a href="http: //php.ac.cn1">PHP.NET1</a>.<br />';
$input .= 'Test 2: <A name="test" HREF=\'HTTP: //PHP.NET2\' target="_blank">PHP.NET2</A>.<BR />';
$input .= 'Test 3: <a hRef=http: //php.ac.cn3>php.net3</a><br />';
$input .= 'This last line had nothing to do with any of this';

echo
replaceAnchorsWithText($input).'<hr/>';
?>
将会输出
Test 1: PHP.NET1(http: //php.ac.cn1).
Test 2: PHP.NET2(HTTP: //PHP.NET2).
Test 3: php.net3 (仍然是锚点)
This last line had nothing to do with any of this
Evgeny
1 年前
请注意!如果你定义了命名空间,
使用格式必须更改

echo preg_replace_callback(
"|(\d{2}/\d{2}/)(\d{4})|",
__NAMESPACE__ . '\\next_year',
$text);
steven at nevvix dot com
6 年前
<?php
$format
= <<<SQL
CREATE DATABASE IF NOT EXISTS :database;
GRANT ALL PRIVILEGES ON :database_name.* TO ':user'@':host';
SET PASSWORD = PASSWORD(':pass');
SQL;
$args = ["database"=>"people", "user"=>"staff", "pass"=>"pass123", "host"=>"localhost"];

preg_replace_callback("/:(\w+)/", function ($matches) use ($args) {
return @
$args[$matches[1]] ?: $matches[0];
},
$format);

/*
结果:

CREATE DATABASE IF NOT EXISTS people;
GRANT ALL PRIVILEGES ON :database_name.* TO 'staff'@'localhost';
SET PASSWORD = PASSWORD('pass123');

占位符 `:database_name` 在 `$args` 中不存在匹配的键,因此按原样返回。
这样你就可以知道需要通过添加 "database_name" 项目来更正数组。
*/
2962051004 at qq dot com
5 年前
<?php

/**
* 将中文转为Html实体
* Turning Chinese into Html entity
* 作者 QiangGe
* 邮箱 [email protected]
*
*/

$str = <<<EOT
你好 world
EOT;

function
ChineseToEntity($str) {
return
preg_replace_callback(
'/[\x{4e00}-\x{9fa5}]/u', // utf-8
// '/[\x7f-\xff]+/', // if gb2312
function ($matches) {
$json = json_encode(array($matches[0]));
preg_match('/\[\"(.*)\"\]/', $json, $arr);
/*
* 通过json_encode函数将中文转为unicode
* 然后用正则取出unicode
* Turn the Chinese into Unicode through the json_encode function, then extract Unicode from regular.
* 我认为这个想法很完美。
*/
return '&#x'. str_replace('\\u', '', $arr[1]). ';';
},
$str
);
}

echo
ChineseToEntity($str);
// &#x4f60;&#x597d; world
Drake
14 年前
将十六进制解码为字符串 =)
<?php
class PhpHex2Str
{
private
$strings;

private function
x_hex2str($hex) {
$hex = substr($hex[0], 1);
$str = '';
for(
$i=0;$i < strlen($hex);$i+=2) {
$str.=chr(hexdec(substr($hex,$i,2)));
}
return
$str;
}

public function
decode($strings = null) {
$this->strings = (string) $strings;
return
preg_replace_callback('#\%[a-zA-Z0-9]{2}#', 'x_hex2str', $this->strings);
}
}

// 例子
$strings = 'a %20 b%0A h %27 h %23';

$obj = new PhpHex2Str;
$strings = $obj->decode($strings);
var_dump($strings);
?>
Anteaus
9 年前
请注意,从 php5.4 开始,您 **绝对不能** 按引用传递变量,例如 '[, int &$count ]' - 如果您这样做会导致致命错误。
我认为作者想说的是该函数按引用接受参数,但实际并非如此。 - 手册需要更新/澄清?
alex dot cs00 at yahoo dot ca
13 年前
不要使用此函数获取 BBCode,正如解释的那样。 如果您的文本超过 5000 个字符(平均),它将超出其限制并使您下载 PHP 页面。

根据此,您应该使用更高级但更复杂的工具。 您将需要一个名为“str_replace_once()”(搜索它)的函数,一个名为“countWord()”的函数,著名的“after()”、“before()”、“between()”。

str_replace_once 与 str_replace 相同,但只替换第一个匹配项。 至于 countWord,我想您知道如何计算单词出现次数。 至于 after、before 和 between,这是一个您可以在网站上轻松找到的用户函数。 否则,您可以自己编写。

以下函数能够处理所有块,假设 [code] 和 [/code],您可能希望父级之间的内容不被解析,包括 [code] 如果在另一个 [code] 内部。

<?php
function prepareCode($code, $op, $end)
{
$ix = 0;
$iy = 0;
$nbr_Op = countWord($op, $code);
while(
$ix < $nbr_Op)
{
if(
in_string($op, before($end, $code), false))
{
// 以下代码将默认的 [tag] 替换为 [tag:#]
$code = str_replace_once($op, substr($op, 0, -1).':'.$ix.']', $code);
$iy++;
}
elseif(
in_string($end, before($op, $code), false))
{
$iy = $iy-1;
$code = str_replace_once($end, substr($end, 0, -1).':'.($ix-1).']', $code);
$ix = $ix-2;
}
$ix++;
}
while(
in_string($end, $code))
{
$code = str_replace_once($end, substr($end, 0, -1).':'.($iy-1).']', $code);
$iy=$iy-1;
}

$code = preg_replace('#\\'.substr($end, 0, 1).':-[0-9]\]#i', '', $code);
if(
in_string(substr($op, 0, -1).':0]', $code) && !in_string(substr($end, 0, -1).':0]', $code))
{
$code .= substr($end, 0, -1).":0]";
}
return
$code;
}
?>

$code 返回整个文本的半格式化内容。 您只需将其用作
$code = prepareCode($code="您的文本", $op="[tag]" , $end="[/tag]");
然后只需替换父标签
str_replace("[tag:0]", "<tag>", $code);
str_replace("[/tag:0]", "</tag>", $code);
所以最后就像
[
jobowo
2 年前
请注意,在使用 preg_replace_callback 中的“Use ($variable)”时,如果您希望匿名函数修改该值,您必须按引用传递该值。 例如 preg_replace_callback($pattern, function($matches) use (&$alterThis) { $alterThis+=$something;},$string);
Underdog
10 年前
对于回调,我建议只使用永久函数或匿名函数。

根据使用情况,使用 create_function 作为回调可能会遇到内存问题,这可能是由于尝试与 PHP 5.2 或更早版本兼容造成的。 一些服务器出于某种原因拒绝更新其 PHP。

请仔细阅读 create_function 文档,以获取有关其内存用法的更多详细信息。

此致。
tgage at nobigfoot dot com
6 年前
要使用匿名回调函数传递给 preg_replace_callback 的父作用域中的变量,请使用 use() 参数。

$var1 = "one";
$var2 = "two";
$line = preg_replace_callback('/^.*$/',
function( $matches ) use ( $var1, $var2 ) {
return( $var1 . " " . $var2 );
}, $line);

将用父作用域中的 $var1 和 $var2(“one two”)的连接值替换整个字符串。
To Top