【问题标题】:Can this preg_replace_callback method be done in xPath or XQuery?这个 preg_replace_callback 方法可以在 xPath 或 XQuery 中完成吗?
【发布时间】:2010-11-02 12:29:33
【问题描述】:

下面的脚本采用 $myContent 块并对 $myKeyword 值执行 doReplace() 函数。我遇到的问题是它不知道替换文本是否已经在标签内。

我需要修改 doReplace() 函数,使其不会触及命名标签(h1、h2、h3、i、u、b、strong、em、img)内部或作为其属性的任何文本。

我认为将其转换为 xPath 方法并寻找有关如何使用 xPath 完成它的建议会更好。所以问题是:“如何将其转换为 xPath”?

注意:替换函数中有 count 变量,因为我只替换关键字的前三个出现。第一次出现时加粗,下一次变为斜体,第三次出现时加下划线。

$myKeyword = "test keyword";

$myContent = "My content contains the "test keyword". 
Don't do the replace if the test keyword is inside:
h1, h2, h3, i, u, b, strong, em, tags. 
<h1>This test keyword would not be replaced</h1>";

$myContent = preg_replace_callback("/\b($mykeyword)\b/i","doReplace", $myContent);

function doReplace($matches)
{
    static $count = 0;
    switch($count++) {
    case 0: return ' <b>'.trim($matches[1]).'</b>';
    case 1: return ' <em>'.trim($matches[1]).'</em>';
    case 2: return ' <u>'.trim($matches[1]).'</u>';
    default: return $matches[1];
    }
}

【问题讨论】:

  • 这个问题不清楚。向我们展示源 XML 并展示想要的结果。什么是$matches?。请给我们看一个真实的例子。格式化你的代码,这样就不需要水平滚动——因为它现在很难阅读和理解。
  • @Dimitre - 我已经重新格式化了代码。源将是 $myContent 并将通过 loadHTML 加载

标签: php xslt preg-replace xquery


【解决方案1】:

您不能在 XPath 1.0 或 2.0 中,因为您需要递归来表达这个算法。当然,您可以使用扩展功能。

这个 XQuery:

declare variable $match as xs:string external;
declare variable $replace as xs:string external;
declare variable $preserve as xs:string external;
declare variable $vPreserve := tokenize($preserve,',');
declare function local:copy-replace($element as element()) {   
   element {node-name($element)}
           {$element/@*,  
            for $child in $element/node()   
                return if ($child instance of element())   
                       then local:copy-replace($child)   
                       else if ($child instance of text() and
                                not(name($element)=$vPreserve))
                            then replace($child,$match,$replace)
                            else $child
           }
};   
local:copy-replace(/*) 

有了这个输入:

<html>
    <h1>This test keyword would not be replaced</h1>
    <p>This test keyword should be replaced</p>
</html>

输出:

<html>
    <h1>This test keyword would not be replaced</h1>
    <p>This replaced should be replaced</p>
</html>

还有这个样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml"/>
    <xsl:param name="pMatch" select="'test keyword'"/>
    <xsl:param name="pReplace" select="'replaced'"/>
    <xsl:param name="pPreserve" select="'h1,h2,h3,i,u,b,strong,em'"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()">
        <xsl:choose>
            <xsl:when test="contains(concat(',',$pPreserve,','),
                                     concat(',',name(..),','))">
                <xsl:value-of select="."/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="replace"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="replace">
        <xsl:param name="pString" select="."/>
        <xsl:choose>
            <xsl:when test="contains($pString,$pMatch)">
                <xsl:value-of
                     select="concat(substring-before($pString,
                                                     $pMatch),
                                    $pReplace)"/>
                <xsl:call-template name="replace">
                    <xsl:with-param name="pString"
                                    select="substring-after($pString,
                                                            $pMatch)"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$pString"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

编辑:更好的 XQuery。

【讨论】:

  • 谢谢亚历杭德罗。我已经重新标记了您的建议。
  • @Alejandro:谢谢!我是否将我的内容传递给 copy-replace($myContent)?我在哪里将关键字传递给函数?复制替换($myContent,$myKeyword)?
  • @Scott B:您需要将参数传递给您的 XQuery/XSLT 处理器。这取决于您的宿主语言。如果那是 PHP,我认为您没有本机 XQuery 处理器(您可以使用 .Net/Java 桥来使用 Saxon、Altova 或 XQSharp)。因此,您应该改用 XSLT 解决方案。
  • 谢谢亚历山德罗。哇,有很多东西要咀嚼。我原来的功能就是这么简单。
  • @Scott B:那是因为您需要处理嵌套元素。 XSLT 解决方案没有那么复杂:一个身份规则(按原样复制输入);文本节点规则,测试父元素是否在保留列表中,然后按原样输出,否则调用递归命名模板;只要剩余字符串中存在匹配项,递归命名模板就会输出替换。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 2019-03-30
  • 1970-01-01
  • 2015-11-13
  • 1970-01-01
  • 1970-01-01
  • 2014-03-26
相关资源
最近更新 更多