6

假设我们有一个非常大的字符串$str,我们需要将它的很大一部分(假设是整个字符串,但没有前 3 个字节)传递给函数。这样做的方法substr

consumer_function(substr($str, 3));

似乎效率不高,因为substr在返回结果之前似乎将字符从初始字符串复制到新字符串中。有什么方法可以将大部分字符串传递给函数而无需过度复制?我们无法更改该函数的代码。

4

4 回答 4

3

我看不出这里有问题。你猜测有问题,你没有理由认为有问题。

“它似乎没有效率”不是问题。如果你测量它并发现它很慢,那么你就有问题了。然后,您在代码上使用XDebug之类的分析器,看看您是否可以找到代码的哪些部分运行缓慢。

如果您没有问题,那么您就无法解决它,并且猜测可能看起来可能很慢的事情并不意味着您有问题。

优化俱乐部规则:

  1. 优化俱乐部的第一条规则是,你不要优化。
  2. 优化俱乐部的第二条规则是,没有衡量就不要优化。
  3. 如果您的应用程序运行速度比底层传输协议快,那么优化就结束了。
  4. 一次一个因素。
  5. 没有市场机器人,没有市场机器人时间表。
  6. 只要需要,测试就会继续进行。
  7. 如果这是您在优化俱乐部的第一个晚上,您必须编写一个测试用例。
于 2013-01-03T02:04:53.320 回答
1

处理你的工作$str,而不必创建另一个变量,你可以这样做:

for($i=1;$i<=$no;$i++) $str[strlen($str)-$i]=null;
$str=rtrim($str);

从它的末尾刮掉最后一个$no字符

并且:

for($i=0;$i<$no;$i++) $str[$i]=null;
$str=ltrim($str);

刮掉它的第一个字符。

更新:

测试 A:从字符串的开头剃掉 30 个字符

测试用例 1: substr($str,30)

52784749 bytes of data
0.72129082679749s execution time
52903844 bytes of ram used

测试用例 2:带有空字符串字符和 ltrim 的循环

52784749 bytes of data
0.23676204681396s execution time
52904276 bytes of ram used

测试 B:从字符串末尾剃掉 30 个字符

测试用例 1: substr($str,0,-30)

52784749 bytes of data
0.83467292785645s execution time
52903924 bytes of ram used

测试用例 2:带有空字符串字符和 rtrim 的循环

52784749 bytes of data
0.27498316764832s execution time
52904340 bytes of ram used

总而言之,当您确实需要这种微优化时,这是一个合理的问题,使用此解决方案可实现3 倍的处理时间,甚至更好(高达 40倍)使用 1.2Mb 的较小数据集。
需要更多的测试,但看起来是一个可行的选择。

更新2:

正如 Grigory 指出的那样,内存在速度上是一个很大的问题,Fergus 注意到 ltrim() 的内存占用:
不幸的是,使用 trim() 将使我们回到原点,在某些时候使用的内存是原来的两倍,而且只有速度增加

另一方面,如果不使用 trim(),我们最终会得到一个长度相同且包含空字符的字符串,但速度会提高节省内存

更新3:

也适用于nullfalse和“ \x08 ”(BackSpace 字符)。
var_dump() 报告字符串的长度与原始字符串的长度相同,但引号中的值是您所期望的:只有您感兴趣的部分。

太糟糕了这个问题[关闭] :(

于 2013-01-03T00:33:03.340 回答
1

“让它发挥作用,然后让它变得完美”

说真的 - 过早的优化不是一个好的方法。除非你觉得肯定有性能影响——一个明显的影响——然后离开它。当你重新访问它时,使用一些很少使用的技巧来做一些非常常见的事情只会导致维护噩梦。

默认情况下没有可用的替代方法 - 如果您查看 PHP 网站上的字符串函数,您可以看到可用的方法。

但是,您可以使用数组表示法处理字符串:

$str[ index ] 

例如:

$str = "abc";
$str[0] // a
$str[1] // b
$str[2] // c

结合unset()它完全可以从字符串中手动取消设置特定项目..

$str = "abc";
unset( $str[1] ); // $str = "ac" now.

抛出一个基本循环,可以使用它;在您的示例中,您想删除 3 - 所以您可以像这样实现它:(注意,数组表示法 = 索引从 0 开始!!)

for( $i=0; $i<=2; $i++ )
  unset( $str[i] );

但是,请记住您丢失了原始字符串 - 因此您以后可能需要任何数据?是的,没了。

但是 - 如果我是你,我会坚持下去substr()

编辑: Grigory 在评论中指出这在 PHP 5.3 中不起作用 - 这很奇怪,因为PHP 文档指出

按字符访问和修改字符串

可以通过使用方数组括号指定字符串后所需字符的从零开始的偏移量来访问和修改字符串中的字符,如 $str[42]。为此,将字符串视为字符数组。当您想要提取或替换超过 1 个字符时,可以使用函数 substr() 和 substr_replace()。

所以这确实是另一个坚持的理由substr()——我现在有点好奇;所以我将尝试看看这种行为是否仅由于unset(). 会回来报告的!

更新:正如预期的那样,这种行为是由于unset()- 实际上我不能说我太惊讶了。

**Fatal errors:** [type:1] -- Cannot unset string offsets -- at line 7

你可以在这里看到我在phpFiddle的测试用例。

因此,总而言之,没有任何字符串函数可以在语言中原生地执行此操作,而且您不能通过逐个字符地操作字符串来做到这一点。坚持推荐的方式。

于 2013-01-03T00:38:47.340 回答
0

基于之前的答案。如果我们可以相信 php 会尝试类似数组的字符串,即没有内部重复,这里还有 2 个解决方案需要测试。请注意,我们$str多次重写原始字符串。

$str         = "abcdefghi"; // a given string 
$set_strip   = 3; // how many chars strip

$strlen  = strlen($str);
$strip   = $strlen - $set_strip;

// test before commit errors    
if ($strip > 0 && $strip <= $strlen)
{
    // SOL. 1.- using str_split with $strip as 2º parameter (trusting strrev() acts efficienly)
    $str             = strrev($str); // reverse string
    $str             = str_split($str, $strip); // split into a array with 2 elements
    $str             = strrev($str[0]); // back to original order

    // SOL. 2.- shortening array $set_strip times
    $str             = str_split($str);
    for ($i = 0; $i < $set_strip; $i ++ )
        array_shift($str);

    $str = implode('', $str); // back to string
}
else
    echo "\$set_strip value not allowed = $set_strip, must be non-negative  and < $strlen";

两者都给了我们字符串:defghi

于 2013-01-03T03:17:47.740 回答