如何在第n次发生针时在PHP中拆分字符串?

时间:2011-05-10 20:29:36

标签: php string

必须有一种快速有效的方法在针的“第n”次出现时分割(文本)字符串,但我找不到它。 strpos comments in the PHP manual中有一套相当完整的函数,但这似乎对我需要的内容有所帮助。

我的原始文字为$string,并希望在 nth 出现$needle时将其拆分,在我的情况下,needle只是一个空格。 (我可以做理智检查!)

有人能指出我正确的方向吗?非常感谢!

12 个答案:

答案 0 :(得分:19)

可能吗?

function split2($string,$needle,$nth){
$max = strlen($string);
$n = 0;
for($i=0;$i<$max;$i++){
    if($string[$i]==$needle){
        $n++;
        if($n>=$nth){
            break;
        }
    }
}
$arr[] = substr($string,0,$i);
$arr[] = substr($string,$i+1,$max);

return $arr;
}

答案 1 :(得分:9)

如果您的指针总是1个字符,请使用Galled的答案,它会更快一点。如果你的$ needle是一个字符串,试试这个。似乎工作正常。

function splitn($string, $needle, $offset)
{
    $newString = $string;
    $totalPos = 0;
    $length = strlen($needle);
    for($i = 0; $i < $offset; $i++)
    {
        $pos = strpos($newString, $needle);

        // If you run out of string before you find all your needles
        if($pos === false)
            return false;
        $newString = substr($newString, $pos+$length);
        $totalPos += $pos+$length;
    }
    return array(substr($string, 0, $totalPos-$length),substr($string, $totalPos));
}

答案 2 :(得分:8)

就我个人而言,我只是把它拆分成一个爆炸的数组,然后将第一个n-1部分作为上半部分内爆,并将剩下的数字作为下半部分内爆。

答案 3 :(得分:5)

这是一种我更喜欢regexp解决方案的方法(参见我的其他答案):

function split_nth($str, $delim, $n)
{
  return array_map(function($p) use ($delim) {
      return implode($delim, $p);
  }, array_chunk(explode($delim, $str), $n));
}

只需通过以下方式调用:

split_nth("1 2 3 4 5 6", " ", 2);

输出:

array(3) {
  [0]=>
  string(3) "1 2"
  [1]=>
  string(3) "3 4"
  [2]=>
  string(3) "5 6"
}

答案 4 :(得分:1)

您可以使用以下内容:

/* Function copied from the php manual comment you referenced */
function strnripos_generic( $haystack, $needle, $nth, $offset, $insensitive, $reverse )
{
    //  If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
    if( ! is_string( $needle ) ) {
        $needle = chr( (int) $needle );
    }

    //  Are the supplied values valid / reasonable?
    $len = strlen( $needle );
    if( 1 > $nth || 0 === $len ) {
        return false;
    }

    if( $insensitive ) {
        $haystack = strtolower( $haystack );
        $needle   = strtolower( $needle   );
    }

    if( $reverse ) {
        $haystack = strrev( $haystack );
        $needle   = strrev( $needle   );
    }

    //  $offset is incremented in the call to strpos, so make sure that the first
    //  call starts at the right position by initially decreasing $offset by $len.
    $offset -= $len;
    do
    {
        $offset = strpos( $haystack, $needle, $offset + $len );
    } while( --$nth  && false !== $offset );

    return false === $offset || ! $reverse ? $offset : strlen( $haystack ) - $offset;
}

// Our split function
function mysplit ($haystack, $needle, $nth) {
    $position = strnripos_generic($haystack, $needle, $nth, 0, false, false);
    $retval = array();

    if ($position !== false) {
        $retval[0] = substr($haystack, 0, $position-1);
        $retval[1] = substr($haystack, $position);    
        return $retval;
    }

    return false;
}

然后你只需使用mysplit函数,你将获得一个包含两个子串的数组。首先包含直到针的第n次出现的所有字符(未包括),并且从针的第n次出现(包括)到结尾包含第二次。

答案 5 :(得分:1)

这很难看,但似乎有效:

$foo = '1 2 3 4 5 6 7 8 9 10 11 12 13 14';

$parts = preg_split('!([^ ]* [^ ]* [^ ]*) !', $foo, -1,
            PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);

var_dump($parts);

