Разбить строку на массив и добавить предыдущее значение

У меня есть эта строка:

вар/Журнал /file.log

В конце концов я хочу получить массив, похожий на этот:

Array => [
    '1' => 'var',
    '2' => 'var/log',
    '3' => 'var/log/file.log'
]

В настоящее время у меня есть это:

<?php
    $string = 'var/log/file.log';
    $array = explode('/', $string);
    $output = [
        1 => $array[0],
        2 => $array[0]. '/' .$array[1],
        3 => $array[0]. '/' .$array[1]. '/' .$array[2]
    ];

    echo '<pre>'. print_r($output, 1) .'</pre>';

Это кажется действительно нелогичным, и я не уверен, что в PHP уже есть что-то, что может позаботиться об этом.

Как построить массив, используя добавление предыдущего значения?

Ответы

Ответ 1

В этом решении используется подход, начинающийся с вашего входного пути, а затем удаляя путь один за другим, добавляя оставшиеся входные данные в массив на каждом шаге. Затем мы обращаем массив в качестве последнего шага, чтобы сгенерировать желаемый результат.

$input = "var/log/file.log";
$array = [];
while (preg_match("/\//i", $input)) {
    array_push($array, $input);
    $input = preg_replace("/\/[^\/]+$/", "", $input);
    echo $input;
}
array_push($array, $input);
$array = array_reverse($array);
print_r($array);

Array
(
    [0] => var
    [1] => var/log
    [2] => var/log/file.log
)

Вышеуказанный вызов preg_replace удаляет окончательный путь входной строки, включая косую черту. Это повторяется до тех пор, пока не останется только один конечный компонент пути. Затем мы добавляем этот последний компонент в тот же массив.

Ответ 2

<?php
$string = 'var/log/some/other/directory/file.log';
$array = explode('/', $string);

$i = 0;
foreach ($array as $data) {
    $output[] = isset($output) ? $output[$i - 1] . '/' . $data : $data;
    $i++;
}


echo '<pre>';

print_r($output);

Более простое решение выше. Вы просто устанавливаете в своем новом поле массива конкатенацию предыдущего поля из нового массива и текущего из вашего foreach.

Выход:

Array
(
    [0] => var
    [1] => var/log
    [2] => var/log/some
    [3] => var/log/some/other
    [4] => var/log/some/other/directory
    [5] => var/log/some/other/directory/file.log
)

Ответ 3

Вы можете сделать что-то подобное с foreach

<?php
$string = 'var/log/file.log';
$array = explode('/', $string);

$last = '';
$output = array();
foreach ($array as $key => $value) {
    $result = $last.$value;
    $output[$key] = $result;
    $last = $result.'/';
}

echo '<pre>'. print_r($output, 1) .'</pre>';

Ответ 4

Вы можете получить родительский каталог в цикле и добавить его в output переменную. Например с помощью следующего алгоритма:

$path = 'var/log/file.log';
$output = [];

$pos = strlen($path);
while ($pos !== false) {
    $path = substr($path, 0, $pos);
    array_unshift($output, $path);
    $pos = strrpos($path, DIRECTORY_SEPARATOR);
}

или с помощью функции dirname()

$path = 'var/log/file.log';
$output = [];

do {
  array_unshift($output, $path);
  $path = dirname($path);
} while ($path !== '.');

Также вы можете работать со строкой $path как с массивом символов и находить в нем разделитель каталогов:

$path = 'var/log/file.log';
$output = [];

$tmp = '';
$len = strrpos($path, DIRECTORY_SEPARATOR); // you can use strlen instead of strrpos,
                                            // but it'll look over filename also
for ($i = 0; $i < $len; $i++) {        
    if ($path[$i] === DIRECTORY_SEPARATOR) {
        $output[] = $tmp;
    }
    $tmp .= $path[$i];
}
$output[] = $path;

но имейте в виду, что вы не можете использовать этот способ, если строка $path имеет многобайтовую кодировку

Результатом всех методов будет:

Array (
     [0] => var
     [1] => var/log
     [2] => var/log/file.log 
)

Ответ 5

Здесь уже есть много хороших ответов, но здесь есть еще один немного другой способ сделать это:

$string = 'var/log/some/other/directory/file.log';
$array = explode('/', $string);
for ($c = count($array); $c > 0; ) {
    $output[--$c] = implode('/', $array);
    array_pop($array);
}
for ($i = 0; $i < count($output); $i++) {
    echo "$output[$i]\n";
}

Выход:

var 
var/log 
var/log/some 
var/log/some/other 
var/log/some/other/directory 
var/log/some/other/directory/file.log

Демо на 3v4l.org

Ответ 6

