Php 如何使用正则表达式中的特定单词获取ID?

Php 如何使用正则表达式中的特定单词获取ID?,php,regex,Php,Regex,我的字符串: <div class="sect1" id="s9781473910270.i101"> <div class="sect2" id="s9781473910270.i102"> <h1 class="title">1.2 Summations and Products[label*summation]</h1> <p>text</p> </div> </div>

我的字符串:

<div class="sect1" id="s9781473910270.i101">       
<div class="sect2" id="s9781473910270.i102">
<h1 class="title">1.2 Summations and Products[label*summation]</h1>
<p>text</p> 
</div>
</div>           
<div class="sect1" id="s9781473910270.i103">
<p>sometext [ref*summation]</p>
</div>

<div class="figure" id="s9781473910270.i220">
<div class="metadata" id="s9781473910270.i221">
</div>
<p>fig1.2 [label*somefigure]</p>
<p>sometext [ref*somefigure]</p>
</div>        
<div class="sect1" id="s9781473910270.i101">       
<div class="sect2" id="s9781473910270.i102">
<h1 class="title">1.2 Summations and Products[label*summation]</h1>
<p>text</p> 
</div>
</div>             
<div class="sect1" id="s9781473910270.i103">
<p>sometext <a class="section-ref" href="s9781473910270.i102">1.2</a></p>
</div>


<div class="figure" id="s9781473910270.i220">
<div class="metadata" id="s9781473910270.i221">
<p>fig1.2 [label*somefigure]</p>
</div>
<p>sometext <a class="fig-ref" href="s9781473910270.i220">fig 1.2</a></p>          
</div>      
如何在不使用DOM解析器的情况下以更简单的方式完成

我的想法是,必须将
label*string
及其ID存储在一个数组中,并将循环引用字符串以匹配
label*string
,如果字符串匹配,则应替换它们的相关ID和类,而不是
ref*string

因此,我尝试使用正则表达式获取
标签*字符串及其相关id和类名。

这不是正则表达式的任务。正则表达式(通常)用于正则语言。您要做的是在上下文敏感语言上做一些工作(引用以前声明过的标识符)

因此,您应该使用DOM解析器。这方面的算法非常简单,因为您可以对一个节点及其子节点进行操作


所以你的问题的理论答案是:你不能。尽管它可能会以一些蹩脚的方式与许多正则表达式扩展一起工作。

这种方法包括使用html结构来使用DOMXPath检索所需的元素。第二次使用正则表达式从文本节点或属性提取信息:

$classRel = ['sect2'  => 'section-ref',
             'figure' => 'fig-ref'];

libxml_use_internal_errors(true);

$dom = new DOMDocument;
$dom->loadHTML($html); // or $dom->loadHTMLFile($url); 

$xp = new DOMXPath($dom);

// make a custom php function available for the XPath query
// (it isn't really necessary, but it is more rigorous than writing
// "contains(@class, 'myClass')" )
$xp->registerNamespace("php", "http://php.net/xpath");

function hasClass($classNode, $className) {
    if (!empty($classNode))
        return in_array($className, preg_split('~\s+~', $classNode[0]->value, -1, PREG_SPLIT_NO_EMPTY));
    return false;
}

$xp->registerPHPFunctions('hasClass');


// The XPath query will find the first ancestor of a text node with '[label*'
// that is a div tag with an id and a class attribute,
// if the class attribute doesn't contain the "metadata" class.

$labelQuery = <<<'EOD'
//text()[contains(., 'label*')]
/ancestor::div
[@id and @class and not(php:function('hasClass', @class, 'metadata'))][1]
EOD;

$idNodeList = $xp->query($labelQuery);

$links = [];

// For each div node, a new link node is created in the associative array $links.
// The keys are labels. 
foreach($idNodeList as $divNode) {

    // The pattern extract the first text part in group 1 and the label in group 2
    if (preg_match('~(\S+) .*? \[label\* ([^]]+) ]~x', $divNode->textContent, $m)) {
        $links[$m[2]] = $dom->createElement('a');
        $links[$m[2]]->setAttribute('href', $divNode->getAttribute('id'));
        $links[$m[2]]->setAttribute('class', $classRel[$divNode->getAttribute('class')]);
        $links[$m[2]]->nodeValue = $m[1];
    }
}


