在字符串中删除特定字符之间的所有内容的PHP函数

38

我对function delete_all_between($char1, $char2, $string)很感兴趣,它会在给定的$string中查找$char1和$char2,并且如果找到了这样的内容,就会清除在这两个字符之间的子字符串,包括$char1和$char2本身。

例如:

$string = 'Some valid and <script>some invalid</script> text!';
delete_all_between('<script>', '</script>', $string);
现在,$string 应该只包含

'Some valid and  text'; //note two spaces between 'and  text'

有人有快速解决方案吗?


这里的最终目的是什么?过滤掉HTML吗? - pp19dd
4
你不希望用这种方法来过滤 HTML 内容。可以使用 strip_tags 来处理。请注意不要改变原意,使翻译更加通俗易懂。 - user229044
@pp19dd,删除特定标签之间的所有内容。 - Miloš Đakonović
2
https://dev59.com/dnM_5IYBdhLWcg3wiDuA - Wahyu Kristianto
9个回答

68
<?php

$string = 'Some valid and <script>some invalid</script> text!';
$out = delete_all_between('<script>', '</script>', $string);
print($out);

function delete_all_between($beginning, $end, $string) {
  $beginningPos = strpos($string, $beginning);
  $endPos = strpos($string, $end);
  if ($beginningPos === false || $endPos === false) {
    return $string;
  }

  $textToDelete = substr($string, $beginningPos, ($endPos + strlen($end)) - $beginningPos);

  return delete_all_between($beginning, $end, str_replace($textToDelete, '', $string)); // recursion to ensure all occurrences are replaced
}

3
但是当一个句子中有多个 <script></script> 时,它只会删除第一个,而不是全部...? - Tomasz
2
非常好,你应该改为 $endPos = strpos($string, $end, $beginningPos); 以获取正确的 $end。 - LFS96
@Tom,你说得完全正确。我也遇到了同样的问题,并像这样解决它:我把这一行代码 $out = delete_all_between('<script>', '</script>', $string); 放在一个while循环中,检查是否在删除一个标签后仍然存在脚本标签,然后重复此操作,直到所有脚本标签都被删除: while (strpos($string, '<script>') !== false) { $out = delete_all_between('<script>', '</script>', $string); } - johnnydoe82
这使得它变得太难了。只需使用下面的一行代码并投票支持它! - Mav2287
3
在处理大型复杂字符串时,使用正则表达式比标准字符串替换要慢得多。建议优先使用标准字符串替换以获得更好的性能。 - GeekTantra
显示剩余2条评论

60
这里是一行代码: preg_replace('/START[\s\S]+?END/', '', $string);
它的作用是将字符串中的STARTEND之间的内容替换为空。感谢另一个SO线程!

1
这非常优雅和简单。 - contool
投票支持非函数解决方案。 - pc_
1
你如何不包括字符本身? - ii iml0sto1
1
最佳选择 :) - Brayan Pastor
我写了一个使用strpos/substr的自定义函数,它有点能用(但它开始突破所有合理的php内存限制),但后来我发现了这个黄金宝藏 :) - Rok Sprogar
为了避免将来的读者对此错误感到困惑,如果应用于原始问题,则需要对 START 或 END 中包含的 / 进行转义,使用 \ 进行转义。或者可以使用 | 作为分隔符代替 /。 - zsalya

1

我认为substr()运行速度过慢。最好的方法是:

return substr($string, 0, $beginningPos) . 
       substr($string, $endPos + strlen($end));

