Вопрос по string, file, php – Как соединить строки пути файловой системы в PHP?

62

Есть ли в PHP встроенная функция для интеллектуального объединения строк пути? Функция, указав в качестве аргументов "abc / de /" и "/fg/x.php", должна возвращать "abc / de / fg / x.php"; тот же результат должен быть получен с использованием "abc / de" и "fg / x.php" в качестве аргументов для этой функции.

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

Можно всегда использовать "/", я пишу только для Linux.

В Питоне естьos.path.join(), это здорово.

@ Tibo и его комментарии: нет, этот пример вообще не вводит в заблуждение, за исключением случаев, когда кто-то игнорирует суть вопроса и настаивает на переоценке ссылки на Python, что является незначительным замечанием для ориентации. Вопрос о том, чего он хочет, совершенно ясен:н в точкуos.path.join. Читайте: «Функция, заданнаяabc/de/ а также/fg/x.php в качестве аргумента, должен возвращатьabc/de/fg/x.php». Sz.
Вау, я только что пришел сюда из Google. Не могу поверить, что PHP такой. Dmitry Minkovsky
Обратите внимание, что ваш пример вводит в заблуждение, посколькуos.path.join('some/relative/path, '/an/absolute/path') всегда вернет/an/absolute/path. Так что вы либо ищетеos.path.join замена (затем исправьте ваш пример) или что-то близкое к нему, за исключением того, что абсолютные пути, идущие на втором (или n-м) пути, рассматриваются как относительные пут Tibo

Ваш Ответ

17   ответов
106
function join_paths() {
    $paths = array();

    foreach (func_get_args() as $arg) {
        if ($arg !== '') { $paths[] = $arg; }
    }

    return preg_replace('#/+#','/',join('/', $paths));
}

Рассмотри эти тесты

array               my version    @deceze      @david_miller    @mark

['','']             ''            ''           '/'              '/'
['','/']            '/'           ''           '/'              '/'
['/','a']           '/a'          'a'          '//a'            '/a'
['/','/a']          '/a'          'a'          '//a'            '//a'
['abc','def']       'abc/def'     'abc/def'    'abc/def'        'abc/def'
['abc','/def']      'abc/def'     'abc/def'    'abc/def'        'abc//def'
['/abc','def']      '/abc/def'    'abc/def'    '/abc/def'       '/abc/def'
['','foo.jpg']      'foo.jpg'     'foo.jpg'    '/foo.jpg'       '/foo.jpg'
['dir','0','a.jpg'] 'dir/0/a.jpg' 'dir/a.jpg'  'dir/0/a.jpg'    'dir/0/a.txt'
Это лучший ответ, так как он наилучшим образом соответствует вопросу - он наиболее близок кos.path.join и делает разумно присоединяйся к путям. Ответ можно улучшить, добавив «эталонную» реализациюos.path.join и с указанием спецификаций OP, нарушающих правило (контрольный пример['abc','/def'] неправильно w.r.tos.path.join, но в соответствии с вопросом). Tibo
Всегда должен использоватьDIRECTORY_SEPARATOR ... Qix
@ qix послушай, я понимаю твою точку зрения, обычно я с тобой согласен, но реально PHP не будет работать на платформах, которые в ближайшее время не используют косую черту в качестве разделителя пути, и использовать константу preg_replace ( ) стал бы настоящим беспорядком (вам нужно было бы избежать разделителя пути в регулярном выражении), поэтому я выбрал этот компромисс. Riccardo Galli
То есть вы срезаете углы, потому что ленивы, хотя интерполяция строк в PHP практически бесплатна? Tsk. Qix
Как заметка, моя команда только что нашла случай, когда/ разделители не работают (старая установка PHP5.3.4 на Windows Server 2012 с использованием оболочки msys git). Félix Saparelli
46

а комментарии заполняются «предложениями по функциям» или «сообщениями об ошибках» ... Все, что делает этот фрагмент кода, - это объединение двух строк с косой чертой без дублирования косых черт между ними. Вот и все. Ни больше ни меньше. Он не оценивает фактические пути на жестком диске и не сохраняет начальный слеш (добавьте его обратно, если необходимо, по крайней мере, вы можете быть уверены, что этот код всегда возвращает строкубе начальная косая черта).

join('/', array(trim("abc/de/", '/'), trim("/fg/x.php", '/')));

Конечным результатом всегда будет путь без косых черт в начале или конце и без двойных косых черт внутри. Не стесняйтесь сделать из этого функцию.

EDIT: Вот хорошая гибкая оболочка функции для приведенного выше фрагмента. Вы можете передать столько фрагментов пути, сколько захотите, либо в виде массива, либо в виде отдельных аргументов:

function joinPaths() {
    $args = func_get_args();
    $paths = array();
    foreach ($args as $arg) {
        $paths = array_merge($paths, (array)$arg);
    }

    $paths = array_map(create_function('$p', 'return trim($p, "/");'), $paths);
    $paths = array_filter($paths);
    return join('/', $paths);
}

echo joinPaths(array('my/path', 'is', '/an/array'));
//or
echo joinPaths('my/paths/', '/are/', 'a/r/g/u/m/e/n/t/s/');

: О)

