找到 XML 块并全部替换,正则表达式进行匹配并回调以覆盖


find xml block and replace all, regex to match and call back to overwrite

我只需要做一个快速匹配并替换来自 xml 的所有内容。 我不想表达该文件,因为该文件就像 100mb,我无法阻止这种情况。 所以这是示例数据。

     <?xml version="1.0" encoding="UTF-8"?>
    <products>
        <product active="1" on_sale="0" discountable="0">
            <sku>SKUTARGET</sku>
            <name><![CDATA[sdfsdf (NET)]]></name>
            <description><![CDATA[agag adgsgsdg asdgsdg]]></description>
            <keywords></keywords>
            <price>9.000000</price>
            <stock_quantity>35</stock_quantity>
            <reorder_quantity>0</reorder_quantity>
            <height>0.000000</height>
            <length>0.000000</length>
            <diameter>0.000000</diameter>
            <weight>0.000000</weight>
            <color>Black</color>
            <material>PVC</material>
            <barcode>883045010070</barcode>
            <release_date>2008-11-10</release_date>
            <images>
                <image>/sdssd/sdfsd.jpg</image>
                <image>/AL10sdfsds07XO/sdfsd.jpg</image>
            </images>
            <categories>
                <category code="166" video="0" parent="172">sd &amp; Sexy sdf</category>
                <category code="172" video="0" parent="">sd &amp; dddsdsds</category>
                <category code="641" video="0" parent="172">sdfsdf Costume sdfsdfsdf</category>
            </categories>
            <manufacturer code="AL" video="0">sdfsdf sdfs</manufacturer>
            <type code="LI" video="0">sdfsd</type>
        </product>
        <product active="1" on_sale="0" discountable="0">
            <sku>XXXXXXX</sku>
            <name><![CDATA[LEATHER sdfsdf (NET)]]></name>
            <description><![CDATA[asdgsdgsd sad sadg asdg asdg asdg asdg asdg asdg asdg asdg asdg asdg asdg]]></description>
            <keywords></keywords>
            <price>5.000000</price>
            <stock_quantity>36</stock_quantity>
            <reorder_quantity>0</reorder_quantity>
            <height>0.000000</height>
            <length>0.000000</length>
            <diameter>0.000000</diameter>
            <weight>0.000000</weight>
            <color>Black</color>
            <material>Leather</material>
            <barcode>883045300164</barcode>
            <release_date>2008-11-10</release_date>
            <images>
                <image>/AL10sds0XO/sdsdsd.jpg</image>
                <image>/sdsds/AL1sd00XOB.jpg</image>
                <image>/AL1sdsds00XO/sdsds.jpg</image>
            </images>
            <categories>
                <category code="80" video="0" parent="44">sdgsdgsdg</category>
                <category code="181" video="0" parent="172">Sleep &amp; Lounge</category>
            </categories>
            <manufacturer code="AL" video="0">Allure sdsds</manufacturer>
            <type code="LI" video="0">sdsfsdfsd</type>
        </product>
    </products>

我需要的只是从节点产品开始的一个块,在这种情况下,sku 是一个变量"SKUTARGET">

        <product active="1" on_sale="0" discountable="0">
            <sku>SKUTARGET</sku>
            <name><![CDATA[sdfsdf (NET)]]></name>
            <description><![CDATA[agag adgsgsdg asdgsdg]]></description>
            <keywords></keywords>
            <price>9.000000</price>
            <stock_quantity>35</stock_quantity>
            <reorder_quantity>0</reorder_quantity>
            <height>0.000000</height>
            <length>0.000000</length>
            <diameter>0.000000</diameter>
            <weight>0.000000</weight>
            <color>Black</color>
            <material>PVC</material>
            <barcode>883045010070</barcode>
            <release_date>2008-11-10</release_date>
            <images>
                <image>/sdssd/sdfsd.jpg</image>
                <image>/AL10sdfsds07XO/sdfsd.jpg</image>
            </images>
            <categories>
                <category code="166" video="0" parent="172">sd &amp; Sexy sdf</category>
                <category code="172" video="0" parent="">sd &amp; dddsdsds</category>
                <category code="641" video="0" parent="172">sdfsdf Costume sdfsdfsdf</category>
            </categories>
            <manufacturer code="AL" video="0">sdfsdf sdfs</manufacturer>
            <type code="LI" video="0">sdfsd</type>
        </product>

