216

I am trying to extract a substring. I need some help with doing it in PHP.

Here are some sample strings I am working with and the results I need:

home/cat1/subcat2 => home

test/cat2 => test

startpage => startpage

I want to get the string till the first /, but if no / is present, get the whole string.

I tried,

substr($mystring, 0, strpos($mystring, '/'))

I think it says - get the position of / and then get the substring from position 0 to that position.

I don't know how to handle the case where there is no /, without making the statement too big.

Is there a way to handle that case also without making the PHP statement too complex?

  • 2
    You might find [`s($str)->beforeFirst('/')`](https://github.com/delight-im/PHP-Str/blob/8fd0c608d5496d43adaa899642c1cce047e076dc/src/Str.php#L389) helpful, as found in [this standalone library](https://github.com/delight-im/PHP-Str). – caw Jul 27 '16 at 00:20

14 Answers14

399

The most efficient solution is the strtok function:

strtok($mystring, '/')

NOTE: In case of more than one character to split with the results may not meet your expectations e.g. strtok("somethingtosplit", "to") returns s because it is splitting by any single character from the second argument (in this case o is used).

@friek108 thanks for pointing that out in your comment.

For example:

$mystring = 'home/cat1/subcat2/';
$first = strtok($mystring, '/');
echo $first; // home

and

$mystring = 'home';
$first = strtok($mystring, '/');
echo $first; // home
jmarceli
  • 19,102
  • 6
  • 69
  • 67
  • 33
    This is also about 40% faster than the `current-explode` solution. (Not that I use it so often that it matters.) – towr Oct 09 '14 at 10:30
  • 6
    This should be the excepted answer. Definitely faster and more memory and CPU cycles efficient then any of the `explode` solutions given. – Shivam Maheshwari Jul 06 '16 at 20:04
  • 1
    The problem with `strtok` is `first/second` will return `first` but `/second` will return `second` rather than an empty string. – rybo111 Sep 27 '16 at 09:34
  • Further to my above comment: *The behavior when an empty part was found changed with PHP 4.1.0. The old behavior returned an empty string, while the new, correct, behavior simply skips the part of the string* – rybo111 Sep 27 '16 at 09:46
  • Brilliant. Much cleaner than using explode and then having to index into the array. Preferred solution. – xtempore Aug 11 '18 at 02:53
  • 4
    This is not that safe in some circumstances - for example try this: `echo strtok("history_table_id","_id"); `. It returns simply "h". Be careful! – friek108 Apr 16 '20 at 23:12
  • 1
    This however handles differently depending on if the string by which is split, is longer than 1 character. – Ken Sep 30 '20 at 08:54
302

Use explode()

$arr = explode("/", $string, 2);
$first = $arr[0];

In this case, I'm using the limit parameter to explode so that php won't scan the string any more than what's needed.

gnud
  • 77,584
  • 5
  • 64
  • 78
  • 3
    +1 Thanks for the answer. It worked :) But one question. I am only able to do this -> `$arr = explode('/',$mystring,2); echo $arr[0];`. I am unable to get the first string in one statement itself - `echo explode('/',$mystring,2)[0];`. Since explode returns an array, I should be able to do it right? But I get an error. Any suggestions? –  Dec 20 '09 at 14:17
  • 1
    Php doesn't like you indexing into return values from functions. – gnud Dec 20 '09 at 14:20
  • 3
    oh. okay. would have been nice if it was possible. –  Dec 20 '09 at 14:22
  • `list($first,) = explode("/", $string, 2);` – Fluffy Sep 02 '10 at 08:23
  • 40
    `explode() + [0]` is a long-winded way to write `strtok($string, "/")` – mario Mar 24 '11 at 09:06
  • Isn't it overkill to create a whole new array? That's what `explode` does. – rvighne Jan 20 '14 at 05:55
  • 7
    by the way, you can do explode("/", $string, 2)[0] in php 5.5 – But those new buttons though.. Oct 24 '14 at 15:40
  • 3
    @mario The problem with `strtok()` is `first/second` will return `first` but `/second` will return `second` rather than an empty string. – rybo111 Sep 27 '16 at 09:40
103
$first = explode("/", $string)[0];
hakre
  • 193,403
  • 52
  • 435
  • 836
AndyBrandy
  • 1,065
  • 1
  • 7
  • 3
  • 4
    elegant, but not very efficient in computer time. But these days folks don't care much anymore about the minute CPU cycles – Dennis Oct 06 '14 at 19:59
21

What about this :

substr($mystring.'/', 0, strpos($mystring, '/'))

Simply add a '/' to the end of mystring so you can be sure there is at least one ;)

IRCF
  • 211
  • 2
  • 2
14

Late is better than never. php has a predefined function for that. here is that good way.

strstr

if you want to get the part before match just set before_needle (3rd parameter) to true http://php.net/manual/en/function.strstr.php

function not_strtok($string, $delimiter)
{    
    $buffer = strstr($string, $delimiter, true);

    if (false === $buffer) {
        return $string;
    }

    return $buffer;
}

var_dump(
    not_strtok('st/art/page', '/')
);
hakre
  • 193,403
  • 52
  • 435
  • 836
aimme
  • 6,385
  • 7
  • 48
  • 65
13

One-line version of the accepted answer:

$out=explode("/", $mystring, 2)[0];

Should work in php 5.4+

hakre
  • 193,403
  • 52
  • 435
  • 836
111
  • 1,788
  • 1
  • 23
  • 38
9

This is probably the shortest example that came to my mind:

list($first) = explode("/", $mystring);

1) list() will automatically assign string until "/" if delimiter is found
2) if delimiter "/"is not found then the whole string will be assigned