Должно ли это использовать DIRECTORY_SEPARATOR вместо '/'? Dave
@ fe_ Это совершенно другая функция по сравнению с тем, о чем спрашивал вопрос. deceze♦
function pj ($ a, $ b) {return rtrim ($ a, '/'). '/'. итрим ($ b, '/'); } user89021
Это не всегда работает, как описано. joinPaths ('', 'foo.jpg') становится '/foo.jpg'. Я заметил это после того, как мой файловый менеджер php начал записывать загруженные пользователем файлы в корень файловой системы! Исправленная версия должна удалить все пути, которые являются пустыми строками. Dwayne
Я не согласен, так как человек четко уведомил, что он привык к результатам, полученным от python'sos.path.join который дает этот результат и который он находит отличным. Поэтому я не верю, что это другая функция. Как толькоjoin('/a/b','../c') должен вернуть/a/c без необходимости внешней нормализации. fe_lix_
17

к пути, который начинается с абсолютного пути Unix, напримерjoinPaths('/var/www', '/vhosts/site');.

function unix_path() {
  $args = func_get_args();
  $paths = array();

  foreach($args as $arg) {
    $paths = array_merge($paths, (array)$arg);
  }

  foreach($paths as &$path) {
    $path = trim($path, '/');
  }

  if (substr($args[0], 0, 1) == '/') {
    $paths[0] = '/' . $paths[0];
  }

  return join('/', $paths);
}
14

Я возьму:

function trimds($s) {
    return rtrim($s,DIRECTORY_SEPARATOR);
}

function joinpaths() {
    return implode(DIRECTORY_SEPARATOR, array_map('trimds', func_get_args()));
}

Я бы использовал анонимную функцию дляtrimds, но старые версии PHP не поддерживают его.

Пример

join_paths('a','\\b','/c','d/','/e/','f.jpg'); // a\b\c\d\e\f.jpg (on Windows)

ОбновленоАпрель 201 Марта 2014 Май 2018:

function join_paths(...$paths) {
    return preg_replace('~[/\\\\]+~', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, $paths));
}

Этот исправит любые косые черты в соответствии с вашей ОС, не удалит начальную косую черту, а также очистит и несколько косых черт подряд.

Это всегда создает абсолютный путь, но, по крайней мере, кто-то упоминал DIRECTORY_SEPARATOR ... Karoly Horvath
4

Альтернатива используетimplode() а такжеexplode().

$a = '/a/bc/def/';
$b = '/q/rs/tuv/path.xml';

$path = implode('/',array_filter(explode('/', $a . $b)));

echo $path;  // -> a/bc/def/q/rs/tuv/path.xml
Попробуй URL/offset/0/limit/1. Danon
4

Если вы знаете, что файл / каталог существует, вы можете добавить дополнительные косые черты (которые могут быть ненужными), затем позвоните Realpath, т.е.

realpath(join('/', $parts));

Это, конечно, не совсем то же самое, что версия Python, но во многих случаях может быть достаточно хорошей.

2

http: //nz2.php.net/manual/en/function.pathinfo.ph

для присоединения ответ от @deceze выглядит хорошо

2

Другой способ атаковать этот:

function joinPaths() {
  $paths = array_filter(func_get_args());
  return preg_replace('#/{2,}#', '/', implode('/', $paths));
}
1

опубликованной deceze. Без этого изменения joinPaths ('', 'foo.jpg') становится '/foo.jpg'

function joinPaths() {
    $args = func_get_args();
    $paths = array();
    foreach ($args as $arg)
        $paths = array_merge($paths, (array)$arg);

    $paths2 = array();
    foreach ($paths as $i=>$path)
    {   $path = trim($path, '/');
        if (strlen($path))
            $paths2[]= $path;
    }
    $result = join('/', $paths2); // If first element of old path was absolute, make this one absolute also
    if (strlen($paths[0]) && substr($paths[0], 0, 1) == '/')
        return '/'.$result;
    return $result;
}
1

кажется, работает очень хорошо, и выглядит довольно аккуратно для меня.

private function JoinPaths() {
  $slash = DIRECTORY_SEPARATOR;
  $sections = preg_split(
          "@[/\\\\]@",
          implode('/', func_get_args()),
          null,
          PREG_SPLIT_NO_EMPTY);
  return implode($slash, $sections);
}
1

function joinPaths($leftHandSide, $rightHandSide) { 
    return rtrim($leftHandSide, '/') .'/'. ltrim($rightHandSide, '/'); 
}

ПРИМЕЧАНИЕ: скопировано из комментария user89021

1

