【问题标题】:PHP str_replace() with a limit param?PHP str_replace() 带有限制参数?
【发布时间】:2012-01-20 13:35:45
【问题描述】:

str_replace 用替换词替换所有出现的单词。

preg_replace 用替换替换出现的模式,并采用可选的限制参数。

我不需要模式匹配,但想要一个限制参数的便利。我应该使用什么?

【问题讨论】:

  • 字符串仍然是模式,尽管是简单的模式。
  • str_replace 的最后一个参数有什么问题?
  • @Sebastien C. 这个论点是为了完成替换的数量。但它不限制完成的替换次数。

标签: php


【解决方案1】:

有更好的方法来做到这一点

<?
$str = 'abcdef abcdef abcdef';
// pattern, replacement, string, limit
echo preg_replace('/abc/', '123', $str, 1); // outputs '123def abcdef abcdef'
?>

【讨论】:

    【解决方案2】:
    function str_replace2($find, $replacement, $subject, $limit = 0){
      if ($limit == 0)
        return str_replace($find, $replacement, $subject);
      $ptn = '/' . preg_quote($find,'/') . '/';
      return preg_replace($ptn, $replacement, $subject, $limit);
    }
    

    这将允许您限制替换次数。字符串应使用preg_quote 进行转义,以确保任何特殊字符都不会被解释为模式字符。

    Demo, BTW


    如果您有兴趣,here's a version 包含 &amp;$count 参数:

    function str_replace2($find, $replacement, $subject, $limit = 0, &$count = 0){
      if ($limit == 0)
        return str_replace($find, $replacement, $subject, $count);
      $ptn = '/' . preg_quote($find,'/') . '/';
      return preg_replace($ptn, $replacement, $subject, $limit, $count);
    }
    

    【讨论】:

    • 我认为这是正确的,因为它可以满足我的需求。这与我确定的解决方案非常接近,但如果可能的话,我想避免 preg_replace 开销,因为我不需要正则表达式/模式匹配。
    • @editor:嗯,你可以写一个 strpossubstrs 来匹配,但它会做更多的工作(并且可以说更快/花,因为你正在处理在发动机本身之外)。这取决于您,但看到基准比较会很有趣。
    • $count 应该在$limit 之前,以便与str_replace() 向后兼容。此外,您的代码仅支持字符串,而 str_replace() 支持 $search$replace$subject 的数组。
    • @bfrohs:您有权发表您的意见,但我提供的解决方案符合发帖人的需求。此外,没有向后兼容性的要求(也不应该有,尽管相似,但两者在实现上有所不同)最重要的是,如果我错误地假设,我很抱歉,因为你做了一个完整的实现(给你的规格)7 个月后是不必要的。
    • 这与答案是 my 的答案无关。我觉得您的解决方案具有误导性,因为该函数被命名为 str_replace2 (可以使用它来代替 str_replace0 用于“无限制”,而不是标准的 -1;您使用自己的变量名,与str_replace 不匹配;并且您的变量名不一致(缩写为“模式”$ptn 而其余变量使用完整的单词)。是的,这是我的意见,但它也会如果我对您的答案投了赞成票,请发表我的看法。如果您解决了其中一些问题,我很乐意投赞成票:)
    【解决方案3】:
    $str = implode($replace, explode($search, $subject, $count + 1));
    

    快速概念验证:

    $str =
    "To be, or not to be, that is the question:
    Whether 'tis Nobler in the mind to suffer
    The Slings and Arrows of outrageous Fortune,
    Or to take Arms against a Sea of troubles,
    And by opposing end them";
    
    /* Replace the first 2 occurrences of 'to' with 'CAN IT' in $str. */
    echo implode('CAN IT', explode('to', $str, 3));
    

    输出(强调):

    成为,还是可以成为,这是个问题:
    头脑中的贵族是否可以受苦
    令人发指的命运的弹弓和箭,
    或者 拿起武器对抗麻烦之海,
    并通过反对结束他们

    请注意,此方法区分大小写。

    【讨论】:

      【解决方案4】:

      最初来自https://stackoverflow.com/a/11400172/526741

      我编写了一个与str_replace() 100% 向后兼容的函数。也就是说,您可以用str_replace_limit() 替换所有 次出现的str_replace(),而不会搞砸任何事情,即使是那些使用$search$replace 和/或$subject 的数组。

      函数可以完全独立,如果你想用($string===strval(intval(strval($string))))替换函数调用,但我建议不要这样做,因为valid_integer()在处理时是一个相当有用的函数整数作为字符串提供。

      注意:只要有可能,str_replace_limit() 将使用 str_replace(),因此所有对 str_replace() 的调用都可以替换为 str_replace_limit(),而无需担心达到性能。

      用法

      <?php
      $search = 'a';
      $replace = 'b';
      $subject = 'abcabc';
      
      $limit = -1; // No limit
      $new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
      echo $count.' replacements -- '.$new_string;
      

      2 个替换 -- bbcbbc

      $limit = 1; // Limit of 1
      $new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
      echo $count.' replacements -- '.$new_string;
      

      1 个替换 -- bbcabc

      $limit = 10; // Limit of 10
      $new_string = str_replace_limit($search, $replace, $subject, $count, $limit);
      echo $count.' replacements -- '.$new_string;
      

      2 个替换 -- bbcbbc

      功能

      <?php
      
      /**
       * Checks if $string is a valid integer. Integers provided as strings (e.g. '2' vs 2)
       * are also supported.
       * @param mixed $string
       * @return bool Returns boolean TRUE if string is a valid integer, or FALSE if it is not 
       */
      function valid_integer($string){
          // 1. Cast as string (in case integer is provided)
          // 1. Convert the string to an integer and back to a string
          // 2. Check if identical (note: 'identical', NOT just 'equal')
          // Note: TRUE, FALSE, and NULL $string values all return FALSE
          $string = strval($string);
          return ($string===strval(intval($string)));
      }
      
      /**
       * Replace $limit occurences of the search string with the replacement string
       * @param mixed $search The value being searched for, otherwise known as the needle. An
       * array may be used to designate multiple needles.
       * @param mixed $replace The replacement value that replaces found search values. An
       * array may be used to designate multiple replacements.
       * @param mixed $subject The string or array being searched and replaced on, otherwise
       * known as the haystack. If subject is an array, then the search and replace is
       * performed with every entry of subject, and the return value is an array as well. 
       * @param string $count If passed, this will be set to the number of replacements
       * performed.
       * @param int $limit The maximum possible replacements for each pattern in each subject
       * string. Defaults to -1 (no limit).
       * @return string This function returns a string with the replaced values.
       */
      function str_replace_limit(
              $search,
              $replace,
              $subject,
              &$count,
              $limit = -1
          ){
      
          // Set some defaults
          $count = 0;
      
          // Invalid $limit provided. Throw a warning.
          if(!valid_integer($limit)){
              $backtrace = debug_backtrace();
              trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                      '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting an '.
                      'integer', E_USER_WARNING);
              return $subject;
          }
      
          // Invalid $limit provided. Throw a warning.
          if($limit<-1){
              $backtrace = debug_backtrace();
              trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                      '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                      'a positive integer', E_USER_WARNING);
              return $subject;
          }
      
          // No replacements necessary. Throw a notice as this was most likely not the intended
          // use. And, if it was (e.g. part of a loop, setting $limit dynamically), it can be
          // worked around by simply checking to see if $limit===0, and if it does, skip the
          // function call (and set $count to 0, if applicable).
          if($limit===0){
              $backtrace = debug_backtrace();
              trigger_error('Invalid $limit `'.$limit.'` provided to '.__function__.'() in '.
                      '`'.$backtrace[0]['file'].'` on line '.$backtrace[0]['line'].'. Expecting -1 or '.
                      'a positive integer', E_USER_NOTICE);
              return $subject;
          }
      
          // Use str_replace() whenever possible (for performance reasons)
          if($limit===-1){
              return str_replace($search, $replace, $subject, $count);
          }
      
          if(is_array($subject)){
      
              // Loop through $subject values and call this function for each one.
              foreach($subject as $key => $this_subject){
      
                  // Skip values that are arrays (to match str_replace()).
                  if(!is_array($this_subject)){
      
                      // Call this function again for
                      $this_function = __FUNCTION__;
                      $subject[$key] = $this_function(
                              $search,
                              $replace,
                              $this_subject,
                              $this_count,
                              $limit
                      );
      
                      // Adjust $count
                      $count += $this_count;
      
                      // Adjust $limit, if not -1
                      if($limit!=-1){
                          $limit -= $this_count;
                      }
      
                      // Reached $limit, return $subject
                      if($limit===0){
                          return $subject;
                      }
      
                  }
      
              }
      
              return $subject;
      
          } elseif(is_array($search)){
              // Only treat $replace as an array if $search is also an array (to match str_replace())
      
              // Clear keys of $search (to match str_replace()).
              $search = array_values($search);
      
              // Clear keys of $replace, if applicable (to match str_replace()).
              if(is_array($replace)){
                  $replace = array_values($replace);
              }
      
              // Loop through $search array.
              foreach($search as $key => $this_search){
      
                  // Don't support multi-dimensional arrays (to match str_replace()).
                  $this_search = strval($this_search);
      
                  // If $replace is an array, use the value of $replace[$key] as the replacement. If
                  // $replace[$key] doesn't exist, just an empty string (to match str_replace()).
                  if(is_array($replace)){
                      if(array_key_exists($key, $replace)){
                          $this_replace = strval($replace[$key]);
                      } else {
                          $this_replace = '';
                      }
                  } else {
                      $this_replace = strval($replace);
                  }
      
                  // Call this function again for
                  $this_function = __FUNCTION__;
                  $subject = $this_function(
                          $this_search,
                          $this_replace,
                          $subject,
                          $this_count,
                          $limit
                  );
      
                  // Adjust $count
                  $count += $this_count;
      
                  // Adjust $limit, if not -1
                  if($limit!=-1){
                      $limit -= $this_count;
                  }
      
                  // Reached $limit, return $subject
                  if($limit===0){
                      return $subject;
                  }
      
              }
      
              return $subject;
      
          } else {
              $search = strval($search);
              $replace = strval($replace);
      
              // Get position of first $search
              $pos = strpos($subject, $search);
      
              // Return $subject if $search cannot be found
              if($pos===false){
                  return $subject;
              }
      
              // Get length of $search, to make proper replacement later on
              $search_len = strlen($search);
      
              // Loop until $search can no longer be found, or $limit is reached
              for($i=0;(($i<$limit)||($limit===-1));$i++){
      
                  // Replace 
                  $subject = substr_replace($subject, $replace, $pos, $search_len);
      
                  // Increase $count
                  $count++;
      
                  // Get location of next $search
                  $pos = strpos($subject, $search);
      
                  // Break out of loop if $needle
                  if($pos===false){
                      break;
                  }
      
              }
      
              // Return new $subject
              return $subject;
      
          }
      
      }
      

      【讨论】:

        【解决方案5】:

        这是@brad-christie 的答案的增强版本,其中将数组的兼容性宣传为 $find 和 $replace 值:

        function str_replace_limit($find, $replacement, $subject, $limit = 0) {
        
            if ($limit == 0)
              return str_replace($find, $replacement, $subject);
        
            for ($i = 0; $i < count($find); $i++) {
              $find[$i] = '/' . preg_quote($find[$i],'/') . '/';
            }
        
            return preg_replace($find, $replacement, $subject, $limit);
          }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-12-14
          • 2021-12-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多