从字符串开头移除一个字符串

192

我有一个看起来像这样的字符串:

$str = "bla_string_bla_bla_bla";

如果在字符串开头找到第一个bla_,我该如何删除它?

使用str_replace()会删除所有bla_


1
你可能会发现s($str)->replacePrefix('_bla')很有用,它可以在这个独立库中找到。 - caw
10个回答

400

不使用正则表达式的普通形式:

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

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

需要时间:0.0369毫秒(0.000,036,954秒)

同时使用:

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

执行时间为:0.1749毫秒(0.000,174,999秒)第一次运行(编译),之后为0.0510毫秒(0.000,051,021秒)。

在我的服务器上进行了性能测试。


8
我从来没有见过三元运算符被滥用得这么糟糕,一个简单的 if(condition) { statement } 会清晰得多。 - Tatu Ulmanen
56
@salathe,我不明白。已经提出了习惯用语和基于正则表达式的解决方案:从效率的角度比较这两个解决方案有助于找到最佳(再次从效率的角度)。为什么这是邪恶的? - cbrandolino
8
@cbrandolino,没有人说这是“邪恶的”。我只是认为这与问题完全无关,就像“这里有两个解决方案,还有一张小猫的图片可以获得更多的赞”一样。 - salathe
9
@salathe的意思是,小猫咪根本不相关,但是用户画像(略微)有一定影响。他甚至没有根据自己的用户画像来加权回答,只是客观地添加了它。话虽如此,如果有两个答案完全相同,一个有小猫咪,一个没有;我会赞成有小猫咪的,谁不会呢?:P - xDaizu
12
可以将 if (substr($str, 0, strlen($prefix)) == $prefix) 改为 if (0 === strpos($str, $prefix)),这样可以避免不必要的内存分配,同时保持可读性不变 :) - xDaizu
显示剩余9条评论

84

你可以使用正则表达式中的脱字符号(^),它将匹配锚定在字符串的开头:

$str = preg_replace('/^bla_/', '', $str);

我想知道它是否比 substr() 版本更快... 我猜它是,应该标记为正确答案。 - Flash Thunder
除非它被 preg_quote 包含,否则返回翻译后的文本。 - vp_arth
2
我认为这对程序员的眼睛来说更加舒适,更加直观。即使它在性能上输给了其他建议的解决方案(我真的很怀疑),我仍然更喜欢这个。 - Fr0zenFyr
"multibyte" 的噩梦是其他解决方案的另一个问题,而如果文件的编码正确,则此解决方案运行良好。无论如何,这不应该是这个问题的范围,所以我不会在意。 - Fr0zenFyr
回来提一下,这个方法还有一个额外的好处,就是可以处理字符串数组。substrstrpos无法接受一个数组作为参数。所以如果你需要处理一个数组,那么使用这个方法肯定会带来明显的性能提升。祝好! - Fr0zenFyr
它对我不起作用。 - Rachid Loukili

27
function remove_prefix($text, $prefix) {
    if(0 === strpos($text, $prefix))
        $text = substr($text, strlen($prefix)).'';
    return $text;
}

16
不需要使用“.''”。 - NateS
1
说实话,既然每个人似乎都在微调,根据我的计算,这个方法始终是最快的。100万次迭代只需要0.17秒,而被接受的答案中的(substr($str, 0, strlen($prefix)) == $prefix)则需要大约0.37秒。 - But those new buttons though..
但是strpos会一直查找整个字符串直到最后,只有在找到匹配项后,才会将位置与零进行比较。这是错误的。 如果你有一百万个连续的字母,并且想知道前三个是否是"ABC",你告诉你的狗"去找到某个地方的ABC",当它从一英里的路程走回来时,你会说:"哦,我想要它在第一个位置!"之所以对你来说看起来很快,是因为你在测试短字符串。 - undefined

10
在PHP 8+中,我们可以通过使用str_starts_with()函数来简化操作:
$str = "bla_string_bla_bla_bla";
$prefix = "bla_";
if (str_starts_with($str, $prefix)) {
  $str = substr($str, strlen($prefix));
}

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

编辑:修正示例代码中的括号错误。


6
这里。
$array = explode("_", $string);
if($array[0] == "bla") array_shift($array);
$string = implode("_", $array);

2
测量时间为0.0000459153秒 :) - Fabio Mora
速度很快,但这是硬编码依赖于针尾带有“_”。是否有通用版本? - toddmo
1
我不会使用explode(),但如果你_必须_使用,那么你应该使用它的limit参数。这个答案缺少了有关教育方面的解释。 - mickmackusa

5

这里有一个更快的方法:

// strpos is faster than an unnecessary substr() and is built just for that 
if (strpos($str, $prefix) === 0) $str = substr($str, strlen($prefix));

2

这里有很多不同的答案,似乎都是基于字符串分析。以下是我使用PHP的explode将字符串拆分为仅包含两个值的数组,并干净地返回第二个值的方法:

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

输出结果如下:
string_bla_bla_bla

1
不错的速度,但是这个代码硬编码依赖于针对下划线结尾。是否有通用版本?- toddmo 6月29日23:26
一个通用版本:
$parts = explode($start, $full, 2);
if ($parts[0] === '') {
    $end = $parts[1];
} else {
    $fail = true;
}

一些基准测试:

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

在我的相当老的家用电脑上:
$ php bench.php

输出:

   strpos+strlen : 0.158388 s
         explode : 0.126772 s

1

Symfony用户可以安装字符串组件并使用trimPrefix()

u('file-image-0001.png')->trimPrefix('file-');           // 'image-0001.png'

-1

1
substr_replace 将替换给定范围内的字符,无论它们是您想要删除的前缀还是其他内容。 OP 希望仅在字符串开头找到 bla_ 时才删除它。 - Olivier 'Ölbaum' Scherler
这个回答是误导性的,价值低下。如果你只想要放链接,你可以在问题下面提供评论。str_replace()的计数参数并不能让你限制替换的次数。 - mickmackusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接