предложенная @RiccardoGalli, но она улучшена для использованияDIRECTORY_SEPARATOR constant, как предложили @Qix и @ FélixSaparelli, и, что более важно, трим каждый данный элемент во избежание появления в окончательном пути имен папок, содержащих только пробелы (в моем случае это требовалось).

Относительно экранирования разделителя каталогов внутриpreg_replace() шаблон, как вы можете видеть, я использовалpreg_quote() функция, которая отлично справляется со своей работой.
Кроме того, я бы заменил множественные разделителиолько @ (квантификатор RegExp{2,}).

// PHP 7.+
function paths_join(string ...$parts): string {
    $parts = array_map('trim', $parts);
    $path = [];

    foreach ($parts as $part) {
        if ($part !== '') {
            $path[] = $part;
        }
    }

    $path = implode(DIRECTORY_SEPARATOR, $path);

    return preg_replace(
        '#' . preg_quote(DIRECTORY_SEPARATOR) . '{2,}#',
        DIRECTORY_SEPARATOR,
        $path
    );
}
0

Вот функция, которая ведет себя как Node'spath.resolve:

function resolve_path() {
    $working_dir = getcwd();
    foreach(func_get_args() as $p) {
        if($p === null || $p === '') continue;
        elseif($p[0] === '/') $working_dir = $p;
        else $working_dir .= "/$p";
    }
    $working_dir = preg_replace('~/{2,}~','/', $working_dir);
    if($working_dir === '/') return '/';
    $out = [];
    foreach(explode('/',rtrim($working_dir,'/')) as $p) {
        if($p === '.') continue;
        if($p === '..') array_pop($out);
        else $out[] = $p;
    }
    return implode('/',$out);
}

Тестовые случаи:

resolve_path('/foo/bar','./baz')         # /foo/bar/baz
resolve_path('/foo/bar','/tmp/file/')    # /tmp/file
resolve_path('/foo/bar','/tmp','file')   # /tmp/file
resolve_path('/foo//bar/../baz')         # /foo/baz
resolve_path('/','foo')                  # /foo
resolve_path('/','foo','/')              # /
resolve_path('wwwroot', 'static_files/png/', '../gif/image.gif') 
                                  # __DIR__.'/wwwroot/static_files/gif/image.gif'
0

чтобы избежать уничтожения префикса протокола.

Идея состоит в том, чтобы проверить наличие протокола в одном аргументе и сохранить его в результате. ВНИМАНИЕ: это наивная реализация!

Например

array("http://domain.de","/a","/b/")

приводит к (ведение протокола)

"http://domain.de/a/b/"

вместо (протокол убийства)

"http:/domain.de/a/b/"

Ноhttp: //codepad.org/hzpWmpz нужен лучший навык написания кода.

0

Ответ Риккардо и я думаю, что это лучший ответ.

Я использую его для объединения путей в @ U Building, но с одним небольшим изменением для обработки двойного слеша протоколов:

function joinPath () {
    $paths = array();

    foreach (func_get_args() as $arg) {
        if ($arg !== '') { $paths[] = $arg; }
    }

    // Replace the slash with DIRECTORY_SEPARATOR
    $paths = preg_replace('#/+#', '/', join('/', $paths));
    return preg_replace('#:/#', '://', $paths);
}
0

к пути.

Этот код не использует ненужный массив.

Мультиплатформная
function os_path_join(...$parts) {
  return preg_replace('#'.DIRECTORY_SEPARATOR.'+#', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, array_filter($parts)));
}
Unix-системы
function os_path_join(...$parts) {
  return preg_replace('#/+#', '/', implode('/', array_filter($parts)));
}
Система на основе Unix без параметров REST (не соблюдайте явную философию PEP8):
function os_path_join() {
  return preg_replace('#/+#', '/', implode('/', array_filter(func_get_args())));
}
Использовани
$path = os_path_join("", "/", "mydir/", "/here/");
Бонус: если вы действительно хотите следовать Python os.path.join (). Первый аргумент обязателен:
function os_path_join($path=null, ...$paths) {
  if (!is_null($path)) {
    throw new Exception("TypeError: join() missing 1 required positional argument: 'path'", 1);
  }
  $path = rtrim($path, DIRECTORY_SEPARATOR);
  foreach ($paths as $key => $current_path) {
    $paths[$key] = $paths[$key] = trim($current_path, DIRECTORY_SEPARATOR);
  }
  return implode(DIRECTORY_SEPARATOR, array_merge([$path], array_filter($paths)));
}

Проверьте источник os.path.join (), если хотите:https: //github.com/python/cpython/blob/master/Lib/ntpath.p

-6

кто заменяет все '/ +' на '/' (регулярные выражения), забывают, что os.path.join () из python может обрабатывать такие соединения:

os.path.join('http://example.com/parent/path', 'subdir/file.html')

Result: 'http: //example.com/parent/path/subdir/file.htm'

Вопрос задан для PHP, ответ для Python Toilal

Похожие вопросы