使用文本样式创建包含子列表的列表


Using text styling to create a list with sublists

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

$strings = "1. dog
1. cat
1. fish
 1. horse
 1. monkey
1. pig
";

该列表中的horsemonkey应该是子列表的一部分,因为它们在数字之前有一个空格。这是我正在使用的代码:

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>

horsemonkey不是作为子列表创建的,而只是被忽略了。我觉得我正在接近答案,但我不确定该怎么做才能得到这个答案......

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

<?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。此外,您关于$字符串不在双引号内插是正确的,除非它们是集合变量;我总是忘记这一点。而且,您使用/m是正确的^因为您希望匹配每行的开头(而不是整个字符串的开头),并且尽管$/m模式下匹配每行的末尾,但您使用('n|$)也是正确的——因为否则,量词+将不起作用,因为$实际上不会消耗'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>.