В качестве альтернативы:

<?php
declare(strict_types=1);

error_reporting(-1);
ini_set('display_errors', 'On');

$input = 'var/log/file.log';
$output = explode('/', $input);

for ($i = 1, $lim = \count($output); $i < $lim; $i++) {
    $output[$i] = $output[$i - 1] . '/' . $output[$i];
}

print_r($output);

https://3v4l.org/Ng2fJ

Ответ 7

Поскольку я не могу с собой поделать, я оценил все эти ответы. Ответ @Yoshi (удален, но вы можете увидеть код ниже) был довольно ясным, затем следовали @OliverNybo (примерно на 15% медленнее), @pr1nc3 (примерно на 35% медленнее), пробел в шахте и @MaximFedorov сначала и во-вторых ответ (примерно на 55-75% медленнее), затем еще один пробел в @TimBiegeleisen и, наконец, в @MaximFedorov последний ответ (который фактически не возвращал правильный результат). Вот результаты для 100 000 итераций (раз в секундах):

enter image description here

Здесь код тестирования. Примечание. Я удалил вызов array_reverse где он использовался, поскольку он не выполняет ничего, кроме изменения порядка вывода.

<!DOCTYPE html>
<html>
<head>
    <style type="text/css">
        table {
            border-collapse: collapse;align-content:
        }
        td, th {
            border: 1px solid black;
            padding: 5px;
        }
    </style>
</head>
<body>
<pre>
<?php
$string = 'var/log/some/other/directory/file.log';
$elapsed = array();
foreach (array('TimBiegeleisen', 'pr1nc3', 'OliverNybo', 'MaximFedorov1', 'MaximFedorov2', 'MaximFedorov3', 'Nick') as $func) {
    $start = explode(' ', microtime());
    for ($i = 0; $i < 100000; $i++) $func($string);
    $elapsed[$func] = elapsed_time($start);
}
asort($elapsed);
$fastest = min($elapsed);

echo "<table><tr><th>Function</th><th>Elapsed Time</th><th>Delta</tr>";
foreach ($elapsed as $key => $value) {
    echo "<td>$key</td><td>$value</td>";
    echo "<td>" . sprintf("%.0f%%", ($value - $fastest) / $fastest * 100) . "</td></tr>";
}
echo "</table>\n";

function TimBiegeleisen($input) {
    $array = [];
    while (preg_match("/\//i", $input)) {
        array_push($array, $input);
        $input = preg_replace("/\/[^\/]+$/", "", $input);
    }
    array_push($array, $input);
    return $array;
//  return array_reverse($array);   
}

function pr1nc3($string) {
    $array = explode('/', $string);

    $i = 0;
    foreach ($array as $data) {
        $output[] = isset($output) ? $output[$i - 1] . '/' . $data : $data;
        $i++;
    }
    return $output;
}

function OliverNybo($string) {
    $array = explode('/', $string);

    $last = '';
    $output = array();
    foreach ($array as $key => $value) {
        $result = $last.$value;
        $output[$key] = $result;
        $last = $result.'/';
    }   
    return $output;
}

function MaximFedorov1($path) {
    $output = [];

    $pos = strlen($path);
    while ($pos !== false) {
        $path = substr($path, 0, $pos);
        array_unshift($output, $path);
        $pos = strrpos($path, '/');
    }
    return $output;
}

function MaximFedorov2($path) {
    $output = [];

    do {
      array_unshift($output, $path);
      $path = dirname($path);
    } while ($path !== '.');
    return $output;
}

function MaximFedorov3($path) {
    $output = [];
    $tmp = '';
    $len = strrpos($path, '/'); // you can use strlen instead of strrpos,
                                                // but it'll look over filename also
    for ($i = 0; $i < $len; $i++) {        
        if ($path[$i] === '/') {
            $output[] = $tmp;
        }
        $tmp .= $path[$i];
    }
    $output[] = $path;
    return $output;
}

function Nick($string) {
    $array = explode('/', $string);
    for ($c = count($array); $c > 0; ) {
        $output[--$c] = implode('/', $array);
        array_pop($array);
    }
    return $output;
//  return array_reverse($output)
}

function Yoshi($input) {
    $output = explode('/', $input);

    for ($i = 1, $lim = \count($output); $i < $lim; $i++) {
        $output[$i] = $output[$i - 1] . '/' . $output[$i];
    }
    return $output;
}

function elapsed_time(array $start) {
    $now = explode(' ', microtime());
    $deltasec = $now[1] - $start[1];
    $deltamsec = (float)$now[0] - (float)$start[0];
    return $deltasec + $deltamsec;
}


?>
</pre>
</body>
</html>