这是我目前正在使用的代码

    <?php
    ob_start();
    ?> 
    <?xml version="1.0" encoding="UTF-8"?>
    <products>
        <product active="1" on_sale="0" discountable="0">
            <sku>SKUTARGET</sku>
            <name><![CDATA[sdfsdf (NET)]]></name>
            <description><![CDATA[agag adgsgsdg asdgsdg]]></description>
            <keywords></keywords>
            <price>9.000000</price>
            <stock_quantity>35</stock_quantity>
            <reorder_quantity>0</reorder_quantity>
            <height>0.000000</height>
            <length>0.000000</length>
            <diameter>0.000000</diameter>
            <weight>0.000000</weight>
            <color>Black</color>
            <material>PVC</material>
            <barcode>883045010070</barcode>
            <release_date>2008-11-10</release_date>
            <images>
                <image>/sdssd/sdfsd.jpg</image>
                <image>/AL10sdfsds07XO/sdfsd.jpg</image>
            </images>
            <categories>
                <category code="166" video="0" parent="172">sd &amp; Sexy sdf</category>
                <category code="172" video="0" parent="">sd &amp; dddsdsds</category>
                <category code="641" video="0" parent="172">sdfsdf Costume sdfsdfsdf</category>
            </categories>
            <manufacturer code="AL" video="0">sdfsdf sdfs</manufacturer>
            <type code="LI" video="0">sdfsd</type>
        </product>
        <product active="1" on_sale="0" discountable="0">
            <sku>XXXXXXX</sku>
            <name><![CDATA[LEATHER sdfsdf (NET)]]></name>
            <description><![CDATA[asdgsdgsd sad sadg asdg asdg asdg asdg asdg asdg asdg asdg asdg asdg asdg]]></description>
            <keywords></keywords>
            <price>5.000000</price>
            <stock_quantity>36</stock_quantity>
            <reorder_quantity>0</reorder_quantity>
            <height>0.000000</height>
            <length>0.000000</length>
            <diameter>0.000000</diameter>
            <weight>0.000000</weight>
            <color>Black</color>
            <material>Leather</material>
            <barcode>883045300164</barcode>
            <release_date>2008-11-10</release_date>
            <images>
                <image>/AL10sds0XO/sdsdsd.jpg</image>
                <image>/sdsds/AL1sd00XOB.jpg</image>
                <image>/AL1sdsds00XO/sdsds.jpg</image>
            </images>
            <categories>
                <category code="80" video="0" parent="44">sdgsdgsdg</category>
                <category code="181" video="0" parent="172">Sleep &amp; Lounge</category>
            </categories>
            <manufacturer code="AL" video="0">Allure sdsds</manufacturer>
            <type code="LI" video="0">sdsfsdfsd</type>
        </product>
    </products>
    <?php
    $xml_str = ob_get_contents();
    ob_end_clean();
    $tar_sku="SKUTARGET"; // this is the sku of the product block I need to have
    $pat= '/^.*(<product *<sku>'.$tar_sku.'</sku>*</product>).*$/is'; // this should match the block with the sku but no other block
    $replacement='$1';//This should overwrite everything with that found block.
    $returnValue = preg_replace($pat, $replacement, $xml_str);

任何帮助都会很棒。 谢谢。杰里米

[编辑]

