3
votes

Trouvez le plus long chemin commun à deux chemins en Java

Si j'ai deux chemins, comment puis-je trouver le chemin commun le plus long des deux?

Paths.get("/a/b/c")

Résultat attendu:

import java.nio.file.Path;
import java.nio.file.Paths;

Path common(Path pathA, Path pathB) {
    ...
}
...
common(Paths.get("/a/b/c/d/e"), Paths.get("/a/b/c/g/h"))

p >


3 commentaires

Voulez-vous trouver le préfixe commun le plus long? Ou n'importe quel sous-chemin commun le plus long?


Vous pouvez diviser les chaînes et comparer les parties entre les "/". À la fin, retournez la sous-chaîne correcte.


@ MichałZiober le préfixe commun le plus long. Il doit trouver le répertoire parent le plus proche commun aux deux chemins.


3 Réponses :


2
votes
Path result = path1.resolve(relativePath).normalize()
// result: /a/b/c

0 commentaires

1
votes

Nous pouvons générer tous les sous-chemins à partir du plus long possible et vérifier lesquels sont égaux:

/a/b/c = /a/b/d => /a/b
/a/ = /a/b/d => /a
/f/b/c = /a/b/d => /
/a/b/c/d/e = /a/b/f/../c/g => /a/b/c
C:/Winnt/System32 = C:/Winnt/System64 => C:/Winnt

Et utilisation:

Map<String, String> paths = new LinkedHashMap<>();
paths.put("/a/b/c", "/a/b/d");
paths.put("/a/", "/a/b/d");
paths.put("/f/b/c", "/a/b/d");
paths.put("/a/b/c/d/e", "/a/b/f/../c/g");
paths.put("C:/Winnt/System32", "C:/Winnt/System64");

paths.forEach((k, v) ->
        System.out.println(
                k + " = " + v + " => " + commonPath(Paths.get(k), Paths.get(v))));

Ci-dessus code imprime:

private Path commonPath(Path path0, Path path1) {
    if (path0.equals(path1)) {
        return path0;
    }

    path0 = path0.normalize();
    path1 = path1.normalize();
    int minCount = Math.min(path0.getNameCount(), path1.getNameCount());
    for (int i = minCount; i > 0; i--) {
        Path sp0 = path0.subpath(0, i);
        if (sp0.equals(path1.subpath(0, i))) {
            String root = Objects.toString(path0.getRoot(), "");
            return Paths.get(root, sp0.toString());
        }
    }

    return path0.getRoot();
}


2 commentaires

Vous voudrez peut-être ajouter un appel à getRoot () pour résoudre le problème de suppression du "/" initial.


Bonne prise. Merci! J'ai mis à jour la réponse avec votre suggestion.



2
votes

Essayez cette idée simple

    Path a = Paths.get("a/b/c/d/e");
    Path b = Paths.get("a/b/c/g/h");

    // Normalize
    a = a.normalize();
    b = b.normalize();

    // Create common root
    Path common = null;
    if (a.isAbsolute() && b.isAbsolute() && a.getRoot().equals(b.getRoot())) {
            common = a.getRoot();
    }
    else if (!a.isAbsolute() && !b.isAbsolute()) {
            common = Paths.get("");
    }

    // Iterate from root until names differ
    if (common != null) {
            int n = Math.min(a.getNameCount(), b.getNameCount());
            for (int i=0; i<n; i++) {
                    if (a.getName(i).equals(b.getName(i))) {
                            common = common.resolve(a.getName(i));
                    }
                    else {
                            break;
                    }
            }
    }

    // Show
    System.out.println(common);


5 commentaires

Cela semble bon, sauf si vous utilisez des chemins absolus "/ a / b / c", vous obtiendrez le chemin relatif "a / b / c".


Vous voudrez peut-être ajouter un appel getRoot () .


Merci @DodgyCodeException, j'ai ajouté du code pour prendre en charge les deux cas.


Maintenant, cela fonctionne bien sous Linux, mais pas sous Windows ("C: \ a \ b", "D: \ f \ g" -> "\"?)


@DodgyCodeException, belle prise. J'ai mis à jour la réponse.