2

我正在尝试制作标记以格式化有序列表,这是标记样式:

$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没有被创建为子列表,而只是被忽略了。我觉得我已经接近答案了,但我不知道该怎么做才能得到那个答案......

注意我想允许无限数量的子列表

4

1 回答 1

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;

?>

这是一个ideone演示


这是一个非常巧妙的想法,preg_replace_callback递归使用。$此外,除非它们是一个集合变量,否则您对-strings 不会在双引号内插值是正确的;我总是忘记这一点。而且,您使用是正确的,/m因为您希望^匹配每行的开头(而不是整个字符串的开头),并且(\n|$)尽管$在 mode 中匹配每行的结尾,但您也可以正确使用/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>

于 2013-10-15T19:19:44.757 回答