Wie kombiniere ich Pfade in Java?

350

Gibt es ein Java-Äquivalent für System.IO.Path.Combine()in C # /. NET? Oder irgendein Code, um dies zu erreichen?

Diese statische Methode kombiniert eine oder mehrere Zeichenfolgen zu einem Pfad.

Rodrigue
quelle
1
Diese SO-Frage könnte helfen.
Jim Blizard

Antworten:

407

Anstatt alles stringbasiert zu halten, sollten Sie eine Klasse verwenden, die einen Dateisystempfad darstellt.

Wenn Sie Java 7 oder Java 8 verwenden, sollten Sie die Verwendung unbedingt in Betracht ziehen java.nio.file.Path. Path.resolvekann verwendet werden, um einen Pfad mit einem anderen oder mit einer Zeichenfolge zu kombinieren. Die PathsHilfsklasse ist auch nützlich. Zum Beispiel:

Path path = Paths.get("foo", "bar", "baz.txt");

Wenn Sie für Umgebungen vor Java-7 sorgen müssen, können Sie Folgendes verwenden java.io.File:

File baseDirectory = new File("foo");
File subDirectory = new File(baseDirectory, "bar");
File fileInDirectory = new File(subDirectory, "baz.txt");

Wenn Sie es später als Zeichenfolge zurückhaben möchten, können Sie es aufrufen getPath(). In der Tat, wenn Sie wirklich nachahmen wollten Path.Combine, könnten Sie einfach etwas schreiben wie:

public static String combine(String path1, String path2)
{
    File file1 = new File(path1);
    File file2 = new File(file1, path2);
    return file2.getPath();
}
Jon Skeet
quelle
10
Vorsicht vor absoluten Pfaden. Die .NET-Version gibt zurück path2(ignoriert path1), wenn path2es sich um einen absoluten Pfad handelt. Die Java-Version löscht das führende /oder \ und behandelt es als relativen Pfad.
Finnw
23
@ Matthew - weil ein Verzeichnis eine Datei ist. Es ist eine Datei, deren Inhalt die untergeordneten Elemente dieses Verzeichnisses, ihren Speicherort auf der Festplatte, Berechtigungen usw. definiert.
Dónal
7
@Hugo: Also verschwendet es ganze zwei Objekte ? Schockierend! Sieht für mich ziemlich sauber aus, um ehrlich zu sein ... es behält die Logik für relative Dateinamen bei, wo sie hingehört, in der File-Klasse.
Jon Skeet
1
@modosansreves: Schau dir an File.getCanonicalPath.
Jon Skeet
1
@ SargeBorsch: Nun, C # ist nur eine Sprache. Sie können ganz einfach Ihr eigenes Äquivalent Filein C # erstellen, wenn Sie möchten. (Ich nehme an, Sie meinen, die Existenz von Fileist ein Vorteil, dem ich zustimmen würde.)
Jon Skeet
118

In Java 7 sollten Sie Folgendes verwenden resolve:

Path newPath = path.resolve(childPath);

Während die NIO2 Path-Klasse für File mit einer unnötig anderen API etwas redundant erscheint, ist sie in der Tat subtil eleganter und robuster.

Beachten Sie, dass Paths.get()(wie von jemand anderem vorgeschlagen) keine Überlastung auftritt Path, und dass dies Paths.get(path.toString(), childPath)NICHT dasselbe ist wie resolve(). Aus den Paths.get()Dokumenten :

Beachten Sie, dass diese Methode zwar sehr praktisch ist, ihre Verwendung jedoch einen angenommenen Verweis auf das Standard-Dateisystem impliziert und die Nützlichkeit des aufrufenden Codes einschränkt. Daher sollte es nicht in Bibliothekscode verwendet werden, der für eine flexible Wiederverwendung vorgesehen ist. Eine flexiblere Alternative besteht darin, eine vorhandene Pfadinstanz als Anker zu verwenden, z.

Path dir = ...
Path path = dir.resolve("file");

Die Schwesterfunktion resolveist die ausgezeichnete relativize:

Path childPath = path.relativize(newPath);
Aleksandr Dubinsky
quelle
17

Ich weiß, dass es lange her ist, dass Jon ursprünglich geantwortet hat, aber ich hatte eine ähnliche Anforderung wie das OP.

Um Jons Lösung zu erweitern, habe ich Folgendes gefunden: Für ein oder mehrere Pfadsegmente werden so viele Pfadsegmente verwendet, wie Sie darauf werfen können.

Verwendungszweck

Path.combine("/Users/beardtwizzle/");
Path.combine("/", "Users", "beardtwizzle");
Path.combine(new String[] { "/", "Users", "beardtwizzle", "arrayUsage" });

