正则表达式:用ol标记替换哈希字符列表

时间:2017-10-26 19:29:04

标签: php

让我先谈谈:我猜我对正则表达式不好。真。在过去的4天里,我试图找出如何替换以下格式:

 # Item number 1
 # Item number 2
 # Item number 3

依此类推,用:

<ol>
   <li>Item number 1</li>
   <li>Item number 2</li>
   <li>Item number 3</li>
</ol>

等等。最初我想用/^\s\d\.\s/mi替换<li>,但我放弃的速度非常快,因为它更复杂。

所以我尝试使用preg_match_all运行一个循环来获取所有可能的组,并将它们替换为html标记。但我做错了什么,我不知道是什么。任何帮助将非常感激。

这是我的代码($_POST使用XHR处理的请求):

$innerhtml = htmlspecialchars(addslashes($_POST['innerhtml']));
$br_nums   = '<br>';
if (strstr($innerhtml, PHP_EOL)) {
    $innerhtml = preg_replace("/\r\n\r\n/", $br_nums, $innerhtml);
}

preg_match_all('/^\s[\#\.]\s.*/mi', $innerhtml, $outmatch);
if (isset($outmatch[0])) {
    $origin_outmatc = $outmatch[0];
    $outmatch       = implode('[\r\n]', $outmatch[0]);
    $original_match = $outmatch;
    $outmatch       = explode('<br>', $outmatch);

    foreach ($outmatch as $key => $match) {
        if (preg_match('/^\<br\>/i', $match) || preg_match('/^\<br\>\[\\r\\n\]/i', $match)) {
            $match = str_replace('<br>', '', preg_replace('/^\[\\r\\n\]/i', '', $match));
        }
    }

    $full_ol = '';
    foreach($outmatch as $ol) {
        $full_ol .= '<ol>';
        $ol       = preg_replace('/^\s[\#\.]\s/', '<li>', str_replace('[\r\n]', '</li>', $ol));
        $full_ol .= $ol;
        $full_ol .= '</ol>';
    }

    $full_ol = str_replace(' # ', '<li>', preg_replace('/(?:$|)\<(?!\/li\>)\/ol\>/i', '</li></ol>', $full_ol));
    $full_ol = preg_replace('/(?:|^)\<ol\>[\r\n]\<\/li\>/i', '<ol>', $full_ol);

    $full_ol = explode('<ol>', $full_ol);
    foreach ($full_ol as $key => $list) {
        if (empty($list)) {
            unset($full_ol[$key]);
            $full_ol = array_values($full_ol);
        }
    }

    foreach ($full_ol as $key => $list) {
        $full_ol[$key] = '<ol>' . $list;
    }

    $original_match = str_replace('<br>', '+SPLIT_HERE+<br>', str_replace('[\r\n]', "\r\n", $original_match));
    $original_match = explode('+SPLIT_HERE+', $original_match);

    foreach ($original_match as $key => $possible_match) {
        if (!preg_match('/^\s\#\s/mi', $possible_match)) {
            unset($original_match[$key]);
            $original_match = array_values($original_match);
        }
    }

    foreach ($full_ol as $key => $possible_match) {
        if (preg_match('/^\<ol\>\<\/li\>\<\/ol\>$/i', $possible_match)) {
            unset($full_ol[$key]);
            $full_ol = array_values($full_ol);
        }
    }

    // Preview
    var_dump($original_match, $full_ol);

    // Replace original with html version
    $innerhtml = str_replace($original_match, $full_ol, $innerhtml);
}

请指导我 - 我怎样才能做得更好(或者至少做得对)?我很沮丧......谢谢。

2 个答案:

答案 0 :(得分:0)

此代码应该有效。用phptester.net测试。

如果您有任何疑问,请记录下来:)

<?php

$innerhtml = "
Hallo 123,

this is a little test

 # jeah
 # huhu
 # third

 line beetween?

 # okay lets do this again
 # second
 # final

 what about u?
";

$br_nums   = '<br>';

// with that code it is not working. because double line breaks means normaly <p>
// if (strstr($innerhtml, PHP_EOL)) {
//     $innerhtml = preg_replace("/\r\n\r\n/", $br_nums, $innerhtml);
// }

preg_match_all('/\s*#{1}\s*(.*)\n/', $innerhtml . "\n", $matches); // for last line matching

$olStarted = false;

if (!empty($matches[1])) {
    foreach($matches[1] as $x => $match) {
        $replace = '';

        // start the ol, if is not started already
        if (!$olStarted) {
            $replace .= '<ol>';
            $olStarted = true;
        }

        // build li 
        $replace .= '<li>' . $match . '</li>';

        // end ol when of them is true
        //
        // 1* no next list item is there
        // 2* next list item is there, but a line breaks are between them
        if(
            !isset($matches[0][$x + 1]) || // 1*
            strpos($matches[0][$x + 1], "\n") === 1 // 2*
        ) {
           $replace .= '</ol>';
           $olStarted = false;
        }

        // actually replace the line
        $innerhtml = str_replace($matches[0][$x], $replace, $innerhtml);
    }
}

var_dump($innerhtml);

答案 1 :(得分:-1)

匹配# Item number 1的正则表达式为:

\s*[#]\s+[iI][tT][eE][mM]\s+[nN][uU][mM][bB][eE][rR]\s+\[0-9]+\s*

这意味着:

(0+ spaces)#(1+ spaces)Item(1+ spaces)number(1+ spaces)123(0+ spaces)

例如:" # Item number 12 "

如果文本与此模式匹配,则只匹配下一个模式:

[iI][tT][eE][mM]\s+[nN][uU][mM][bB][eE][rR]\s+\[0-9]+\s*

通过匹配,您可以获得匹配字符串的索引开头。字符串从Match.IndexLength的子字符串,您将拥有此值:

Item number 1

Pd积

如果"Item"可以变为任何字符串,只需写"\w+"而不是"[iI][tT][eE][mM]"。这同样适用于"number"

第二种方式:

匹配主模式:

\s*[#]\s+[iI][tT][eE][mM]\s+[nN][uU][mM][bB][eE][rR]\s+\[0-9]+\s*

找到匹配后,匹配下一个模式:

\s*[#]\s+

现在从"# item number 2"Match.Value.Length的字符串Length - Match.Value.Length子串。在这种情况下,子串从2到"# item number 2"长度 - Match.Value.Length