Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/263.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/html/87.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在php中使用ULs将多组LIs包装在一个字符串中_Php_Html - Fatal编程技术网

如何在php中使用ULs将多组LIs包装在一个字符串中

如何在php中使用ULs将多组LIs包装在一个字符串中,php,html,Php,Html,我从我无法影响的不同来源获取数据(带有一些html的字符串)。字符串包含(但不限于)可视分组的LI元素,但缺少父UL元素。我需要用UL标签包装LI标签组 如果一个字符串中只有一组LI元素,则此方法可以正常工作。我可以很容易地使用DOMDocument,搜索LI标记,并用新创建的UL标记将它们包装起来。不幸的是,可以有多个组,并且组之间的分离没有定义——但总是某种文本或html标记。很容易将这些群体视为人类:) 所以从逻辑上讲,我需要找到一个开头作为组的起点,一个结尾,后面没有另一个开头作为终点,

我从我无法影响的不同来源获取数据(带有一些html的字符串)。字符串包含(但不限于)可视分组的
LI
元素,但缺少父
UL
元素。我需要用
UL
标签包装
LI
标签组

如果一个字符串中只有一组
LI
元素,则此方法可以正常工作。我可以很容易地使用
DOMDocument
,搜索
LI
标记,并用新创建的
UL
标记将它们包装起来。不幸的是,可以有多个组,并且组之间的分离没有定义——但总是某种文本或html标记。很容易将这些群体视为人类:)

