Gibt es in PHP eine integrierte Funktion zum intelligenten Verbinden von Pfadzeichenfolgen? Die angegebene Funktion abc/de/
und /fg/x.php
als Argumente sollten zurückgegeben werden abc/de/fg/x.php
. Das gleiche Ergebnis sollte mit abc/de
und fg/x.php
als Argumente für diese Funktion angegeben werden.
Wenn nicht, gibt es eine verfügbare Klasse? Es kann auch nützlich sein, um Pfade zu teilen oder Teile davon zu entfernen. Wenn Sie etwas geschrieben haben, können Sie Ihren Code hier teilen?
Es ist in Ordnung, immer zu verwenden /
, ich codiere nur für Linux.
In Python gibt es das os.path.join
, was großartig ist.
os.path.join('some/relative/path, '/an/absolute/path')
es immer wiederkommt/an/absolute/path
. Sie suchen also entweder nach einemos.path.join
Ersatz (und korrigieren dann Ihr Beispiel) oder etwas in der Nähe davon, mit der Ausnahme, dass absolute Pfade, die an zweiter Stelle (oder an n-ter Stelle stehen) als relative Pfade behandelt werden.os.path.join
. Lesen Sie: "Die Funktion, angegebenabc/de/
und/fg/x.php
als Argumente, sollte zurückgebenabc/de/fg/x.php
".Antworten:
join('/', array(trim("abc/de/", '/'), trim("/fg/x.php", '/')));
Das Endergebnis ist immer ein Pfad ohne Schrägstriche am Anfang oder Ende und ohne doppelte Schrägstriche. Fühlen Sie sich frei, daraus eine Funktion zu machen.
EDIT: Hier ist ein schöner flexibler Funktions-Wrapper für das obige Snippet. Sie können so viele Pfadausschnitte übergeben, wie Sie möchten, entweder als Array oder als separate Argumente:
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/');
:Ö)
quelle
os.path.join
die dieses Ergebnis liefern und die sie großartig findet. Ich glaube also nicht, dass es eine andere Funktion ist. Genau so, wiejoin('/a/b','../c')
es/a/c
ohne externe Normalisierung zurückkehren sollte.function join_paths() { $paths = array(); foreach (func_get_args() as $arg) { if ($arg !== '') { $paths[] = $arg; } } return preg_replace('#/+#','/',join('/', $paths)); }
Meine Lösung ist einfacher und ähnlicher wie Python os.path.join
Betrachten Sie diese Testfälle
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'
quelle
os.path.join
und verbindet sie auf intelligente Weise . Die Antwort könnte verbessert werden, indem die "Referenz" -Implementierung von hinzugefügtos.path.join
wird und die spezifischen OP-['abc','/def']
Werte angegeben werdenos.path.join
, die gegen die Regel verstoßen (Testfall ist falsch , aber gemäß der Frage richtig)./
Trennzeichen nicht funktionieren (eine alte PHP5.3.4-Installation auf einem Windows Server 2012 unter Verwendung der msys git-Shell).Die Funktion von @ deceze behält nicht den führenden / bei, wenn versucht wird, einem Pfad beizutreten, der mit einem absoluten Unix-Pfad beginnt, z
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); }
quelle
Meine Einstellung:
function trimds($s) { return rtrim($s,DIRECTORY_SEPARATOR); } function joinpaths() { return implode(DIRECTORY_SEPARATOR, array_map('trimds', func_get_args())); }
Ich hätte eine anonyme Funktion für verwendet
trimds
, aber ältere Versionen von PHP unterstützen sie nicht.Beispiel:
join_paths('a','\\b','/c','d/','/e/','f.jpg'); // a\b\c\d\e\f.jpg (on Windows)
Aktualisiert
April 2013Marz 2014Mai 2018 :function join_paths(...$paths) { return preg_replace('~[/\\\\]+~', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, $paths)); }
Dieser korrigiert alle Schrägstriche entsprechend Ihrem Betriebssystem, entfernt keinen führenden Schrägstrich und bereinigt und mehrere Schrägstriche hintereinander.
quelle
Wenn Sie wissen, dass die Datei / das Verzeichnis vorhanden ist , können Sie zusätzliche Schrägstriche hinzufügen (die möglicherweise nicht erforderlich sind) und dann realpath aufrufen , d. H.
realpath(join('/', $parts));
Dies ist natürlich nicht ganz dasselbe wie die Python-Version, kann aber in vielen Fällen gut genug sein.
quelle
Eine Alternative ist die Verwendung von
implode()
undexplode()
.$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
quelle
/offset/0/limit/1
.Die folgende Lösung verwendet die von @RiccardoGalli vorgeschlagene Logik, wird jedoch verbessert, um die
DIRECTORY_SEPARATOR
Konstante zu nutzen, wie von @Qix und @ FélixSaparelli vorgeschlagen, und, was noch wichtiger ist, um jedes gegebene Element zu trimmen zu um zu vermeiden, dass im Finale nur Leerzeichen- Pfad (es war eine Anforderung in meinem Fall).preg_replace()
Wie Sie sehen, habe ich in Bezug auf das Escapezeichen des Verzeichnis-Trennzeichens innerhalb des Musters diepreg_quote()
Funktion verwendet, die die Aufgabe gut erledigt.Außerdem würde ich nur mehrere Trennzeichen ersetzen (RegExp-Quantifizierer
{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 ); }
quelle
Um Teile von Pfaden abzurufen, können Sie pathinfo http://nz2.php.net/manual/en/function.pathinfo.php verwenden
für die Teilnahme an der Antwort von @deceze sieht gut aus
quelle
Eine andere Art, diesen anzugreifen:
function joinPaths() { $paths = array_filter(func_get_args()); return preg_replace('#/{2,}#', '/', implode('/', $paths)); }
quelle
Dies ist eine korrigierte Version der von deceze geposteten Funktion. Ohne diese Änderung wird joinPaths ('', 'foo.jpg') zu '/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; }
quelle
Das scheint ganz gut zu funktionieren und sieht für mich einigermaßen ordentlich aus.
private function JoinPaths() { $slash = DIRECTORY_SEPARATOR; $sections = preg_split( "@[/\\\\]@", implode('/', func_get_args()), null, PREG_SPLIT_NO_EMPTY); return implode($slash, $sections); }
quelle
Beste gefundene Lösung:
function joinPaths($leftHandSide, $rightHandSide) { return rtrim($leftHandSide, '/') .'/'. ltrim($rightHandSide, '/'); }
HINWEIS: Aus dem Kommentar von user89021 kopiert
quelle
Betriebssystemunabhängige Version basierend auf der Antwort von mpen, jedoch in einer einzigen Funktion zusammengefasst und mit der Option, ein Trennzeichen für nachfolgende Pfade hinzuzufügen.
function joinPathParts($parts, $trailingSeparator = false){ return implode( DIRECTORY_SEPARATOR, array_map( function($s){ return rtrim($s,DIRECTORY_SEPARATOR); }, $parts) ) .($trailingSeparator ? DIRECTORY_SEPARATOR : ''); }
Oder für Sie Einzeiler-Liebhaber:
function joinPathParts($parts, $trailingSeparator = false){ return implode(DIRECTORY_SEPARATOR, array_map(function($s){return rtrim($s,DIRECTORY_SEPARATOR);}, $parts)).($trailingSeparator ? DIRECTORY_SEPARATOR : ''); }
Nennen Sie es einfach mit einer Reihe von Pfadteilen:
// No trailing separator - ex. C:\www\logs\myscript.txt $logFile = joinPathParts([getcwd(), 'logs', 'myscript.txt']); // Trailing separator - ex. C:\www\download\images\user1234\ $dir = joinPathParts([getcwd(), 'download', 'images', 'user1234'], true);
quelle
Elegante Python-inspirierte PHP-Einzeiler-Möglichkeit, sich dem Pfad anzuschließen.
Dieser Code verwendet kein unnötiges Array.
Multi-Plattform
function os_path_join(...$parts) { return preg_replace('#'.DIRECTORY_SEPARATOR.'+#', DIRECTORY_SEPARATOR, implode(DIRECTORY_SEPARATOR, array_filter($parts))); }
Unix-basierte Systeme
function os_path_join(...$parts) { return preg_replace('#/+#', '/', implode('/', array_filter($parts))); }
Unix-basiertes System ohne REST-Parameter (beachten Sie nicht die explizite PEP8-Philosophie):
function os_path_join() { return preg_replace('#/+#', '/', implode('/', array_filter(func_get_args()))); }
Verwendung
$path = os_path_join("", "/", "mydir/", "/here/");
Bonus: Wenn Sie wirklich Python folgen möchten, folgen Sie os.path.join (). Das erste Argument ist erforderlich:
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))); }
Überprüfen Sie die Quelle von os.path.join (), wenn Sie möchten: https://github.com/python/cpython/blob/master/Lib/ntpath.py
Warnung: Diese Lösung ist nicht für URLs geeignet.
quelle
Als lustiges Projekt habe ich eine weitere Lösung erstellt. Sollte für alle Betriebssysteme universell sein:
<?php function join_paths(...$parts) { if (sizeof($parts) === 0) return ''; $prefix = ($parts[0] === DIRECTORY_SEPARATOR) ? DIRECTORY_SEPARATOR : ''; $processed = array_filter(array_map(function ($part) { return rtrim($part, DIRECTORY_SEPARATOR); }, $parts), function ($part) { return !empty($part); }); return $prefix . implode(DIRECTORY_SEPARATOR, $processed); } // relative paths var_dump(join_paths('hello/', 'world')); var_dump(join_paths('hello', 'world')); var_dump(join_paths('hello', '', 'world')); var_dump(join_paths('', 'hello/world')); echo "\n"; // absolute paths var_dump(join_paths('/hello/', 'world')); var_dump(join_paths('/hello', 'world')); var_dump(join_paths('/hello/', '', 'world')); var_dump(join_paths('/hello', '', 'world')); var_dump(join_paths('', '/hello/world')); var_dump(join_paths('/', 'hello/world'));
Ergebnisse:
string(11) "hello/world" string(11) "hello/world" string(11) "hello/world" string(11) "hello/world" string(12) "/hello/world" string(12) "/hello/world" string(12) "/hello/world" string(12) "/hello/world" string(12) "/hello/world" string(12) "/hello/world"
quelle
Hier ist eine Funktion, die sich wie die von Node
path.resolve
verhält :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); }
Testfälle:
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'
quelle
Aus der großartigen Antwort von Ricardo Galli geht eine kleine Verbesserung hervor, um zu vermeiden, dass das Protokollpräfix gelöscht wird.
Die Idee ist, das Vorhandensein eines Protokolls in einem Argument zu testen und es im Ergebnis beizubehalten. WARNUNG: Dies ist eine naive Implementierung!
Zum Beispiel:
array("http://domain.de","/a","/b/")
Ergebnisse zu (Protokoll halten)
"http://domain.de/a/b/"
statt (Tötungsprotokoll)
"http:/domain.de/a/b/"
Aber http://codepad.org/hzpWmpzk benötigt eine bessere Fähigkeit zum Schreiben von Code.
quelle
Ich liebe Riccardos Antwort und ich denke, es ist die beste Antwort.
Ich verwende es, um Pfade beim Erstellen von URLs zu verbinden , aber mit einer kleinen Änderung, um den doppelten Schrägstrich der Protokolle zu handhaben:
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); }
quelle
function path_combine($paths) { for ($i = 0; $i < count($paths); ++$i) { $paths[$i] = trim($paths[$i]); } $dirty_paths = explode(DIRECTORY_SEPARATOR, join(DIRECTORY_SEPARATOR, $paths)); for ($i = 0; $i < count($dirty_paths); ++$i) { $dirty_paths[$i] = trim($dirty_paths[$i]); } $unslashed_paths = array(); for ($i = 0; $i < count($dirty_paths); ++$i) { $path = $dirty_paths[$i]; if (strlen($path) == 0) continue; array_push($unslashed_paths, $path); } $first_not_empty_index = 0; while(strlen($paths[$first_not_empty_index]) == 0) { ++$first_not_empty_index; } $starts_with_slash = $paths[$first_not_empty_index][0] == DIRECTORY_SEPARATOR; return $starts_with_slash ? DIRECTORY_SEPARATOR . join(DIRECTORY_SEPARATOR, $unslashed_paths) : join(DIRECTORY_SEPARATOR, $unslashed_paths); }
Anwendungsbeispiel:
$test = path_combine([' ', '/cosecheamo', 'pizze', '///// 4formaggi', 'GORGONZOLA']); echo $test;
Wird ausgegeben:
/cosecheamo/pizze/4formaggi/GORGONZOLA
quelle
Hier ist meine Lösung:
function joinPath(): string { $path = ''; foreach (func_get_args() as $numArg => $arg) { $arg = trim($arg); $firstChar = substr($arg, 0, 1); $lastChar = substr($arg, -1); if ($numArg != 0 && $firstChar != '/') { $arg = '/'.$arg; } # Eliminamos el slash del final if ($lastChar == '/') { $arg = rtrim($arg, '/'); } $path .= $arg; } return $path; }
quelle
Hmmm die meisten scheinen etwas zu kompliziert. Keine Ahnung, das ist meine Meinung dazu:
// Takes any amount of arguments, joins them, then replaces double slashes function join_urls() { $parts = func_get_args(); $url_part = implode("/", $parts); return preg_replace('/\/{1,}/', '/', $url_part); }
quelle
Für Leute, die eine Join-Funktion wünschen, die den Windows-Backslash und den Linux-Forward-Slash ausführt.
Verwendung:
<?php use App\Util\Paths echo Paths::join('a','b'); //Prints 'a/b' on *nix, or 'a\\b' on Windows
Klassendatei:
<?php namespace App\Util; class Paths { public static function join_with_separator($separator, $paths) { $slash_delimited_path = preg_replace('#\\\\#','/', join('/', $paths)); $duplicates_cleaned_path = preg_replace('#/+#', $separator, $slash_delimited_path); return $duplicates_cleaned_path; } public static function join() { $paths = array(); foreach (func_get_args() as $arg) { if ($arg !== '') { $paths[] = $arg; } } return Paths::join_with_separator(DIRECTORY_SEPARATOR, $paths); } }
Hier ist die Testfunktion:
<?php namespace Tests\Unit; use PHPUnit\Framework\TestCase; use App\Util\Paths; class PathsTest extends TestCase { public function testWindowsPaths() { $TEST_INPUTS = [ [], ['a'], ['a','b'], ['C:\\','blah.txt'], ['C:\\subdir','blah.txt'], ['C:\\subdir\\','blah.txt'], ['C:\\subdir','nested','1/2','blah.txt'], ]; $EXPECTED_OUTPUTS = [ '', 'a', 'a\\b', 'C:\\blah.txt', 'C:\\subdir\\blah.txt', 'C:\\subdir\\blah.txt', 'C:\\subdir\\nested\\1\\2\\blah.txt', ]; for ($i = 0; $i < count($TEST_INPUTS); $i++) { $actualPath = Paths::join_with_separator('\\', $TEST_INPUTS[$i]); $expectedPath = $EXPECTED_OUTPUTS[$i]; $this->assertEquals($expectedPath, $actualPath); } } public function testNixPaths() { $TEST_INPUTS = [ [], ['a'], ['a','b'], ['/home','blah.txt'], ['/home/username','blah.txt'], ['/home/username/','blah.txt'], ['/home/subdir','nested','1\\2','blah.txt'], ]; $EXPECTED_OUTPUTS = [ '', 'a', 'a/b', '/home/blah.txt', '/home/username/blah.txt', '/home/username/blah.txt', '/home/subdir/nested/1/2/blah.txt', ]; for ($i = 0; $i < count($TEST_INPUTS); $i++) { $actualPath = Paths::join_with_separator('/', $TEST_INPUTS[$i]); $expectedPath = $EXPECTED_OUTPUTS[$i]; $this->assertEquals($expectedPath, $actualPath); } } }
quelle
Ich mochte mehrere Lösungen vorgestellt. Aber diejenigen, die alle '/ +' durch '/' (reguläre Ausdrücke) ersetzen, vergessen, dass os.path.join () aus Python diese Art von Join verarbeiten kann:
os.path.join('http://example.com/parent/path', 'subdir/file.html')
Ergebnis: ' http://example.com/parent/path/subdir/file.html '
quelle