输出:

array(5) {
  [0]=>
  string(5) "1 2 3"
  [1]=>
  string(5) "4 5 6"
  [2]=>
  string(5) "7 8 9"
  [3]=>
  string(8) "10 11 12"
  [4]=>
  string(5) "13 14"
}

将查询中的单个空格替换为您要拆分的单个字符。该表达式不能用多个字符作为分隔符。

这是每第3个空间的硬编码。稍微调整一下,可能很容易调整。虽然构建动态表达式的str_repeat也可以正常工作。

答案 6 :(得分:1)

我编辑了Galled的功能,使其在每第n次出现后爆炸而不是第一次出现。

function split2($string, $needle, $nth) {
  $max = strlen($string);
  $n = 0;
  $arr = array();
  //Loop trough each character
  for ($i = 0; $i < $max; $i++) {

    //if character == needle
    if ($string[$i] == $needle) {
      $n++;
      //Make a string for every n-th needle
      if ($n == $nth) {
        $arr[] = substr($string, $i-$nth, $i);
        $n=0; //reset n for next $nth
      }
      //Include last part of the string
      if(($i+$nth) >= $max) {
        $arr[] = substr($string, $i + 1, $max);
        break;
      }
    }
  }
  return $arr;
}

答案 7 :(得分:1)

接受Matthew's answer并为Dɑvïd's comment添加解决方案:

function split_nth($str, $delim, $n) {
  $result = array_map(function($p) use ($delim) {
      return implode($delim, $p);
  }, array_chunk(explode($delim, $str), $n));
  $result_before_split = array_shift($result);
  $result_after_split = implode(" ", $result); 
  return array($result_before_split, $result_after_split);
}

只需通过以下方式调用它:

list($split_before, $split_after) = split_nth("1 2 3 4 5 6", " ", 2);

输出:

1 2
3 4 5 6

答案 8 :(得分:0)

function strposnth($haystack,$needle,$n){
  $offset = 0;
  for($i=1;$i<=$n;$i++){
    $indx = strpos($haystack, $needle, $offset);
    if($i == $n || $indx === false)
        return $indx;
    else {
        $offset = $indx+1;
    }
  }
  return false;
}

答案 9 :(得分:0)

function split_nth($haystack,$needle,$nth){
    $result = array();
    if(substr_count($haystack,$needle) > ($nth-1)){
        $haystack = explode($needle,$haystack);
        $result[] = implode(array_splice($haystack,0,$nth),$needle);
        $result[] = implode($haystack,$needle);
    }
return $result;
}

答案 10 :(得分:0)

轻松地做到这一点:

$i = $pos = 0;    
do {
        $pos = strpos( $string, $needle, $pos+1 );
} while( $i++ < $nth);

答案 11 :(得分:0)

我非常喜欢Hamze GhaemPanah's answer的简洁性。但是,其中有一个小错误。

在原始代码中:

$i = $pos = 0;    
do {
        $pos = strpos( $string, $needle, $pos+1 );
} while( $i++ < $nth);
$nth循环中的

do while应该替换为($nth-1),因为它会错误地重复一个额外的时间-将$pos设置为{{1}的位置}针的实例。这是一个示例playground to demonstrate。如果此链接失败,则代码如下:

$nth+1

也就是说,当搜索我们的针的第二个实例的位置时,我们的循环将一个额外的时间设置$nth = 2; $string = "44 E conway ave west horse"; $needle = " "; echo"======= ORIGINAL =======\n"; $i = $pos = 0; do { $pos = strpos( $string, $needle, $pos+1 ); } while( $i++ < $nth); echo "position: $pos \n"; echo substr($string, 0, $pos) . "\n\n"; /* Outputs: ======= ORIGINAL ======= position: 11 44 E conway */ echo"======= FIXED =======\n"; $i = $pos = 0; do { $pos = strpos( $string, $needle, $pos+1 ); } while( $i++ < ($nth-1) ); echo "position: $pos \n"; echo substr($string, 0, $pos); /* Outputs: ======= FIXED ======= position: 4 44 E */ 迭代到我们的针的第二个实例的位置。因此,当我们在针的第二个实例上拆分字符串时(如OP要求的那样),我们得到了不正确的子字符串。

对Hamze的巧妙回答表示敬意。

干杯。