所以从逻辑上讲,我需要找到一个开头
  • 作为组的起点,一个结尾
  • ,后面没有另一个开头
  • 作为终点,忽略所有空格

    示例源字符串可以是(它不总是有新行,也不总是那么漂亮):

    它工作正常,直到它不工作。例如,如果某些
    LI
    -块已经具有包装
    UL
    :)


    由于纯文本未被视为子节点,因此我所有的
    DOMDocument
    方法都失败。这意味着我能够找到
    LI
    s并检查它们的兄弟姐妹是否
    LI
    s,然后将它们全部包装在
    UL
    中(如果后一种情况适用)。但是,如果
    LI
    -组仅由一些没有任何
    HTML
    标记的文本分隔,则所有
    LI
    都被视为没有任何分隔的直接同级。

    我不会使用正则表达式解析HTML(我们都看到这一点,所以回答:-p)

    下面是一个逐行分解文本的解决方案:

    <?php
    
    function isLi($line) {
        return strstr($line, '<li');
    }
    
    $text = 'Some text
    <strong>Some other text</strong>
    <li>Element A1</li><li>Element A2</li>
    <li>Element A3</li>
    Text that separates group A from group B
    <li>Element B1</li>
    
    <li>Element B2</li> <li>Element B3</li>
    <li>Element B4</li>
    <strong>Element that separates group B from group C</strong>
    <li>Element C1</li>
    <li>Element C2</li>
    Text can follow.
    <li>Hello, nothing follows this</li>';
    
    $array = explode("\n", $text);
    
    $html = '';
    $previousWasLi = false;
    
    foreach ($array as $line) {
        if (empty($line)) {
            continue;
        }
        if (isLi($line) && $previousWasLi == false) {
            $html .= "<ul>\n";
            $html .= $line ."\n";
            $previousWasLi = true;
        } elseif (isLi($line) && $previousWasLi == true) {
            $html .= $line ."\n";
            $previousWasLi = true;
        } elseif (!isLi($line) && $previousWasLi == true) {
            $html .= "</ul>\n";
            $html .= $line ."\n";
            $previousWasLi = false;
        } elseif (!isLi($line) && $previousWasLi == false) {
            $html .= $line ."\n";
        }
    }
    
    // if the last line was an li, we need to close the ul
    if ($previousWasLi) {
        $html .= '</ul>';
    }
    
    echo $html;
    
    在使用@delboy1978uk解决方案之前,您可以使用以下代码“几乎完全格式化”代码1步骤:

    <?php
    // $code_to_split is your code
    $text = implode("\n<li", explode('<li', implode("</li>\n", explode('</li>', $code_to_split))));
    function fnIsComplete($totest){
        return (strpos(' '.$totest, '</li>')>0);
    }
    // use @delboy1978uk solution over $text
    // add a param $iscomplete = false as 2° line
    // inserting a validation rule to know if a line is <li ...  >  </li> complete
    // add a test at } elseif (!isLi($line) && $previousWasLi == true) { block
    } elseif (!isLi($line) && $previousWasLi == true) {
        if($iscomplete ){
            $html .= "</ul>\n";
            $html .= $line ."\n";
            $previousWasLi = false;
        }elseif(fnIsComplete($line)) {
            $html .= $line ."\n";
            $html .= "</ul>\n";
            $previousWasLi = false;
        }else{
            $html .= $line ."\n";
        }
    }
    // and when you set $previousWasLi = true; you set also $iscomplete
    $previousWasLi = true; $iscomplete = fnIsComplete($line);
    

    我能想到的最简单的解决方案是:

  • 通过用
    • 替换每个
    • 和用
        替换每个
      • 将每个
      • 包装成
          标签
          
        • 删除所有的
        ,后面跟着
        ,忽略中间的所有空格和换行符
      • 代码应尽可能简单:

        // first step
        $txt = str_replace('<li>', '<ul><li>', $source_txt);
        $txt = str_replace('</li>', '</li></ul>', $txt);
        
        // second step
        $txt = preg_replace('/<\/ul>\s*<ul>/', '', $txt);
        
        //第一步
        $txt=str_replace(“
      • ”、“
        • ”、$source_txt); $txt=str_replace(“
        • ”、“
        ”、$txt); //第二步 $txt=preg_replace('/\s*
          /',''$txt);
      • 如果@Pilan在评论中提到,
      • 已经被
          包装,您可以添加第三个步骤,删除
            ,然后是另一个
              ,然后是另一个

                // third step
                $txt = preg_replace('/<ul>\s*<ul>/', '<ul>', $txt);
                $txt = preg_replace('/<\/ul>\s*<\/ul>/', '</ul>', $txt);
                
                //第三步
                $txt=preg_replace('/
                  \s*
                    /','
                      ',$txt); $txt=preg_replace('/\s*/','
                    ',$txt);
                最好是将过程分割成更小的步骤

                • 查找所有
                  li
                  标记
                • 根据它们之间的文本对它们进行分组
                • 注入
                  ul
                  标签
                它为您提供了更大的灵活性,比如修复丢失的结束标记

                class LiFormatter{
                    public $html;
                    private $lis;
                    private $groups;
                
                    public function __construct($html){
                        $this->html = $html;
                        $this->lis = [];
                        $this->groups = [];
                
                        $this->findNextLi(0);
                
                        if(count($this->lis)==0)
                            return;
                
                        $this->determineGroups();
                        $this->wrap();
                    }
                
                    private function findNextLi($offset){
                        $html = $this->html;
                
                        $start_index = strpos($html,'<li>',$offset);
                
                        if($start_index===false)
                            return;
                
                        $end_index = strpos($html,'</li>',$start_index+4);
                        $next_index = strpos($html,'<li>',$start_index+4);
                
                        if($next_index!==false && $next_index<$end_index){
                            // handle missing closing tag
                            $this->insertAt('</li>',$next_index);
                            $end_index = $next_index;
                        }
                
                        $this->lis[] = ['start' => $start_index, 'end'=>$end_index+5];
                
                        $this->findNextLi($end_index);
                    }
                
                    private function determineGroups(){
                        while(count($this->lis)>0){
                
                            $last_li = array_shift($this->lis);
                            $group = [$last_li];
                
                            while(count($this->lis)>0){
                                $current_li = $this->lis[0];
                                $str_between = substr($this->html,$last_li['end'],$current_li['start']-$last_li['end']);
                
                                if($this->isSeperating($str_between)){
                                    break;
                                }else{
                                    $group[] = $current_li;
                                    array_shift($this->lis);
                                    $last_li = $current_li;
                                }
                            }
                
                            $this->groups[] = $group;
                        }
                    }
                
                    private function wrap(){
                        $offset = 0;
                
                        foreach ($this->groups as $group) {
                            $first_li = reset($group);
                            $last_li = end($group);
                
                            $group_start = $first_li['start'];
                            $group_end = $last_li['end'];
                
                            $this->insertAt('<ul>',$group_start + $offset);
                            $offset += 4;
                
                            $this->insertAt('</ul>',$group_end + $offset);
                            $offset += 5;
                        }
                    }
                
                    private function insertAt($str,$index){
                        $this->html = substr($this->html,0,$index) . $str . substr($this->html,$index);
                    }
                
                    private function isSeperating($str){
                        return preg_match("/\w/", $str);
                    }
                }
                

                正则表达式是的,请

                如果愿意,可以将其移植到PHP。仅用于JS中的演示目的

                var响应=“一些文本一些其他文本
              • 元素A1
              • 元素A2元素A3将A组与B组分开的文本
              • 元素B1
              • 元素B2元素B3元素B4元素将B组与C组分开的元素
              • 元素C1
              • 元素C2文本可以跟随。”; var r=响应。替换(/(?\s*)
              • /g,
                • );/
                    变量r=r.替换(/(?!\s*
                  • )/g,
                  );//
                    $(“#结果”).html(r);
              • 
                
                检查当前LI元素的下一个/上一个同级元素是否仍然是LI或其他元素更有意义……我认为最好的解决方案是通过PHP XML DOM解析器()解析HTML字符串然后应用一些算法,比如平衡圆括号问题。我给你的最好建议是组织你的数据源。通过这样做,你将能够循环通过,
                li
                的每个集合将更容易控制/操作。如果可以的话,这将是非常好的。不幸的是,数据源是我无法影响的第三方。谢谢你的支持努力:)我应该提到字符串的格式不是一直都很好。它来自不同的来源。有断行和无断行;新行在
              • 内或没有;等等。但是我会做一些实验,事先整理它-也许这是一个足够的解决方案。啊,我想问你太棒了!无论如何,祝你好运,让我知道你进展如何!@antesoles如果是这样的话,那么你需要更新你的问题以反映需求。有时候,生活可能很简单。很好,但是如果已经有了
                你会在文本中得到
                • ,你必须检查
                    /(?\s*)
                  • /g
                  替换为
                  • /(?!\s*
                  )/g
                  替换为
                -因此这里所有解决方案的组合-良好的团队合作!:D@Pilan:添加了解决这些边缘情况的第三个步骤。我刚刚将所有
              替换为
              占位符标记(以保持组之间的分离),继续第1步和第2步,然后删除了
              标记。我想,结果是相同的,只是有一些微小的速度差异。谢谢。这或多或少是我已经有的解决方案:)
              <?php
              // $code_to_split is your code
              $text = implode("\n<li", explode('<li', implode("</li>\n", explode('</li>', $code_to_split))));
              function fnIsComplete($totest){
                  return (strpos(' '.$totest, '</li>')>0);
              }
              // use @delboy1978uk solution over $text
              // add a param $iscomplete = false as 2° line
              // inserting a validation rule to know if a line is <li ...  >  </li> complete
              // add a test at } elseif (!isLi($line) && $previousWasLi == true) { block
              } elseif (!isLi($line) && $previousWasLi == true) {
                  if($iscomplete ){
                      $html .= "</ul>\n";
                      $html .= $line ."\n";
                      $previousWasLi = false;
                  }elseif(fnIsComplete($line)) {
                      $html .= $line ."\n";
                      $html .= "</ul>\n";
                      $previousWasLi = false;
                  }else{
                      $html .= $line ."\n";
                  }
              }
              // and when you set $previousWasLi = true; you set also $iscomplete
              $previousWasLi = true; $iscomplete = fnIsComplete($line);
              
              // first step
              $txt = str_replace('<li>', '<ul><li>', $source_txt);
              $txt = str_replace('</li>', '</li></ul>', $txt);
              
              // second step
              $txt = preg_replace('/<\/ul>\s*<ul>/', '', $txt);
              
              // third step
              $txt = preg_replace('/<ul>\s*<ul>/', '<ul>', $txt);
              $txt = preg_replace('/<\/ul>\s*<\/ul>/', '</ul>', $txt);
              
              class LiFormatter{
                  public $html;
                  private $lis;
                  private $groups;
              
                  public function __construct($html){
                      $this->html = $html;
                      $this->lis = [];
                      $this->groups = [];
              
                      $this->findNextLi(0);
              
                      if(count($this->lis)==0)
                          return;
              
                      $this->determineGroups();
                      $this->wrap();
                  }
              
                  private function findNextLi($offset){
                      $html = $this->html;
              
                      $start_index = strpos($html,'<li>',$offset);
              
                      if($start_index===false)
                          return;
              
                      $end_index = strpos($html,'</li>',$start_index+4);
                      $next_index = strpos($html,'<li>',$start_index+4);
              
                      if($next_index!==false && $next_index<$end_index){
                          // handle missing closing tag
                          $this->insertAt('</li>',$next_index);
                          $end_index = $next_index;
                      }
              
                      $this->lis[] = ['start' => $start_index, 'end'=>$end_index+5];
              
                      $this->findNextLi($end_index);
                  }
              
                  private function determineGroups(){
                      while(count($this->lis)>0){
              
                          $last_li = array_shift($this->lis);
                          $group = [$last_li];
              
                          while(count($this->lis)>0){
                              $current_li = $this->lis[0];
                              $str_between = substr($this->html,$last_li['end'],$current_li['start']-$last_li['end']);
              
                              if($this->isSeperating($str_between)){
                                  break;
                              }else{
                                  $group[] = $current_li;
                                  array_shift($this->lis);
                                  $last_li = $current_li;
                              }
                          }
              
                          $this->groups[] = $group;
                      }
                  }
              
                  private function wrap(){
                      $offset = 0;
              
                      foreach ($this->groups as $group) {
                          $first_li = reset($group);
                          $last_li = end($group);
              
                          $group_start = $first_li['start'];
                          $group_end = $last_li['end'];
              
                          $this->insertAt('<ul>',$group_start + $offset);
                          $offset += 4;
              
                          $this->insertAt('</ul>',$group_end + $offset);
                          $offset += 5;
                      }
                  }
              
                  private function insertAt($str,$index){
                      $this->html = substr($this->html,0,$index) . $str . substr($this->html,$index);
                  }
              
                  private function isSeperating($str){
                      return preg_match("/\w/", $str);
                  }
              }
              
              $output = (new LiFormatter($input))->html;