我正在尝试使用标记来格式化Ordered列表,这里是标记样式:
$strings = "1. dog
1. cat
1. fish
1. horse
1. monkey
1. pig
";
该列表中的 horse
和monkey
应该是子列表的一部分,因为它们在数字前面有一个空格。这是我正在使用的代码:
function blq($match){
$str = preg_replace("/^1\. (.+?)$/m", "<li>$1</li>", $match[0]);
$str = preg_replace_callback("/(?:^1\. .+(\n|$))+/m", 'blq', $str);
return "<ol>$str</ol>";
}
$string = preg_replace_callback("/(?:^ ?1\. .+(\n|$))+/m", 'blq', $strings);
echo $string;
该代码正在创建此输出:
<ol><li>dog
</li>
<li>cat
</li>
<li>fish
</li>
1. horse
1. monkey
<li>pig
</li>
</ol>
horse
和monkey
未创建为子列表,但只是被忽略。我觉得我接近答案,但我不知道该怎么做才能得到答案......
注意我想允许无限数量的子列表
答案 0 :(得分:2)
<?php
$text = "1. dog
1. cat
1. fish
1. horse
1. duck
1. goose
1. swan
1. monkey
1. chimpanzee
1. orangutan
1. whale
1. pig
";
function callback($match) {
$out = preg_replace_callback("/(^($match[2] +)1\. .+(\\n|$))(?1)*/m", 'callback', $match[0]);
$out = preg_replace("/^$match[2]1\. (.+)$/m", "<li>$1</li>", $out);
return "<ol>\n$out</ol>\n";
}
$html = preg_replace_callback("/(^( *)1\. .+(\\n|$))(?1)*/m", 'callback', $text);
echo $html;
?>
这是一个非常巧妙的想法,递归地使用preg_replace_callback
。另外,你对$
是正确的 - 字符串不是在双引号内插值,除非它们是一个集变量;我总是忘记这一点。并且,您使用/m
是正确的,因为您希望^
匹配每行的开头(而不是整个字符串的开头),尽管如此(\n|$)
也是正确的$
{1}}匹配/m
模式中每一行的结尾 - 因为否则,量词+
将无效,因为$
实际上不会消费 \n
。当我第一次阅读你的问题时,我没有看到这些事实。
现在,让我们从第一个表达式开始:
/(^( *)1\. .+(\\n|$))(?1)*/m
实际上,除了速记外,递归子表达式(?1)
不是必需的。让我们扩展一下:
/(^( *)1\. .+(\\n|$))(^( *)1\. .+(\\n|$))*/m
| || |
+------------------++------------------+
所以我们有两个相同的一半。为什么不像你一样使用+
?因为我想捕获缩进第一个行的空格数。这些空格存储在$match[2]
。
在回调中,我们将这些空格带回来,加上一个或多个空格:
/(^($match[2] +)1\. .+(\\n|$))(?1)*/m
这样,我们只会在preg_replace_callback
递归的每个级别上查看当前缩进级别(更多空间)下面的级别。并且随着递归的展开,仅由$match[2]
包含在<li></li>
空间中的空格数/^$match[2]1\. (.+)$/m
缩进的行,
<ol></ol>
返回包裹在{{1}}中的整个。