if ($links) { // if $links is empty no need to do anything

    $refNodeList = $xp->query("//text()[contains(., '[ref*')]");

    foreach ($refNodeList as $refNode) {
        // split the text with square brackets parts, the reference name is preserved in a capture
        $parts = preg_split('~\[ref\*([^]]+)]~', $refNode->nodeValue, -1, PREG_SPLIT_DELIM_CAPTURE);

        // create a fragment to receive text parts and links
        $frag = $dom->createDocumentFragment();

        foreach ($parts as $k=>$part) {
            if ($k%2 && isset($links[$part])) { // delimiters are always odd items
                $clone = $links[$part]->cloneNode(true);
                $frag->appendChild($clone);
            } elseif ($part !== '') {
                $frag->appendChild($dom->createTextNode($part));
            }
        }

        $refNode->parentNode->replaceChild($frag, $refNode);
    }
}

$result = '';

$childNodes = $dom->getElementsByTagName('body')->item(0)->childNodes;

foreach ($childNodes as $childNode) {
    $result .= $dom->saveXML($childNode);
}

echo $result;
$classRel=['sect2'=>'section ref',
'图'=>'图参考'];
libxml\u使用\u内部错误(true);
$dom=新的DOMDocument;
$dom->loadHTML($html);//或者$dom->loadHTMLFile($url);
$xp=新的DOMXPath($dom);
//使自定义php函数可用于XPath查询
//(其实没有必要,但比写作更严谨
//“包含(@class,'myClass')”)
$xp->registerNamespace(“php”http://php.net/xpath");
函数hasClass($classNode,$className){
如果(!empty($classNode))
在数组中返回($className,preg_split('~\s+~',$classNode[0]->value,-1,preg_split\u NO\u EMPTY));
返回false;
}
$xp->RegisterHPFunctions('hasClass');
//XPath查询将找到带有“[label*”的文本节点的第一个祖先
//这是一个带有id和class属性的div标记,
//如果class属性不包含“metadata”类。
$labelQuery=createElement('a');
$links[$m[2]]->setAttribute('href',$divNode->getAttribute('id');
$links[$m[2]]->setAttribute('class',$classRel[$divNode->getAttribute('class'));
$links[$m[2]]->nodeValue=$m[1];
}
}
如果($links){//如果$links为空,则无需执行任何操作
$refNodeList=$xp->query(“//text()[contains(,“[ref*”)]”);
foreach($refnodelistas$refNode){
//使用方括号分割文本部分,引用名称将保留在捕获中
$parts=preg\u split(“~\[ref\*([^]+)]]~”,$refNode->nodeValue,-1,preg\u split\u DELIM\u CAPTURE);
//创建一个片段以接收文本部分和链接
$frag=$dom->createDocumentFragment();
foreach($k=>$part){
if($k%2&&isset($links[$part]){//分隔符总是奇数项
$clone=$links[$part]->cloneNode(true);
$frag->appendChild($clone);
}elseif($part!=''){
$frag->appendChild($dom->createTextNode($part));
}
}
$refNode->parentNode->replaceChild($frag,$refNode);
}
}
$result='';
$childNodes=$dom->getElementsByTagName('body')->item(0)->childNodes;
foreach($childNodes作为$childNode){
$result.=$dom->saveXML($childNode);
}
回声$结果;

最简单的方法是使用DOM解析器。您能否提供一个格式良好的html示例(特别是查看div标记关闭的确切位置)?事实上,如果我使用DOM解析器,结果将仅保存为HTML,但我不需要在操作后将结果保存为HTML。无论您使用何种方式,结果始终保存为字符串!使用正确的HTML格式更新了帖子如何使用DOM解析器?非常感谢!它非常有用,再次感谢您宝贵的时间但是如果我使用
saveHTML
method,那么
HTML和body
标记将被字符串包装。我需要消除它。我只需要输入字符串作为输出,而不需要额外的标记。如何处理tat?@Learning:有一些选项可以传递到
DOMDocument::loadHTML
,以避免这种自动行为(与DOCTYPE相同)。
$dom->loadHTML($html,LIBXML\u html\u NOIMPLIED | LIBXML\u html\u NODEFDTD)但是当你使用它的时候,你的html必须有一个根元素,所以把
$html
改成
'.$html'.
,然后用
substr
删除根元素。另一个问题是,使用
DOM
,它会把输入字符串例如

改成

改成
输出中不需要任何更改。@学习:只有当您的html必须严格符合xhtml时,这一点才重要(如果是html5或xhtml过渡版,则无所谓)。在本例中,请删除
loadHTML
选项和根div,然后查看我的编辑,该编辑使用
DOMDocument::saveXML
将每个正文子项连接到结果字符串。这样,您可以获得xml样式的自动关闭标记(请注意,

将更改为

)。另一个问题:(如果重复,则替换链接在
ref*
的位置不起作用。只有替换在最后一次出现时起作用。如果像这样的字符串
$html='sometext[ref*summation]

1.2 Summations and product[label*summation]text

sometext[ref*summation]

-如果重复两次
ref*string
,则只会在最后一次出现时创建链接。我尝试修复它,但没有成功。