Использование стиля текста для создания списка с подсписками


Я пытаюсь сделать разметку для форматирования упорядоченного списка, вот стиль разметки:

$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 не были созданы как подсписок, а просто проигнорированы. Я чувствую, что приближаюсь к ответу, но я не уверен, что нужно сделать, чтобы добраться до этого ответ...

Примечание Я хотел бы разрешить неограниченное количество подсписков

Author: Get Off My Lawn, 2013-10-15

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>.

 2
Author: Andrew Cheong, 2013-10-15 19:34:16