Функция, которую я использую, должна обнаружить первый экземпляр символа, и, если символ повторяется, восстановить повторяющуюся подстроку. Например:
$x = 'fhdfhbc::::dcdcdcuttr482rdvcjv:ducvdk:::chjvdbj'; // ---> function should extract ::::
Я не хочу использовать любой из preg_*
функции, как я избегаю их, когда это возможно (потому что эти функции медленные). Мое решение в настоящее время это:
$char = ":"; // this would be set as necessary
$char_substring = str_repeat($char, strspn(strstr($x, $char), $char)); // yields ---> ::::
Обратите внимание, что вы не можете использовать strrpos
здесь, потому что могут быть (в этом случае) двоеточия на другом конце строки. Ты можешь использовать explode
затем запустите for
или же foreach
цикл, объединяющий пустые места, или какой-то вариант этого:
$explode = explode($char, $x);
$substring = $char; // explode array should have 1 less empty member than the repeated character, so need to start with char count of 1
$emptyEncountered = false;
for($i = 0, $count = count($explode); $i < $count; $i++) {
if ($explode[$i]) {
if ($emptyEncountered) break;
} else {
$emptyEncountered = true;
$substring .= $char;
}
}
echo $substring; // ---> ::::
Есть ли лучший способ, чем использовать preg_ *, цикл for / each или str_repeat (strspn (strstr ()))?
Правильный preg_*
реализация превзойдет explode
подход не только во времени, но и с точки зрения потребления памяти и требуемого распределения.
Единственная реализация, которую я могу придумать, которая является эффективной и которая придерживается ваших ограничений, будет while
цикл:
$substring = '';
$i = strpos($haystack, $needle);
do {
$substring .= $needle;
++$i;
}
while (isset($haystack{$i}) && $haystack{$i} === $needle);
return $substring;
Однако у вас уже есть самая эффективная реализация:
return str_repeat($needle, strspn(strstr($haystack, $needle), $needle));
Это также функционально по своей природе.
В ваших реализациях отсутствует обработка ошибок, поэтому
while
реализация. По моему мнению, это обязательно требуется, но я игнорирую это, потому что вы это делаете.
Результаты на моей машине i7 с Win 10 PHP TS x64 7.1:
$ bench 10000
0.0040609836578369 # str_repeat
0.0044500827789307 # preg_match
0.0046060085296631 # while
0.0050818920135498 # for
0.0052239894866943 # preg_match + preg_quote
0.0079050064086914 # explode
#!/usr/bin/env php
<?php
function bench(callable $cb): void {
global $argv;
$limit = 1000;
if (isset($argv[1]) && is_numeric($argv[1])) {
$limit = (int) $argv[1];
}
elseif (isset($_ENV['LOOP']) && is_numeric($_ENV['LOOP'])) {
$limit = (int) $_ENV['LOOP'];
}
gc_collect_cycles();
gc_disable();
$start = microtime(true);
for ($i = 0; $i < $limit; ++$i) {
$cb();
}
$end = microtime(true);
gc_enable();
gc_collect_cycles();
echo $end - $start, "\n";
}
$haystack = 'fhdfhbc::::dcdcdcuttr482rdvcjv:ducvdk:::chjvdbj';
$needle = ':';
bench(function () use ($haystack, $needle) {
return str_repeat($needle, strspn(strstr($haystack, $needle), $needle));
});
bench(function () use ($haystack, $needle) {
preg_match("/{$needle}{2,}/", $haystack, $matches);
return $matches[0] ?? '';
});
bench(function () use ($haystack, $needle) {
$substring = '';
$i = strpos($haystack, $needle);
do {
$substring .= $needle;
++$i;
}
while (isset($haystack{$i}) && $haystack{$i} === $needle);
return $substring;
});
bench(function () use ($haystack, $needle) {
$substring = '';
for ($i = strpos($haystack, $needle); isset($haystack{$i}) && $haystack{$i} === $needle; ++$i) {
$substring .= $needle;
}
return $substring;
});
bench(function () use ($haystack, $needle) {
$needle = preg_quote($needle, '/');
preg_match("/{$needle}{2,}/", $haystack, $matches);
return $matches[0] ?? '';
});
bench(function () use ($haystack, $needle) {
$explode = explode($needle, $haystack);
$substring = $needle;
$empty = false;
for ($i = 0, $count = count($explode); $i < $count; $i++) {
if ($explode[$i]) {
if ($empty) {
break;
}
}
else {
$empty = true;
$substring .= $needle;
}
}
return $substring;
});
Других решений пока нет …