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

时间:2022-09-03 21:47:38

There must be a fast and efficient way to split a (text) string at the "nth" occurrence of a needle, but I cannot find it. There is a fairly full set of functions in the strpos comments in the PHP manual, but that seems a bit much for what I need.

必须有一种快速有效的方法在针的“第n”次出现时分割(文本)字符串,但我找不到它。 PHP手册中的strpos注释中有一套相当完整的函数,但这似乎对我需要的东西有点多。

I have plain text as $string, and want to split it at nth occurrence of $needle, and in my case, needle is simply a space. (I can do the sanity checks!)

我有纯文本作为$ string,并希望在第n次出现$ needle时拆分它,在我的情况下,needle只是一个空格。 (我可以做理智检查!)

Can someone point me in the right direction? Many thanks!

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

9 个解决方案

#1


19  

Could be?

可能?

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;
}

#2


9  

If your needle will always be 1 character, use Galled's answer, it's going to be faster by quite a bit. If your $needle is a string, try this. Seems to work fine.

如果你的针总是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));
}

#3


7  

Personally I'd just split it into an array with explode, and then implode the first n-1 parts as the first half, and implode the remaining number as the second half.

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

#4


5  

Here's an approach that I would prefer over a regexp solution (see my other answer):

这是一种我更喜欢正则表达式解决方案的方法(参见我的其他答案):

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

Just call it by:

只需拨打:

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

Output:

输出:

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

#5


1  

You can use something like the following:

您可以使用以下内容:

/* 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;
}

Then you just use the mysplit function, you'll get an array with two substrings. First containing all characters up to the nth occurrence of the needle (not included), and second from the nth occurrence of the needle (included) to the end.

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

#6


1  

This is ugly, but seems to work:

这很难看,但似乎有效:

$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);

Output:

输出:

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"
}

Replace the single spaces in the query with a single character you wish to split on. This expression won't work as-is with multiple characters as the delimiter.

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

This is hard coded for every 3rd space. With a little tweaking, probably could be easily adjusted. Although a str_repeat to build a dynamic expression would work as well.

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

#7


1  

I've edited Galled's function to make it explode after every nth occurences in stead of just the first one.

我编辑了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;
}

#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;
}

#1


19  

Could be?

可能?

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;
}

#2


9  

If your needle will always be 1 character, use Galled's answer, it's going to be faster by quite a bit. If your $needle is a string, try this. Seems to work fine.

如果你的针总是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));
}

#3


7  

Personally I'd just split it into an array with explode, and then implode the first n-1 parts as the first half, and implode the remaining number as the second half.

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

#4


5  

Here's an approach that I would prefer over a regexp solution (see my other answer):

这是一种我更喜欢正则表达式解决方案的方法(参见我的其他答案):

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

Just call it by:

只需拨打:

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

Output:

输出:

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

#5


1  

You can use something like the following:

您可以使用以下内容:

/* 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;
}

Then you just use the mysplit function, you'll get an array with two substrings. First containing all characters up to the nth occurrence of the needle (not included), and second from the nth occurrence of the needle (included) to the end.

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

#6


1  

This is ugly, but seems to work:

这很难看,但似乎有效:

$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);

Output:

输出:

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"
}

Replace the single spaces in the query with a single character you wish to split on. This expression won't work as-is with multiple characters as the delimiter.

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

This is hard coded for every 3rd space. With a little tweaking, probably could be easily adjusted. Although a str_repeat to build a dynamic expression would work as well.

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

#7


1  

I've edited Galled's function to make it explode after every nth occurences in stead of just the first one.

我编辑了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;
}

#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;
}