Remove a string from the beginning of a string
Asked Answered
Y

11

197

I have a string that looks like this:

$str = "bla_string_bla_bla_bla";

How can I remove the first bla_; but only if it's found at the beginning of the string?

With str_replace(), it removes all bla_'s.

Youthful answered 23/12, 2010 at 8:40 Comment(1)
You might find s($str)->replacePrefix('_bla') helpful, as found in this standalone library.Meanly
H
404

Plain form, without regex:

$prefix = 'bla_';
$str = 'bla_string_bla_bla_bla';

if (substr($str, 0, strlen($prefix)) == $prefix) {
    $str = substr($str, strlen($prefix));
} 

Takes: 0.0369 ms (0.000,036,954 seconds)

And with:

$prefix = 'bla_';
$str = 'bla_string_bla_bla_bla';
$str = preg_replace('/^' . preg_quote($prefix, '/') . '/', '', $str);

Takes: 0.1749 ms (0.000,174,999 seconds) the 1st run (compiling), and 0.0510 ms (0.000,051,021 seconds) after.

Profiled on my server, obviously.

Hubbell answered 23/12, 2010 at 9:11 Comment(14)
why is the first run more expensive? :PYouthful
I've never seen the ternary operator abused so badly, a simple if(condition) { statement } would have been so much clearer.Monogamist
@salathe: I accept the criticism. It was a personal curiosity to see what was the fastest.Hubbell
@salathe, I don't get it. Both idiomatic and regex-based solutions were proposed: comparing the two in terms of efficiency helps finding the best (again in terms of efficiency) answer. Why is that evil?Sugden
@cbrandolino, no-one said it was evil. I just thought it entirely irrelevant to the question; much like "here are two solutions, and here's a picture of some kittens for more upvotes" would be.Chronometry
regex needs escape: preg_replace('/^' . preg_quote($prefix,'/') . '/', '', $str);Vardar
Shouldn't it be $str = substr($str, strlen($prefix)); rather than $str = substr($str, strlen($prefix), strlen($str)); ?Luik
@KurtZhong Yes, it should be. The 3rd argument (length) can be omitted, but furthermore strlen($str) is always going to exceed the resulting length - strlen(prefix) character count. The 2-argument substr should be used here.Nosedive
@Chronometry The kittens would not be relevant at all, the profiling (marginally) is. He didn't even weighted the answer based on his profiling, just added it objectively. That being said, given two identical answers, one with kittens, one without; I'd upvote the kittens, who wouldn't? :PAudie
if (substr($str, 0, strlen($prefix)) == $prefix) can be changed for if (0 === strpos($str, $prefix)) to avoid unnecessary memory allocation while keeping the same readability :)Audie
Might want to do a explicit compairson (===) because substr may return falseSentimentalize
How come no one uses ltrim($str, $prefix) ?Jenniejennifer
@LasseRafn ltrim has a different purpose, it matches against chars, not strings. E.g. php -r "print ltrim('foo_bar', 'of_');" heads to bar anyway.Hubbell
Since php 8.0 you can use str_starts_with() instead of the substr.. construct...: php.net/manual/de/function.str-starts-with.phpGamages
M
87

You can use regular expressions with the caret symbol (^) which anchors the match to the beginning of the string:

$str = preg_replace('/^bla_/', '', $str);
Monogamist answered 23/12, 2010 at 8:43 Comment(6)
I wonder if it works faster than substr() version... I guess it does, and should be marked as proper answer.Kepner
except it must be preg_quote'dOffspring
I think this is much less painful to the eyes of a programmer and more intuitive. Even if it loses in performance to another suggested solution (which I really doubt), I'd still prefer this.Roundabout
multibyte nightmare is another issue with other solutions while this works well if the encoding of the file is correct. Anyway, it shouldn't be in the scope of this question so I wouldn't care.Roundabout
Came back to mention that this has an additional benefit of working with an array of subject strings. substr and strpos can't accept an array. There you go, a definite performance gain if you are dealing with an array. Cheers!Roundabout
it doesn't work for mePeroxy
H
28
function remove_prefix($text, $prefix) {
    if(0 === strpos($text, $prefix))
        $text = substr($text, strlen($prefix)).'';
    return $text;
}
Hirohito answered 19/5, 2013 at 18:47 Comment(3)
The .'' isn't needed.Hoopes
For what it's worth since every one seems to be micro-optimizing here, this one is consistently fastest by my count. 1 million iterations came in at .17 sec, whereas (substr($str, 0, strlen($prefix)) == $prefix) from the accepted answer was more like .37Scarfskin
But strpos looks through all the string till the end, and only then, if it finds a match,, only then, you compare the position to zero. This is just wrong. If you have a million of letters in a row and want to know if the first three are "ABC" you tell your dog "Go find ABC somewhere" and when it comes back, from a mile walking, you say: "oh know I wanted it in the first position!". The reason it looks fast to you is beacuse you are testing with short strings.Tyrolienne
M
15

In PHP 8+ we can simplify using the str_starts_with() function:

$str = "bla_string_bla_bla_bla";
$prefix = "bla_";
if (str_starts_with($str, $prefix)) {
  $str = substr($str, strlen($prefix));
}

https://www.php.net/manual/en/function.str-starts-with.php

