我在这样的html文件中有标记,放在一起;
*|SUBJECT|*
*|SUBJECT|*
*|IFNOT:ARCHIVE_PAGE|*
*|ARCHIVE|*
*|END:IF|*
*|FACEBOOK:PROFILEURL|*
*|TWITTER:PROFILEURL|*
*|FORWARD|*
*|IF:REWARDS*
*|REWARDS|*
*|END:IF|*
使用这个PHP函数和正则表达式我可以得到所有标签的结果
preg_match_all("/\*\|(.*?)\|\*/", $this->template, $elements);
$this->elements["Tags"] = $elements[0];
$this->elements["TagNames"] = $elements[1];
我想要的是找到一种方法来捕获IF:(TAG)语句和IFNOT:(TAG)语句以及内容。
到目前为止我所拥有的是
ergex=> /\*\|IF(([A-Z{0-3}]):([A-Z_]+))\|\*(.*?)\*\|END:IF\|\*|\*\|(.*?)\|\*/g
但它只是将自己的标签作为一个整体捕获,任何人都可以指出我正确的方向或帮助我。
答案 0 :(得分:2)
正如我在评论中提到的,你的方法是简单化,我可以让你开始使用我用于这些事情的方法。它更像是一个tokenizer / lexer / parser方法。
这听起来很大而且可怕但实际上却让它更简单
<?php
function parse($subject, $tokens)
{
$types = array_keys($tokens);
$patterns = [];
$lexer_stream = [];
$result = false;
foreach ($tokens as $k=>$v){
$patterns[] = "(?P<$k>$v)";
}
$pattern = "/".implode('|', $patterns)."/i";
if (preg_match_all($pattern, $subject, $matches, PREG_OFFSET_CAPTURE)) {
//print_r($matches);
foreach ($matches[0] as $key => $value) {
$match = [];
foreach ($types as $type) {
$match = $matches[$type][$key];
if (is_array($match) && $match[1] != -1) {
break;
}
}
$tok = [
'content' => $match[0],
'type' => $type,
'offset' => $match[1]
];
$lexer_stream[] = $tok;
}
$result = parseTokens( $lexer_stream );
}
return $result;
}
function parseTokens( array &$lexer_stream ){
$result = [];
$mode = 'none';
while($current = current($lexer_stream)){
$content = $current['content'];
$type = $current['type'];
switch($type){
case 'T_WHITESPACE':
next($lexer_stream);
break;
case 'T_TAG_START':
$mode = 'start';
next($lexer_stream);
break;
case 'T_WORD':
if($mode == 'start') echo "Tag $content\n";
if($mode == 'ifnot') echo "IfNot $content\n";
next($lexer_stream);
break;
case 'T_TAG_END':
$mode = 'none';
next($lexer_stream);
break;
case 'T_IFNOT':
$mode = 'ifnot';
next($lexer_stream);
break;
case 'T_EOF': return;
case 'T_UNKNOWN':
default:
print_r($current);
trigger_error("Unknown token $type value $content", E_USER_ERROR);
}
}
if( !$current ) return;
print_r($current);
trigger_error("Unclosed item $mode for $type value $content", E_USER_ERROR);
}
$subject = '*|SUBJECT|*
*|SUBJECT|*
*|IFNOT:ARCHIVE_PAGE|*
*|ARCHIVE|*
*|END:IF|*
*|FACEBOOK:PROFILEURL|*
*|TWITTER:PROFILEURL|*
*|FORWARD|*
*|IF:REWARDS*
*|REWARDS|*
*|END:IF|*';
$tokens = [
'T_WHITESPACE' => '[\r\n\s\t]+',
'T_TAG_START' => '\*\|',
'T_TAG_END' => '\|\*',
'T_IF' => 'IF:',
'T_IFNOT' => 'IFNOT:',
'T_ENDIF' => 'END:IF',
'T_WORD' => '\w+',
'T_EOF' => '\Z',
'T_UNKNOWN' => '.+?'
];
parse($subject,$tokens);
所以你可以看到here
它输出:
Tag SUBJECT
Tag SUBJECT
IfNot ARCHIVE_PAGE
Tag ARCHIVE
Array
(
[content] => END:IF
[type] => T_ENDIF
[offset] => 69
)
<br />
<b>Fatal error</b>: Unknown token T_ENDIF value END:IF in <b>[...][...]</b> on line <b>67</b><br />
错误是因为我只对End if
标签进行了处理(必须留下一些东西供您使用)。
对于我用另一个问题执行的解析器,你可以在我的github上找到它
https://github.com/ArtisticPhoenix/MISC/blob/master/JasonDecoder.php
它应该给你一些关于如何处理嵌套数组结构等的想法。
基本思路是你可以一次只添加一个标签,然后对那个标签进行解析。您可以在错误的位置对不同的模式类型执行尽可能多的错误检查,依此类推。它只是为一切提供了一个很好的结构。必要的是它使用preg_match_all
和一串regx的方法,使用与方法相同的基本方法。主要区别在于它从数组构建完整的regx,然后使用数组键和命名捕获组(以及一些代码魔法),它允许您以更直观的方式引用它们。它还使用PREG_OFFSET_CAPTURE
标志,我发现它比其他标志更快。
一个注意事项是标签的顺序非常重要,如果您先将T_UNKNOWN
标记与所有标记匹配,那么它就不会转到它下面的标记。因此,他们应该更具体地匹配列表中的较高者。例如,你可以做这样的标签
'T_IFNOT' => '\*\|IFNOT:',
而不是我拥有的那个,但它可能必须在:
之前 'T_TAG_START' => '\*\|',
因为该标签会先与之匹配。
也不要忘记放next($lexer_stream);
或者它将是一个无限循环。在嵌入式结构(如数组)中使用while和next来控制数组指针是必要的。
祝你好运,解析快乐!