这是下面建议中的测试代码。 到目前为止,仍然不起作用。 我期望通过该 SKU 匹配回显 xml 块的字符串。 还没有运气。

    <?php
    error_reporting(E_ALL);
    ini_set('display_errors', '1');
    umask(0);
    $xml_str = <<<EOD
            <?xml version="1.0" encoding="UTF-8"?>
            <products>
                <product active="1" on_sale="0" discountable="0">
                    <sku>SKUTARGET</sku>
                    <name><![CDATA[sdfsdf (NET)]]></name>
                    <description><![CDATA[agag adgsgsdg asdgsdg]]></description>
                    <keywords></keywords>
                    <price>9.000000</price>
                    <stock_quantity>35</stock_quantity>
                    <reorder_quantity>0</reorder_quantity>
                    <height>0.000000</height>
                    <length>0.000000</length>
                    <diameter>0.000000</diameter>
                    <weight>0.000000</weight>
                    <color>Black</color>
                    <material>PVC</material>
                    <barcode>883045010070</barcode>
                    <release_date>2008-11-10</release_date>
                    <images>
                        <image>/sdssd/sdfsd.jpg</image>
                        <image>/AL10sdfsds07XO/sdfsd.jpg</image>
                    </images>
                    <categories>
                        <category code="166" video="0" parent="172">sd &amp; Sexy sdf</category>
                        <category code="172" video="0" parent="">sd &amp; dddsdsds</category>
                        <category code="641" video="0" parent="172">sdfsdf Costume sdfsdfsdf</category>
                    </categories>
                    <manufacturer code="AL" video="0">sdfsdf sdfs</manufacturer>
                    <type code="LI" video="0">sdfsd</type>
                </product>
                <product active="1" on_sale="0" discountable="0">
                    <sku>XXXXXXX</sku>
                    <name><![CDATA[LEATHER sdfsdf (NET)]]></name>
                    <description><![CDATA[asdgsdgsd sad sadg asdg asdg asdg asdg asdg asdg asdg asdg asdg asdg asdg]]></description>
                    <keywords></keywords>
                    <price>5.000000</price>
                    <stock_quantity>36</stock_quantity>
                    <reorder_quantity>0</reorder_quantity>
                    <height>0.000000</height>
                    <length>0.000000</length>
                    <diameter>0.000000</diameter>
                    <weight>0.000000</weight>
                    <color>Black</color>
                    <material>Leather</material>
                    <barcode>883045300164</barcode>
                    <release_date>2008-11-10</release_date>
                    <images>
                        <image>/AL10sds0XO/sdsdsd.jpg</image>
                        <image>/sdsds/AL1sd00XOB.jpg</image>
                        <image>/AL1sdsds00XO/sdsds.jpg</image>
                    </images>
                    <categories>
                        <category code="80" video="0" parent="44">sdgsdgsdg</category>
                        <category code="181" video="0" parent="172">Sleep &amp; Lounge</category>
                    </categories>
                    <manufacturer code="AL" video="0">Allure sdsds</manufacturer>
                    <type code="LI" video="0">sdsfsdfsd</type>
                </product>
            </products>

    EOD;

    $tar_sku="SKUTARGET"; // this is the sku of the product block I need to have
    $pattern = "~<product .*?<sku>$tar_sku</sku>.*?</product>~is"; 
    $returnValue = preg_match($pattern,$xml_str);
    echo '--'.$returnValue[0];

不要使用正则表达式来解析 XML。如果您担心内存使用量,则使用正则表达式将比增量解析消耗更多的内存。由于正则表达式只能对字符串进行操作,因此您至少需要 100MB 的内存来保存文件字符串,然后才能对其进行任何操作。如果使用增量 XML 分析器,则可以使用的内存少于文件大小。

这项工作的正确工具是 XMLReader .

博士