EDIT: Fixed a typo (closing bracket) in the example code.

Merell answered 20/7, 2022 at 12:0 Comment(0)
G
8

Here's an even faster approach:

// strpos is faster than an unnecessary substr() and is built just for that 
if (strpos($str, $prefix) === 0) $str = substr($str, strlen($prefix));
Goodrow answered 11/11, 2019 at 16:22 Comment(0)
S
6

Here.

$array = explode("_", $string);
if($array[0] == "bla") array_shift($array);
$string = implode("_", $array);
Sugden answered 23/12, 2010 at 9:5 Comment(3)
Measure 0.0000459153 seconds :)Hubbell
Nice speed, but this is hard-coded to depend on the needle ending with _. Is there a general version?Hickory
I would not use explode(), but if you have to, then you should use its limit parameter. This answer is missing its educational explanation.Cowpoke
N
1

Nice speed, but this is hard-coded to depend on the needle ending with _. Is there a general version? – toddmo Jun 29 at 23:26

A general version:

$parts = explode($start, $full, 2);
if ($parts[0] === '') {
    $end = $parts[1];
} else {
    $fail = true;
}

Some benchmarks:

<?php

$iters = 100000;
$start = "/aaaaaaa/bbbbbbbbbb";
$full = "/aaaaaaa/bbbbbbbbbb/cccccccccc/dddddddddd/eeeeeeeeee";
$end = '';

$fail = false;

$t0 = microtime(true);
for ($i = 0; $i < $iters; $i++) {
    if (strpos($full, $start) === 0) {
        $end = substr($full, strlen($start));
    } else {
        $fail = true;
    }
}
$t = microtime(true) - $t0;
printf("%16s : %f s\n", "strpos+strlen", $t);

$t0 = microtime(true);
for ($i = 0; $i < $iters; $i++) {
    $parts = explode($start, $full, 2);
    if ($parts[0] === '') {
        $end = $parts[1];
    } else {
        $fail = true;
    }
}
$t = microtime(true) - $t0;
printf("%16s : %f s\n", "explode", $t);

On my quite old home PC:

$ php bench.php

Outputs:

   strpos+strlen : 0.158388 s
         explode : 0.126772 s
Neolith answered 14/10, 2018 at 17:54 Comment(0)
C
1

Lots of different answers here. All seemingly based on string analysis. Here is my take on this using PHP explode to break up the string into an array of exactly two values and cleanly returning only the second value:

$str = "bla_string_bla_bla_bla";
$str_parts = explode('bla_', $str, 2);
$str_parts = array_filter($str_parts);
$final = array_shift($str_parts);
echo $final;

Output will be:

string_bla_bla_bla
Chivalric answered 18/6, 2022 at 1:57 Comment(0)
K
1

Symfony users can install the string component and use trimPrefix()

u('file-image-0001.png')->trimPrefix('file-');           // 'image-0001.png'
Karinkarina answered 14/1, 2023 at 13:30 Comment(0)
C
0

I generally don't have an appetite for techniques that need 3 function calls or generate and array to produce a string when a single regex function will do.

I want to mention that if you use pattern delimiters that are covered in the default mask of preg_quote(), then you won't need to nominate the second parameter (# is one of those characters).

Documenation: https://www.php.net/manual/en/function.preg-quote.php

The special regular expression characters are: . \ + * ? [ ^ ] $ ( ) { } = ! < > | : - #

Note that / is not a special regular expression character.

Code: (Demo)

$str = preg_replace('#^' . preg_quote($prefix) . '#'), '', $str);

For completeness, I want to add to this page that sscanf() can be a suitable technique so long as you are in "control" of the prefix string and can confidently assign all characters after the prefix to a placeholder. In the asker's sample input, there are no spaces in the input string, so %s will suffice. If there are any spaces in the substring after the prefix, then %s is no good -- you might use a negated expression with a character that will not occur like %[^~] or similar. This technique will modify the input string by reference if needed. (Demo)

sscanf($str, "$prefix%s", $str);

I'll admit that sscanf() isn't the most intuitive tool for this task and there are scenarios where it is unsuitable, but for the presented task the single function call works as desired. For cases when you have a prefix followed by a number, sscanf() with %d in the format parameter will auto-cast the matched string as an integer. So there may be some scenarios where it is more ideal than a preg_ function call.

Cowpoke answered 12/3 at 5:5 Comment(0)
H
-1

I think substr_replace does what you want, where you can limit your replace to part of your string: https://www.php.net/manual/en/function.substr-replace.php (This will enable you to only look at the beginning of the string.)

You could use the count parameter of str_replace ( https://www.php.net/manual/en/function.str-replace.php ), this will allow you to limit the number of replacements, starting from the left, but it will not enforce it to be at the beginning.

Hurst answered 23/12, 2010 at 9:22 Comment(2)
substr_replace will replace the characters in the given range regardless of whether they’re the prefix you want to remove or something else. The OP wants to remove bla_ “only if it's found at the beginning of the string.”Killigrew
This answer is misleading and low-value. If you just wanted to drop links, you could have just offered a comment under the question. str_replace()'s count parameter DOES NOT allow you to limit the number of replacements.Cowpoke

© 2022 - 2024 — McMap. All rights reserved.