...and if you get really obsessed with performance, you may add extra parameter to explode explode("/", $mystring, 2) which limits maximum of the returned elements.

pavlovich
  • 1,935
  • 15
  • 20
  • 2
    I like this approach. Saves making an unnecessary array. And `strtok()` is unsafe. – rybo111 Sep 27 '16 at 09:43
  • 1
    @rybo111: What is unsafe about `strtok()`? – hakre May 19 '17 at 07:36
  • @rybo111 I echo hakre's sentiment; what makes `strtok()` unsafe? – Doktor J Jun 29 '17 at 13:58
  • @hakre See my replies to jmarceli's answer. It can cause unexpected behaviour depending on the PHP version. – rybo111 Jun 29 '17 at 17:44
  • 1
    php 4.1.0 - rly? – hakre Jun 29 '17 at 17:46
  • @hakre If you're > 4.1.0 (and I hope you are) then the behaviour might not be what you expect, because it'll skip empty tokens. – rybo111 Jun 29 '17 at 17:54
  • A token can not be empty, and [that behavior is documented](http://pubs.opengroup.org/onlinepubs/009695399/functions/strtok.html). The token always starts at the first byte *not* a delimiter. That is, there is never an empty token. This is useful, especially for path separators when you always want the first segment or a further on segment. `///first//////second/third/` – hakre Jun 29 '17 at 18:20
  • @hakre The title of this question says "Get the string before the first '/'" - `strtok` will not strictly do that (as in your example), which is my point. – rybo111 Jun 29 '17 at 19:09
  • The OP gives example which state that the first token has a non-zero length and strtok does exactly that for the OPs case - so if you talk about strictness, there is no unexpected behaviour of strtok here even if you skip the docs. – hakre Jun 29 '17 at 21:15
5

You can try using a regex like this:

$s = preg_replace('|/.*$|', '', $s);

sometimes, regex are slower though, so if performance is an issue, make sure to benchmark this properly and use an other alternative with substrings if it's more suitable for you.

hakre
  • 193,403
  • 52
  • 435
  • 836
t00ny
  • 71
  • 1
  • 2
    Regex is probably overkill too, but since I say the same about `explode` it would be interesting to see which is faster. – rvighne Jan 20 '14 at 05:57
  • 1
    there's absolutely nothing wrong with this and I can't think of why it would deserve 2 downvotes. I personally wouldn't use regex but it's certainly not wrong. – But those new buttons though.. Sep 02 '16 at 02:45
5

The function strstr() in PHP 5.3 should do this job.. The third parameter however should be set to true..

But if you're not using 5.3, then the function below should work accurately:

function strbstr( $str, $char, $start=0 ){
    if ( isset($str[ $start ]) && $str[$start]!=$char ){
        return $str[$start].strbstr( $str, $char, $start+1 );
    }
}

I haven't tested it though, but this should work just fine.. And it's pretty fast as well

Ronald
  • 49
  • 1
  • 1
  • 3
    +1 for `strstr()`, but be aware that it returns `false` if the string doesn't contain `$needle`, thus the `explode()` solutions above are a better fit in this case. – BenMorel Apr 24 '13 at 11:04
3

Using current on explode would ease the process.

 $str = current(explode("/", $str, 2));
user1518659
  • 2,198
  • 9
  • 29
  • 40
3

You could create a helper function to take care of that:

/**
 * Return string before needle if it exists.
 *
 * @param string $str
 * @param mixed $needle
 * @return string
 */
function str_before($str, $needle)
{
    $pos = strpos($str, $needle);

    return ($pos !== false) ? substr($str, 0, $pos) : $str;
}

Here's a use case:

$sing = 'My name is Luka. I live on the second floor.';

echo str_before($sing, '.'); // My name is Luka
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
0
$arr = explode("/", $string, 2); $first = $arr[0];

This Way is better and more accurate than strtok because if you wanna get the values before @ for example while the there's no string before @ it will give you whats after the sign . but explode doesnt

helvete
  • 2,455
  • 13
  • 33
  • 37
Muteb A.
  • 11
  • 1
  • 4
-1

why not use:

function getwhatiwant($s)
{
    $delimiter='/';
    $x=strstr($s,$delimiter,true);
    return ($x?$x:$s);
}

OR:

   function getwhatiwant($s)
   {
       $delimiter='/';
       $t=explode($delimiter, $s);
       return ($t[1]?$t[0]:$s);
   }
user1123382
  • 120
  • 6
  • I see that there is no comment with the dv on this post, so let me explain. If the input string is `0/` then you will get an incorrect result because the ternary is just doing a _falsey_ check. As for your your second snippet, this suffers from something that gnud's answer avoids (4 years earlier) -- the fact that you do not limit the explosions means that potentially more than 2 elements will be created, but you only need the first one. This code-only answer can safely be removed. – mickmackusa Jul 03 '21 at 08:30
  • Actually the ternary here is very nice and concise. This is a very useful method for strings that are known not to be "0$delimiter". Such as a date from which it is desired to remove sub seconds portion. The OP doesn't specify any string criteria. I found this to be very useful for what I needed. Glad it wasn't removed. I would one line it though. `$substr = strstr($string, $delimiter, true) ?: $string;` – NOYB Apr 21 '22 at 23:40
-1
$string="kalion/home/public_html";

$newstring=( stristr($string,"/")==FALSE ) ? $string : substr($string,0,stripos($string,"/"));
stealthyninja
  • 10,343
  • 11
  • 51
  • 59
kalion
  • 9
  • 1
  • There is absolutely zero benefit in calling a case-insensitive function when the needle is a non-letter. This code-only answer only distracts from better answers on this page, so I am voting to delete. – mickmackusa Jul 03 '21 at 08:33