此答案中有两种XMLReader解析实现:

  • getmatchingproducts_xml_expand()getmatchingproducts_xml_noexpand()函数返回所有匹配产品的列表。内存使用情况取决于源 xml 中匹配的 SKU 产品数量。
  • ProductMatcher类是一个迭代器(可以在foreach中使用(,它将以字符串、DOMDocumentSimpleXMLElement的形式增量返回匹配的产品。无论您的源 XML 有多大或有多少产品匹配,它都会使用大约 1MB 的内存。

测试文件

我使用您创建的格式创建了一个 120 MB 的示例文件。这是创建代码:

function maketestfile() {
    $xml = <<<EOT
        <product active="1" on_sale="0" discountable="0">
            <sku>{{SKU}}</sku>
            <name><![CDATA[LEATHER sdfsdf (NET)]]></name>
            <description><![CDATA[asdgsdgsd sad sadg asdg asdg asdg asdg asdg asdg asdg asdg asdg asdg asdg]]></description>
            <keywords></keywords>
            <price>5.000000</price>
            <stock_quantity>36</stock_quantity>
            <reorder_quantity>0</reorder_quantity>
            <height>0.000000</height>
            <length>0.000000</length>
            <diameter>0.000000</diameter>
            <weight>0.000000</weight>
            <color>Black</color>
            <material>Leather</material>
            <barcode>883045300164</barcode>
            <release_date>2008-11-10</release_date>
            <images>
                <image>/AL10sds0XO/sdsdsd.jpg</image>
                <image>/sdsds/AL1sd00XOB.jpg</image>
                <image>/AL1sdsds00XO/sdsds.jpg</image>
            </images>
            <categories>
                <category code="80" video="0" parent="44">sdgsdgsdg</category>
                <category code="181" video="0" parent="172">Sleep &amp; Lounge</category>
            </categories>
            <manufacturer code="AL" video="0">Allure sdsds</manufacturer>
            <type code="LI" video="0">sdsfsdfsd</type>
        </product>
EOT;
    $fo = fopen('test2.xml', 'wb');
    fwrite($fo, "<?xml version='"1.0'" encoding='"UTF-8'"?>'n");
    fwrite($fo, "<products>'n");
    $sku = array('SKUTARGET', 'XXXXXXXX', 'SKUY12345', '124432XXK', 'FOO1234BAR');
    for ($i=0; $i < 100000; $i++) { 
        shuffle($sku);
        fwrite($fo, str_replace('{{SKU}}', $sku[0], $xml));
    }
    fwrite($fo, "</products>'n");
    fclose($fo);
}

记忆和定时功能

function trial($method, $args) {
    //prime the pump
    if (!function_exists($method))
        throw BadFunctionCallException();
    call_user_func_array($method, $args);
    $iter = 2;
    $runtime = 0;
    for ($i=0; $i < $iter; $i++) {
        $start = microtime(true);
        $res = call_user_func_array($method, $args);
        $runtime += microtime(true)-$start;
    }
    return array(
        'peakmem' => memory_get_peak_usage(),
        'mem' => memory_get_usage(),
        'time' => $runtime/$iter,
        'return' => $res,
    );
}
function main($method, $filename) {
    $args = array($filename, 'SKUTARGET');
    $res = trial($method, $args);
    echo "Found products: ",count($res['return']),"'n";
    printf("%30s %3.2f %3.2f %4.3f'n", $method, $res['peakmem']/(1024*1024), $res['mem']/(1024*1024), $res['time']);
}
main($argv[1], $argv[2]);

Regex vs XMLReader

最后,我测试了这些功能。前两个使用其他答案建议的正则表达式,第三个使用XMLReader

function getmatchingproducts_regex1($xmlfile, $desiredsku) {
    $pattern = "~<product [^<]*<sku>".preg_quote($desiredsku,'~')."</sku>.*?</product>~Sus";
    $xmlstr = file_get_contents($xmlfile);
    preg_match_all($pattern, $xmlstr, $matchingproducts);
    return $matchingproducts;
}
function getmatchingproducts_regex2($xmlfile, $desiredsku) {
    $pattern = "~<product [^<]*+<sku>".preg_quote($desiredsku,'~')."</sku>[^<]*(?:<(?!/product>)[^<]*)*</product>~Su";
    $xmlstr = file_get_contents($xmlfile);
    preg_match_all($pattern, $xmlstr, $matchingproducts);
    return $matchingproducts;
}
function getmatchingproducts_xml_expand($xmlfile, $desiredsku) {
    $r = new XMLReader();
    $r->open($xmlfile, null, LIBXML_COMPACT);
    $matchingproducts = array();
    do {
        // advance to first product element
        $r->read();
    } while ($r->nodeType!==XMLReader::NONE
        and !($r->nodeType===XMLReader::ELEMENT and $r->name==='product' and !$r->isEmptyElement));
    while ($r->nodeType!==XMLReader::NONE) {
        if ($r->nodeType===XMLReader::ELEMENT and $r->name==='product' and !$r->isEmptyElement) {
            $dom = $r->expand(new DOMDocument('1.0','UTF-8'));
            $sxe = simplexml_import_dom($dom);
            if ((string) $sxe->sku===$desiredsku) {
                // Matching product found.
                // We have access to the <product> element and contents as:
                // * raw text via $r->readOuterXml()
                // * DOMDocument via $dom
                // * SimpleXML via $sxe
                // Pick the one you want and save:
                $matchingproducts[] = $r->readOuterXml();
                // null the rest to be very conservative about memory
                $dom = $sxe = null;
            }
        }
        // optimization--skip to next product sibling
        $r->next('product');
    }
    $r->close();
    return $matchingproducts;
}

最后,我将所有这些保存在一个文件中,并在我的双核8GB系统上运行它。(数字是峰值内存、最终内存和每次迭代的秒数。"找到的产品"只是为了验证匹配的产品数量是否正确。

$ php xmlreader.php getmatchingproducts_xml_expand test2.xml
Found products: 19969
getmatchingproducts_xml_expand 86.96 58.17 10.648
$ php xmlreader.php getmatchingproducts_regex1 test2.xml
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 1253 bytes) in xmlreader.php on line 72
$ php xmlreader.php getmatchingproducts_regex2 test2.xml
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 1253 bytes) in xmlreader.php on line 78

您会注意到,正则表达式方法甚至无法在不耗尽可用内存的情况下运行!此外,XMLReader方法(除了实际正确解析 XML 之外(使用的内存少于文件大小。我愿意打赌,大部分getmatchingproducts_xml_expand内存也是$matchedproducts数组,而不是解析。 通过将解析器函数包装在一个类中,可以进一步减少内存使用量,以便一次检索一个匹配项。

不过,使用正则表达式的优点是它要快得多。这是另一个尝试,将内存限制提高到 1GB:

$ php -d memory_limit=1G xmlreader.php getmatchingproducts_regex1 test2.xml
Found products: 19968
    getmatchingproducts_regex1 181.31 30.01 1.421
$ php -d memory_limit=1G xmlreader.php getmatchingproducts_regex2 test2.xml
Found products: 19968
    getmatchingproducts_regex2 181.31 30.01 0.906

所有这些速度都来自忽略XML解析规则并将其视为字符串。(有趣的是,整个文件都在内存中这一事实并不影响XMLReader的速度,只影响它的内存使用量。

如果您需要快速访问低内存使用率,则需要某种索引或数据库。您可以使用 sqlite、sqlite3、dbm 创建一个平面文件数据库,并使用 XMLReader 加载由 SKU 键入的产品。然后,不要读取 XML 文件,而是从数据库中加载该产品的 xml 字符串。

只是为了踢,我尝试了一种不使用扩展的XMLReader解析方法,看看是否可以节省时间或内存。不过,差异可以忽略不计,代码也不太清楚。

function getmatchingproducts_xml_noexpand($xmlfile, $desiredsku) {
    $r = new XMLReader();
    $r->open($xmlfile, null, LIBXML_COMPACT);
    $matchingproducts = array();
    $candidateproduct = null;
    do {
        // advance to first product element
        $r->read();
    } while ($r->nodeType!==XMLReader::NONE
        and !($r->nodeType===XMLReader::ELEMENT and $r->name==='product' and !$r->isEmptyElement));
    while ($r->nodeType!==XMLReader::NONE) {
        if ($r->nodeType===XMLReader::ELEMENT and $r->name==='product' and !$r->isEmptyElement) {
            $candidateproduct = array($r->readOuterXML(), $r->depth);
            $r->read();
            while ($r->depth > $candidateproduct[1]) {
                if ($r->nodeType===XMLReader::ELEMENT and $r->name==='sku' and $r->readString()===$desiredsku) {
                    $matchingproducts[] = $candidateproduct[0];
                    $r->next('product');
                    break;
                } else {
                    $r->next();
                }
            }
            $candidateproduct = null;
        } else {
            $r->next();
        }
    }
    $r->close();
    return $matchingproducts;
}

$ php xmlreader.php getmatchingproducts_xml_noexpand test2.xml
Found products: 19969
getmatchingproducts_xml_noexpand 86.95 58.17 13.716

以增量方式返回解析结果

又一次实现。这可能与这所能达到的效率一样高。它使用不到 1MB 的内存解析 120MB 的测试文件。

class ProductMatcher implements Iterator {
    // return values for next()
    const R_STR = 'product_str'; // return string
    const R_DOM = 'product_dom'; // return DOMDocument
    const R_SXE = 'product_sxe'; // return SimpleXMLElement
    protected $reader;
    protected $productcount = null;
    protected $product_str = null;
    protected $product_dom = null;
    protected $product_sxe = null;
    protected $xmlfile;
    protected $returnmethod;
    public $desiredsku;
    function __construct($xmlfile, $desiredsku, $returnmethod=self::R_STR) {
        $this->xmlfile = $xmlfile;
        $this->desiredsku = $desiredsku;
        $this->setReturnMethod($returnmethod);
    }
    function __destruct() {
        if (isset($this->reader)) {
            $this->reader->close();
        }
    }
    protected function _create() {
        $this->productcount = null;
        $this->reader = new XMLReader();
        $this->reader->open($this->xmlfile, null, LIBXML_COMPACT);
    }
    protected function _start() {
        $r =& $this->reader;
        do {
            // advance to first product element
            $r->read();
        } while ($r->nodeType!==XMLReader::NONE
            and !($r->nodeType===XMLReader::ELEMENT and $r->name==='product' and !$r->isEmptyElement));
    }
    protected function advance() {
        $r =& $this->reader;
        $productfound = false;
        $this->product_str = $this->product_sxe = $this->product_dom = null;
        while ($r->nodeType!==XMLReader::NONE and !$productfound) {
            if ($r->nodeType===XMLReader::ELEMENT and $r->name==='product' and !$r->isEmptyElement) {
                // xmlreader_print($r);
                $dom = $r->expand(new DOMDocument('1.0','UTF-8'));
                $sxe = simplexml_import_dom($dom);
                if ((string) $sxe->sku===$this->desiredsku) {
                    $this->product_str = $r->readOuterXml();
                    $this->product_sxe = $sxe;
                    $this->product_dom = $dom;
                    $productfound = true;
                    $this->productcount = (isset($this->productcount)) ? $this->productcount+1 : 0;
                }
            }
            // optimization--skip to next product sibling
            $r->next('product');
        }
        if (!$productfound) {
            $this->productcount = null;
        }
    }
    public function setReturnMethod($method) {
        $this->returnmethod = $method;
    }
    public function getReturnMethod() {
        return $this->returnmethod;
    }
    public function rewind() {
        $this->_create();
        $this->_start();
        $this->advance();
    }
    public function valid() {
        return $this->productcount!==null;
    }
    public function current() {
        return $this->{$this->returnmethod};
    }
    public function key() {
        return $this->productcount;
    }
    public function next() {
        $this->advance();
    }
}
function timeProductMatcher($filename) {
    $matcher = new ProductMatcher($filename, 'SKUTARGET');
    foreach ($matcher as $m) {}
    $runtime = 0;
    $iter = 2;
    for ($i=0; $i < $iter; $i++) { 
        $start = microtime(true);
        $matcher = new ProductMatcher($filename, 'SKUTARGET');
        foreach ($matcher as $n => $match) {}
        $runtime += microtime(true)-$start;
    }
    echo "Found products: ",$n+1, "'n";
    printf("%30s %3.2f %3.2f %4.3f'n", 'ProductMatcher', memory_get_peak_usage()/(1024*1024), memory_get_usage()/(1024*1024), $runtime/$iter);
}
timeProductMatcher($argv[1]);

结果:

$ php xmlreader.php test2.xml
Found products: 19969
                ProductMatcher 0.76 0.75 10.394

扩展示例用法:

$matcher = new ProductMatcher($filename, 'SKUTARGET', ProductMatcher::R_SXE);
foreach ($matcher as $product) {
    // $product is a SimpleXMLElement because we specified R_SXE
    (string) $product->sku === 'SKUTARGET'; // true
}

如果只想提取匹配的<product>..</product>位,请使用 preg_match ,而不是preg_replace

$pattern = "~<product .*?<sku>$tar_sku</sku>.*?</product>~is";
$returnValues = preg_match($pat,$xml_str);

这里$returnValues是一个数组,它要么是空的,要么有一个包含您所追求的相关 XML 位的 elment $returnValues[0]

这是因为preg_match在第一场比赛时停止。如果您知道整个 XML 中只有一个对应的SKUTARGET,请使用 preg_match 。如果您认为可能有多个,并且想要提取所有内容,请使用 preg_match_all

正则表达式与您的基本相同,除了:

  • 如果正则表达式中有内部/,则不能使用 / 来分隔正则表达式(如 /regex/(,除非您转义它们。所以你必须在</sku>等中逃离/。我已将分隔符更改为~,因此我无需费心转义我的内部/.
  • <product * -> <product .*(前者仅匹配空格(
  • </sku>*</product> -> </sku>.*?</product>(前者仅匹配 0 或更多 > /sku 之后。
  • 例如,将贪婪.*更改为非贪婪.*?,以防止抓取属于下一个产品的 XML。

不要使用正则表达式来解析 xml。相反,请使用适用于 php 的 xml 工具系列。目前还不清楚你想在这里做什么。看起来您想从文档中挑选出一个特定的元素并将其替换为其他元素。下面是一个示例。

[编辑] 好的,所以如果你想处理大的xml文件,使用xmlreader。

代码的主要问题是你错误地使用了preg_match。 返回值只是一个整数,表示正则表达式匹配的次数,即 01 。 如果要检索匹配的文本,则必须提供一个数组来存储它:

preg_match($pattern, $subject, $matches);

但是正则表达式也存在一个问题,如果您将第二个<product>元素作为目标而不是第一个元素,您将看到它。 比赛仍然从第一个<product>元素开始,然后继续到第二个元素的结束。 不情愿的.*?不足以保证尽可能短的比赛,因为它只影响比赛结束的地方,而不是比赛开始的地方。

您需要确保在它匹配开始<product>标记后,在找到<sku>标记之前,它不能再匹配任何<product></product>标记。 假设<sku>始终是<product>元素中列出的第一个元素,那么将第一个.*?更改为[^<]*很简单:

"~<product [^<]*<sku>$tar_sku</sku>.*?</product>~is"

此外,考虑到文件的大小,使正则表达式尽可能高效可能是值得的。 为此,我会将第一个量词设为所有格 - [^<]*+ - 并将其他.*?量词替换为更具确定性的东西。

"~<product [^<]*+<sku>$tar_sku</sku>[^<]*(?:<(?!/product>)[^<]*)*</product>~"

请注意,我还删除了修饰符;s标志现在无关紧要,因为正则表达式中没有点,并且 i 标志可能从不需要,因为 XML 标记名称区分大小写。 如果 SKU 不是,您可以使用内联修饰符将 i 标志仅应用于正则表达式的该部分,即 (?i:...)

"~<product [^<]*+<sku>(?i:$tar_sku)</sku>[^<]*(?:<(?!/product>)[^<]*)*</product>~"

下面是一个演示:http://ideone.com/mZqFz