1
实际上,我正在寻找一个函数,它可以给我一个简单而稳定的解决方案来提取TWIG模板中的所有变量。 所提议的正则表达式由于许多原因而无法很好地工作,因此我决定通过仅删除标签之间的所有内容而不是计算标签数量来进行操作 ^_^。
/**
     * deletes ALL the string contents between all the designated characters
     * @param $start - pattern start 
     * @param $end   - pattern end
     * @param $string - input string, 
     * @return mixed - string
     */
    function auxDeleteAllBetween($start, $end, $string) {
        // it helps to assembte comma dilimited strings
        $string = strtr($start. $string . $end, array($start => ','.$start, $end => chr(2)));
        $startPos  = 0;
        $endPos = strlen($string);
        while( $startPos !== false && $endPos !== false){
            $startPos = strpos($string, $start);
            $endPos = strpos($string, $end);
            if ($startPos === false || $endPos === false) {
                $run = false;
                return $string;
            }
            $textToDelete = substr($string, $startPos, ($endPos + strlen($end)) - $startPos);
            $string = str_replace($textToDelete, '', $string);
        }
        return $string;
    }

    /**
     * This function is intended to replace
     * //preg_match_all('/\{\%\s*([^\%\}]*)\s*\%\}|\{\{\s*([^\}\}]*)\s*\}\}/i', $this->_tplSubj, $matchesSubj);
     * which did not give intended results for some reason.
     *
     * @param $inputTpl
     * @return array
     */
    private function auxGetAllTags($inputTpl){
        $inputTpl = strtr($inputTpl, array('}}' => ','.chr(1), '{{' => chr(2)));
        return explode(',',$this->auxDeleteAllBetween(chr(1),chr(2),$inputTpl));
    }