Code hier für andere mit einem ähnlichen Problem

public class Path {
    public static String combine(String... paths)
    {
        File file = new File(paths[0]);

        for (int i = 1; i < paths.length ; i++) {
            file = new File(file, paths[i]);
        }

        return file.getPath();
    }
}
isNaN1247
quelle
12

Plattformunabhängiger Ansatz (verwendet File.separator, dh funktioniert funktioniert abhängig vom Betriebssystem, auf dem der Code ausgeführt wird:

java.nio.file.Paths.get(".", "path", "to", "file.txt")
// relative unix path: ./path/to/file.txt
// relative windows path: .\path\to\filee.txt

java.nio.file.Paths.get("/", "path", "to", "file.txt")
// absolute unix path: /path/to/filee.txt
// windows network drive path: \\path\to\file.txt

java.nio.file.Paths.get("C:", "path", "to", "file.txt")
// absolute windows path: C:\path\to\file.txt
Maksim Kostromin
quelle
11

Um die Antwort von JodaStephen zu verbessern, verfügt Apache Commons IO über FilenameUtils, das dies tut. Beispiel (unter Linux):

assert org.apache.commons.io.FilenameUtils.concat("/home/bob", "work\\stuff.log") == "/home/bob/work/stuff.log"

Es ist plattformunabhängig und produziert alle Separatoren, die Ihr System benötigt.

alpian
quelle
2

Hier ist eine Lösung, die mehrere Pfadteile und Randbedingungen behandelt:

public static String combinePaths(String ... paths)
{
  if ( paths.length == 0)
  {
    return "";
  }

  File combined = new File(paths[0]);

  int i = 1;
  while ( i < paths.length)
  {
    combined = new File(combined, paths[i]);
    ++i;
  }

  return combined.getPath();
}
Rechnung
quelle
2

Wenn Sie nicht mehr als Zeichenfolgen benötigen, können Sie com.google.common.io.Files verwenden

Files.simplifyPath("some/prefix/with//extra///slashes" + "file//name")

bekommen

"some/prefix/with/extra/slashes/file/name"
Gismo Ranas
quelle
1

Vielleicht zu spät zur Party, aber ich wollte meine Meinung dazu mitteilen. Ich verwende ein Builder-Muster und erlaube bequem verkettete appendAnrufe. Es kann leicht erweitert werden, um auch das Arbeiten mit PathObjekten zu unterstützen .

public class Files  {
    public static class PathBuilder {
        private File file;

        private PathBuilder ( File root ) {
            file = root;
        }

        private PathBuilder ( String root ) {
            file = new File(root);
        }

        public PathBuilder append ( File more ) {
            file = new File(file, more.getPath()) );
            return this;
        }

        public PathBuilder append ( String more ) {
            file = new File(file, more);
            return this;
        }

        public File buildFile () {
            return file;
        }
    }

    public static PathBuilder buildPath ( File root ) {
        return new PathBuilder(root);
    }

    public static PathBuilder buildPath ( String root ) {
        return new PathBuilder(root);
    }
}

Anwendungsbeispiel:

File root = File.listRoots()[0];
String hello = "hello";
String world = "world";
String filename = "warez.lha"; 

File file = Files.buildPath(root).append(hello).append(world)
              .append(filename).buildFile();
String absolute = file.getAbsolutePath();

Das Ergebnis absoluteenthält Folgendes:

/hello/world/warez.lha

oder vielleicht sogar:

A:\hello\world\warez.lha
Stephan Henningsen
quelle
1

Dies funktioniert auch in Java 8:

Path file = Paths.get("Some path");
file = Paths.get(file + "Some other path");
rootExplorr
quelle
0

Diese Lösung bietet eine Schnittstelle zum Verbinden von Pfadfragmenten aus einem String [] -Array. Es verwendet java.io.File.File (String parent, String child) :

    public static joinPaths(String[] fragments) {
        String emptyPath = "";
        return buildPath(emptyPath, fragments);
    }

    private static buildPath(String path, String[] fragments) {
        if (path == null || path.isEmpty()) {
            path = "";
        }

        if (fragments == null || fragments.length == 0) {
            return "";
        }

        int pathCurrentSize = path.split("/").length;
        int fragmentsLen = fragments.length;

        if (pathCurrentSize <= fragmentsLen) {
            String newPath = new File(path, fragments[pathCurrentSize - 1]).toString();
            path = buildPath(newPath, fragments);
        }

        return path;
    }

Dann können Sie einfach tun:

String[] fragments = {"dir", "anotherDir/", "/filename.txt"};
String path = joinPaths(fragments);

Kehrt zurück:

"/dir/anotherDir/filename.txt"
JackCid
quelle