php - 使用 preg_replace 转换缩进(无回调)

标签 php xml regex domdocument

我有一些由 DOMDocument::saveXML() 返回的 XML block 。它已经非常缩进了,每层有两个空格,如下所示:

<?xml version="1.0"?>
<root>
  <error>
    <a>eee</a>
    <b>sd</b>
  </error>
</root>

由于无法为缩进字符配置 DOMDocument (AFAIK),因此我 thought it's possible to run a regular expression 并通过将所有两个空格对替换为制表符来更改缩进。这可以通过回调函数 (Demo) 来完成:

$xml_string = $doc->saveXML();
function callback($m)
{
    $spaces = strlen($m[0]);
    $tabs = $spaces / 2;
    return str_repeat("\t", $tabs);
}
$xml_string = preg_replace_callback('/^(?:[ ]{2})+/um', 'callback', $xml_string);

我现在想知道是否可以在没有回调函数的情况下执行此操作(并且没有 e-修饰符 (EVAL))。任何有想法的正则表达式向导?

最佳答案

你可以使用\G:

preg_replace('/^  |\G  /m', "\t", $string);

做了一些基准测试并在 Win32 上用 PHP 5.2 和 5.4 得到了以下结果:

>php -v
PHP 5.2.17 (cli) (built: Jan  6 2011 17:28:41)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2010 Zend Technologies

>php -n test.php
XML length: 21100
Iterations: 1000
callback: 2.3627231121063
\G:       1.4221360683441
while:    3.0971200466156
/e:       7.8781840801239


>php -v
PHP 5.4.0 (cli) (built: Feb 29 2012 19:06:50)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies

>php -n test.php
XML length: 21100
Iterations: 1000
callback: 1.3771259784698
\G:       1.4414191246033
while:    2.7389969825745
/e:       5.5516891479492

令人惊讶的是回调比 PHP 5.4 中的 \G 更快(尽管这似乎取决于数据,\G 在某些其他情况下更快)。

对于 \G 使用 /^ |\G/m,比 /(?:^|\G)/m 快一点/(?>^|\G)/m/(?:^|\G)/m 还要慢。 /u/S/X 开关不会显着影响 \G 性能。

while 替换在深度较低时最快(在我的测试中最多约 4 个缩进,8 个空格),但随着深度的增加会变慢。

使用了以下代码:

<?php

$base_iter = 1000;

$xml_string = str_repeat(<<<_STR_
<?xml version="1.0"?>
<root>
  <error>
    <a>  eee  </a>
    <b>  sd    </b>         
    <c>
            deep
                deeper  still
                    deepest  !
    </c>
  </error>
</root>
_STR_
, 100);


//*** while ***

$re = '%# Match leading spaces following leading tabs.
    ^                     # Anchor to start of line.
    (\t*)                 # $1: Preserve any/all leading tabs.
    [ ]{2}                # Match "n" spaces.
    %mx';

function conv_indent_while($xml_string) {
    global $re;

    while(preg_match($re, $xml_string))
        $xml_string = preg_replace($re, "$1\t", $xml_string);

    return $xml_string;
}


//*** \G ****

function conv_indent_g($string){
    return preg_replace('/^  |\G  /m', "\t", $string);
}


//*** callback ***

function callback($m)
{
    $spaces = strlen($m[0]);
    $tabs = $spaces / 2;
    return str_repeat("\t", $tabs);
}
function conv_indent_callback($str){
    return preg_replace_callback('/^(?:[ ]{2})+/m', 'callback', $str);
}


//*** callback /e *** 

function conv_indent_e($str){
    return preg_replace('/^(?:  )+/me', 'str_repeat("\t", strlen("$0")/2)', $str);
}



//*** tests

function test2() {
    global $base_iter;
    global $xml_string;
    $t = microtime(true);

    for($i = 0; $i < $base_iter; ++$i){
        $s = conv_indent_while($xml_string);
        if(strlen($s) >= strlen($xml_string))
            exit("strlen invalid 2");
    }

    return (microtime(true) - $t);
}

function test1() {
    global $base_iter;
    global $xml_string;
    $t = microtime(true);

    for($i = 0; $i < $base_iter; ++$i){
        $s = conv_indent_g($xml_string);
        if(strlen($s) >= strlen($xml_string))
            exit("strlen invalid 1");
    }

    return (microtime(true) - $t);
}

function test0(){
    global $base_iter;
    global $xml_string;
    $t = microtime(true);

    for($i = 0; $i < $base_iter; ++$i){     
        $s = conv_indent_callback($xml_string);
        if(strlen($s) >= strlen($xml_string))
            exit("strlen invalid 0");
    }

    return (microtime(true) - $t);
}


function test3(){
    global $base_iter;
    global $xml_string;
    $t = microtime(true);

    for($i = 0; $i < $base_iter; ++$i){     
        $s = conv_indent_e($xml_string);
        if(strlen($s) >= strlen($xml_string))
            exit("strlen invalid 02");
    }

    return (microtime(true) - $t);
}



echo 'XML length: ' . strlen($xml_string) . "\n";
echo 'Iterations: ' . $base_iter . "\n";

echo 'callback: ' . test0() . "\n";
echo '\G:       ' . test1() . "\n";
echo 'while:    ' . test2() . "\n";
echo '/e:       ' . test3() . "\n";


?>

关于php - 使用 preg_replace 转换缩进(无回调),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8616594/

相关文章:

php - 如何检查 SELECT EXISTS 是否返回值?

php - 使用 CodeIgniter 锚定到某个部分

xml - 如何处理 XML GENERATE 命令中的 xml 编码?

android - Cordova 自适应图标链接文件资源错误

python - 创建用于文本处理的模式列表

python - 替换逗号分隔字符串中间的下划线分隔子字符串

javascript - 当一个选择框选项更改时,两个选择选项也会更改

php - Magento 以编程方式创建一个 sales/quote_address 对象

xml - 如何将具有重复字段的 XML 文件导入 FileMaker 数据库?

regex - 如何使用正则表达式拆分字符串