$template = '<style>
td{border-bottom:1px solid #eee;}</style>
<p>Dear {{jedi}},<br>New {{padawan}} is waiting for your approval: </p>
<table border="0">
<tbody><tr><td><strong>Register as</strong></td><td>{{register_as}}, user-{{level}}</td></tr>
<tr><td><strong>Name</strong></td><td>{{first_name}} {{last_name}}</td></tr>...';

print_r($this->auxGetAllTags($template));

1
在我的情况下,first 版本存在问题,这是我更正后的版本(如果 $end 字符串也出现在 $beginning 字符串之前)。
<?php 
$string = 'Some </script> valid and <script>some invalid</script> text!';
$out = delete_all_between('<script>', '</script>', $string);
print($out);

function delete_all_between($beginning, $end, $string) {
    $beginningPos = strpos($string, $beginning);
    $tmpstring = substr($string, $beginningPos);  
    $endPos = strpos($tmpstring, $end);
    if ($beginningPos === false || $endPos === false) {
        return $string;
    }
    $textToDelete = substr($string, $beginningPos, ($endPos + strlen($end)) );
    return delete_all_between($beginning, $end, str_replace($textToDelete, '', $string)); // recursion to ensure all occurrences are replaced
}

欢迎来到stackoverflow。该问题仅显示了一个“signature”如何在$string中执行delete_all_between('<script>', '</script>', $string);, 你的回答提供了一种实现方法。考虑到回答展示的顺序是会变的(用户名也可能不同):当你声称第一版本存在问题时,你指的是什么?有哪些问题,以及如何重现? - greybeard
是的,你说得对,抱歉。请参考链接中所述的版本和帖子评论,如果有许多$end字符串,例如: $string = 'Some </script> valid and <script>some invalid</script> text!'; - AndreaTS

0

我想在PHP中删除标签,因为我想在将HTML解析为DOMDocument之前删除未使用的标签。

这是我使用的代码。 光标在开始时为0。它只是程序中用于递归的。

function delete_all_betweenV2($cursor, $beginning, $end, $string, $retainSelf) {
    echo '>>>> Start '.'<br>';
    $beginningPos = strpos($string, $beginning, $cursor);
    $endPos = strpos($string, $end, $beginningPos);
    
    if ($beginningPos === false || $endPos === false) {
      echo '>>>> End '.'<br>';
      return $string;
    }    
    
    if($endPos >= strlen($string)) {
      echo '>>>> End '.'<br>';
      return $string;
    }
    
    $lenOfBeginning = strlen($beginning);
    $lenOfEnd = strlen($end);
    
    $result = $string;
    if($retainSelf) {
      echo 'b4 input String: '.$string.'<br>';
      echo 'b4 cursor = : '.$cursor. '<br>';
      echo 'b4 string: '.$string. '<br>';
      echo 'b4 beginning Pos: '.$beginningPos.'<br>';
      echo 'b4 end Pos: '.$endPos.'<br>';
      echo 'b4 length to be cut is: '.(($endPos - $lenOfEnd) - $beginningPos).'<br>';
      
      if($cursor > 0) {
        echo 'cursor is greater than 0'.'<br>';
        $textToDelete = substr($string, $beginningPos + $lenOfBeginning, ($endPos - $lenOfEnd) - $beginningPos);
      } else {
        echo 'cursor is NOT greater than 0'.'<br>';
        $textToDelete = substr($string, $beginningPos + $lenOfBeginning, ($endPos - $lenOfEnd) - $beginningPos);
      }
      
      echo 'TextToDelete:'.$textToDelete.'<br>';
        
      //$stringStart = substr($string, 0, $beginningPos + $lenOfBeginning);
      //echo $stringStart.'<br>';
      //$stringTail = substr($string, $endPos, strlen($string));
      //echo $stringTail.'<br>';    
      $result = str_replace($textToDelete, '', $string);
      $cursor = $beginningPos + $lenOfBeginning; // just make sure that the cursor search next character/word
      echo 'After cursor = : '.$cursor. '<br>';
      echo 'After result: '.$result. '<br>';
      echo 'After len of result: '.strlen($result). '<br>';
    } else {
        //$stringStart = substr($string, 0, $beginningPos);
        //echo $stringStart.'<br>';
        //$stringTail = substr($string, $endPos + $lenOfEnd, strlen($string));
        //echo $stringTail.'<br>';
        $cursor = 0;
        
        $textToDelete = substr($string, $beginningPos, ($endPos + $lenOfEnd) - $beginningPos);
        echo 'TextToDelete:'.$textToDelete.'<br>';
        $result = str_replace($textToDelete, '', $string);
    } 
    echo '>>>> End '.'<br>';
    return delete_all_betweenV2($cursor, $beginning, $end, $result, $retainSelf);
}

0
作为对AndreaTS的delete_all_between()的修订,但是为了处理标签属性(例如<script type='abc'>)和处理大小写不敏感,我有以下代码...
    function delete_all_between(string $html, string $tag) {
        $startTag="<$tag ";
        $endTag="</$tag>";
        $html     =str_ireplace("<$tag>", "<$tag >", $html);
        $startPos = stripos($html, $startTag);
        $tmpStr = substr($html, $startPos);
        $endPos = stripos($tmpStr, $endTag);
        if ($startPos === false || $endPos === false) {
            return $html;
        }
        $textToDelete = substr($html, $startPos, ($endPos + strlen($startTag))+1 );
        return delete_all_between(str_replace($textToDelete, '', $html), $tag); // recursion to ensure all occurrences are replaced
    }

请查看 http://sandbox.onlinephpfunctions.com/code/1b984b61cfd1c9cea4c6eef1d765ff387a4cd9e9 以运行此 PHP 沙盒。


0

我有一个字符串,其中包含多个类似于<start>xyz<end>的标签。我可以使用以下代码将其删除:

{{preg_replace('/(\<start>.+?)+(\<end>)/i', "", $string)}}

注意:如果您或是一些特殊字符,如$、@等,请使用反斜杠进行转义,例如\<start>或<end>。因此最终结果应该像这样:

{{preg_replace('/(\\\<start>.+?)+(\\\<end>)/i', "", $string)}}

更精确地说:

{{preg_replace('/(\\$.+?)+(\\@)/i', "", $string)}}


-2

您可以使用双重 str_replace() 函数

$q = str_replace('<script>', '', $string); $p = str_replace('some invalid', '', $q); echo $p;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接