Использование стиля текста для создания списка с подсписками
Я пытаюсь сделать разметку для форматирования упорядоченного списка, вот стиль разметки:
$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
не были созданы как подсписок, а просто проигнорированы. Я чувствую, что приближаюсь к ответу, но я не уверен, что нужно сделать, чтобы добраться до этого ответ...
Примечание Я хотел бы разрешить неограниченное количество подсписков
1 answers
<?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
рекурсивно. Кроме того, вы правы насчет $
- строки не интерполируются в двойных кавычках, если только они не являются переменной set; Я всегда об этом забываю. И вы были правы, используя /m
, так как вы хотите, чтобы ^
соответствовало началу каждой строки (а не началу всей строки), и вы также были правы, используя (\n|$)
, несмотря на то, что $
соответствует концу каждой строки в /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>
.