দুটি পরম পাথ (বা ইউআরএল) থেকে জাভাতে কোনও আপেক্ষিক পথ কীভাবে তৈরি করবেন?


275

দুটি পরম পাথ দেওয়া হয়েছে, যেমন

/var/data/stuff/xyz.dat
/var/data

কেউ কীভাবে এমন কোনও আপেক্ষিক পথ তৈরি করতে পারে যা দ্বিতীয় পথটিকে তার ভিত্তি হিসাবে ব্যবহার করে? উপরের উদাহরণে, ফলাফলটি হওয়া উচিত:./stuff/xyz.dat


3
জাভা 7 এবং তারপরে, @ ভিটালিই ফেডোরেনকো এর উত্তর দেখুন।
অ্যান্ডি টমাস

1
tl; DR উত্তর: Paths.get (startPath) .relativize (Paths.get (endPath))। toString (), যা জাভা 8 এ আমার জন্য উদাহরণস্বরূপ "../" দিয়ে ঠিক কাজ করছে বলে মনে হচ্ছে , তাই ...)
অ্যান্ড্রু

উত্তর:


297

এটি একটু চক্রাকার, তবে ইউআরআই ব্যবহার করবেন না কেন? এটিতে একটি পুনঃসংশ্লিষ্ট পদ্ধতি রয়েছে যা আপনার জন্য প্রয়োজনীয় সমস্ত চেক করে।

String path = "/var/data/stuff/xyz.dat";
String base = "/var/data";
String relative = new File(base).toURI().relativize(new File(path).toURI()).getPath();
// relative == "stuff/xyz.dat"

দয়া করে মনে রাখবেন ফাইল পাথ জন্য সেখানে যে java.nio.file.Path#relativizeজাভা 1.7 যেহেতু দ্বারা সরু আউট হিসাবে, @Jirka Meluzin মধ্যে অন্যান্য উত্তর


17
পিটার মোলারের উত্তর দেখুন। রিলেটিভাইজ () সহজ ক্ষেত্রে ব্যতীত সকলের জন্য বেশ ভাঙা প্রদর্শিত হবে।
ডেভ রে

11
হ্যাঁ, এটি কেবল তখনই কাজ করে যদি বেস পাথটি প্রথম পাথের পিতামাতা হয়। আপনার যদি "../../relativepath" এর মতো কিছু শ্রেণিবিন্যাসের পিছনে প্রয়োজন হয় তবে এটি কাজ করবে না। আমি একটি সমাধান পেয়েছি: mrpmorris.blogspot.com/2007/05/…
অরেলিন রিবন

4
যেমন @ ভিটালিআই ফেডোরেনকো লিখেছেন: ব্যবহার করুন java.nio.file.Path#relativize(Path), এটি কেবল পিতামাতার ডাবল-ডটস এবং সকলের সাথে কাজ করে।
ক্যাম্পা

toPath()পরিবর্তে ব্যবহার বিবেচনা করুন toURI()। এটি পুরোপুরি স্টাফ তৈরি করতে সক্ষম "..\.."। তবে java.lang.IllegalArgumentException: 'other' has different rootথেকে আপেক্ষিক পথ "C:\temp"চাইলে ব্যতিক্রম সম্পর্কে সচেতন হন "D:\temp"
ইগোর

এটি প্রত্যাশার মতো কাজ করে না, এটি আমার পরীক্ষার ক্ষেত্রে ডেটা / স্টাফ / xyz.dat প্রদান করে।
আনবিক্যান্ট

238

জাভা 7-এর পর থেকে আপনি পুনরায় সংযুক্তি পদ্ধতিটি ব্যবহার করতে পারেন :

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

public class Test {

     public static void main(String[] args) {
        Path pathAbsolute = Paths.get("/var/data/stuff/xyz.dat");
        Path pathBase = Paths.get("/var/data");
        Path pathRelative = pathBase.relativize(pathAbsolute);
        System.out.println(pathRelative);
    }

}

আউটপুট:

stuff/xyz.dat

3
দুর্দান্ত, সংক্ষিপ্ত, কোনও অতিরিক্ত lib +1 নেই। অ্যাডাম ক্রমের সমাধান (হিট 1) আমার পরীক্ষাগুলি এবং পরবর্তী উত্তরটি পাস করতে পারে না (হিট 2) "একমাত্র 'ওয়ার্কিং' সলিউশন" আমার বাস্তবায়নের চেয়ে একটি নতুন জার যুক্ত করেছে এবং এটি পরে আমি এখানে পাই ... আর কখনও এর চেয়ে ভাল better )
হক্ক

1
তবে এই সমস্যার জন্য নজর রাখুন ।
ben3000

1
পরীক্ষা করা হয়েছে যে ..এটি প্রয়োজনীয় যেখানে যোগ করা পরিচালনা করে (এটি করে) does
ওভেন

দুর্ভাগ্যক্রমে, অ্যান্ড্রয়েড এতে অন্তর্ভুক্ত নেই java.nio.file:(
নাথান ওসমান

1
আমি "রিলেটিভাইজ" এর আগে "পাথবেস" "স্বাভাবিকীকরণ" না করা থাকলে আপনি অদ্ভুত ফলাফল পেয়েছেন। যদিও এই উদাহরণে সূক্ষ্ম, আমি pathBase.normalize().relativize(pathAbsolute);একটি সাধারণ নিয়ম হিসাবে করব ।
pstanton

77

লেখার সময় (জুন ২০১০), এটিই আমার সমাধানের কেসগুলি পাস করার একমাত্র সমাধান ছিল। আমি গ্যারান্টি দিতে পারি না যে এই সমাধানটি বাগ-মুক্ত, তবে এটি অন্তর্ভুক্ত পরীক্ষার কেসগুলি পাস করে। আমি যে পদ্ধতি এবং পরীক্ষাগুলি লিখেছি তা আপাচি কমন্স আইওFilenameUtils থেকে ক্লাসের উপর নির্ভর করে ।

সমাধানটি জাভা 1.4 দিয়ে পরীক্ষা করা হয়েছিল। আপনি জাভা 1.5 ব্যবহার যদি করছি (বা উচ্চতর) আপনি প্রতিস্থাপন বিবেচনা করা উচিত StringBufferসঙ্গে StringBuilder(যদি আপনি এখনও জাভা 1.4 ব্যবহার করছেন আপনি যদি এর পরিবর্তে নিয়োগকর্তা পরিবর্তন বিবেচনা করা উচিত)।

import java.io.File;
import java.util.regex.Pattern;

import org.apache.commons.io.FilenameUtils;

public class ResourceUtils {

    /**
     * Get the relative path from one file to another, specifying the directory separator. 
     * If one of the provided resources does not exist, it is assumed to be a file unless it ends with '/' or
     * '\'.
     * 
     * @param targetPath targetPath is calculated to this file
     * @param basePath basePath is calculated from this file
     * @param pathSeparator directory separator. The platform default is not assumed so that we can test Unix behaviour when running on Windows (for example)
     * @return
     */
    public static String getRelativePath(String targetPath, String basePath, String pathSeparator) {

        // Normalize the paths
        String normalizedTargetPath = FilenameUtils.normalizeNoEndSeparator(targetPath);
        String normalizedBasePath = FilenameUtils.normalizeNoEndSeparator(basePath);

        // Undo the changes to the separators made by normalization
        if (pathSeparator.equals("/")) {
            normalizedTargetPath = FilenameUtils.separatorsToUnix(normalizedTargetPath);
            normalizedBasePath = FilenameUtils.separatorsToUnix(normalizedBasePath);

        } else if (pathSeparator.equals("\\")) {
            normalizedTargetPath = FilenameUtils.separatorsToWindows(normalizedTargetPath);
            normalizedBasePath = FilenameUtils.separatorsToWindows(normalizedBasePath);

        } else {
            throw new IllegalArgumentException("Unrecognised dir separator '" + pathSeparator + "'");
        }

        String[] base = normalizedBasePath.split(Pattern.quote(pathSeparator));
        String[] target = normalizedTargetPath.split(Pattern.quote(pathSeparator));

        // First get all the common elements. Store them as a string,
        // and also count how many of them there are.
        StringBuffer common = new StringBuffer();

        int commonIndex = 0;
        while (commonIndex < target.length && commonIndex < base.length
                && target[commonIndex].equals(base[commonIndex])) {
            common.append(target[commonIndex] + pathSeparator);
            commonIndex++;
        }

        if (commonIndex == 0) {
            // No single common path element. This most
            // likely indicates differing drive letters, like C: and D:.
            // These paths cannot be relativized.
            throw new PathResolutionException("No common path element found for '" + normalizedTargetPath + "' and '" + normalizedBasePath
                    + "'");
        }   

        // The number of directories we have to backtrack depends on whether the base is a file or a dir
        // For example, the relative path from
        //
        // /foo/bar/baz/gg/ff to /foo/bar/baz
        // 
        // ".." if ff is a file
        // "../.." if ff is a directory
        //
        // The following is a heuristic to figure out if the base refers to a file or dir. It's not perfect, because
        // the resource referred to by this path may not actually exist, but it's the best I can do
        boolean baseIsFile = true;

        File baseResource = new File(normalizedBasePath);

        if (baseResource.exists()) {
            baseIsFile = baseResource.isFile();

        } else if (basePath.endsWith(pathSeparator)) {
            baseIsFile = false;
        }

        StringBuffer relative = new StringBuffer();

        if (base.length != commonIndex) {
            int numDirsUp = baseIsFile ? base.length - commonIndex - 1 : base.length - commonIndex;

            for (int i = 0; i < numDirsUp; i++) {
                relative.append(".." + pathSeparator);
            }
        }
        relative.append(normalizedTargetPath.substring(common.length()));
        return relative.toString();
    }


    static class PathResolutionException extends RuntimeException {
        PathResolutionException(String msg) {
            super(msg);
        }
    }    
}

এই পাসের পরীক্ষার কেসগুলি

public void testGetRelativePathsUnix() {
    assertEquals("stuff/xyz.dat", ResourceUtils.getRelativePath("/var/data/stuff/xyz.dat", "/var/data/", "/"));
    assertEquals("../../b/c", ResourceUtils.getRelativePath("/a/b/c", "/a/x/y/", "/"));
    assertEquals("../../b/c", ResourceUtils.getRelativePath("/m/n/o/a/b/c", "/m/n/o/a/x/y/", "/"));
}

public void testGetRelativePathFileToFile() {
    String target = "C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";
    String base = "C:\\Windows\\Speech\\Common\\sapisvr.exe";

    String relPath = ResourceUtils.getRelativePath(target, base, "\\");
    assertEquals("..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);
}

public void testGetRelativePathDirectoryToFile() {
    String target = "C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";
    String base = "C:\\Windows\\Speech\\Common\\";

    String relPath = ResourceUtils.getRelativePath(target, base, "\\");
    assertEquals("..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);
}

public void testGetRelativePathFileToDirectory() {
    String target = "C:\\Windows\\Boot\\Fonts";
    String base = "C:\\Windows\\Speech\\Common\\foo.txt";

    String relPath = ResourceUtils.getRelativePath(target, base, "\\");
    assertEquals("..\\..\\Boot\\Fonts", relPath);
}

public void testGetRelativePathDirectoryToDirectory() {
    String target = "C:\\Windows\\Boot\\";
    String base = "C:\\Windows\\Speech\\Common\\";
    String expected = "..\\..\\Boot";

    String relPath = ResourceUtils.getRelativePath(target, base, "\\");
    assertEquals(expected, relPath);
}

public void testGetRelativePathDifferentDriveLetters() {
    String target = "D:\\sources\\recovery\\RecEnv.exe";
    String base = "C:\\Java\\workspace\\AcceptanceTests\\Standard test data\\geo\\";

    try {
        ResourceUtils.getRelativePath(target, base, "\\");
        fail();

    } catch (PathResolutionException ex) {
        // expected exception
    }
}

5
নিস! যদিও একটি জিনিস যদিও এটি বেস এবং লক্ষ্য একই হয় তা ভেঙে যায় - স্ট্রিং সাধারণটি একটি বিভাজকটিতে শেষ করা হয়, যা সাধারণীকৃত টার্গেট পাথ থাকে না, তাই স্ট্রিং কলটিতে একটি খুব বেশি সংখ্যার জন্য জিজ্ঞাসা করা হয়। মনে করি আমি ফাংশনের শেষ দুটি লাইনের আগে নিম্নলিখিতগুলি যুক্ত করে এটি সংশোধন করেছি: যদি (সাধারণ. দৈর্ঘ্য ()> = সাধারনত টার্গেটপ্যাথ.লাইনেথ ()) {প্রত্যাবর্তন ";"; }
এরহানিস

4
এটিই একমাত্র কার্যকরী সমাধান হ'ল বিভ্রান্তিমূলক। অন্যান্য উত্তরগুলি আরও ভাল কাজ করে (যখন উত্তর এবং লক্ষ্য একই হয় তখন এই উত্তরটি ক্র্যাশ হয়), সহজ হয় এবং কমোনস-আইও-তে নির্ভর করে না।
NateS

26

Java.net.URI.relativize ব্যবহার করার সময় আপনার জাভা বাগ সম্পর্কে সচেতন হওয়া উচিত: JDK-6226081 (ইউআরআই আংশিক শিকড় সহ পাথগুলি পুনরায় সংহত করতে সক্ষম হওয়া উচিত)

এই মুহুর্তে, relativize()পদ্ধতিটি URIকেবল তখনই ইউআরআইগুলিকে পুনরায় সংযুক্ত করবে যখন একজনের অপরের উপসর্গ হয়।

যার মূলত অর্থ আপনার java.net.URI.relativizeজন্য ".." তৈরি করে না।


6
কদর্য। : এখন পর্যন্ত, এই জন্য একটি কার্যসংক্রান্ত নেই দৃশ্যত stackoverflow.com/questions/204784/...
skaffman

Paths.get (startPath) .relativize (Paths.get (endPath))। ToString‌ () জাভা ৮-এ আমার জন্য যেমন "../" দিয়ে ঠিক কাজ করছে বলে মনে হচ্ছে
অ্যান্ড্রু

@ স্কাফম্যান আপনি কি নিশ্চিত? এই উত্তরটি বাগ JDK-6226081 রেফারেন্স করেছে, তবে জেডিকে-4708535 URIUtils.resolve()উল্লেখ করেছে। এবং উত্স কোড থেকে, আমি ব্যাকট্র্যাকিংয়ের সাথে সম্পর্কিত কিছু দেখতে পাচ্ছি না (যেমন ..বিভাগগুলি)। আপনি কি দুটি বাগ গুলিয়ে ফেললেন?
গ্যারেট উইলসন

JDK-6920138 JDK-4708535 এর সদৃশ হিসাবে চিহ্নিত হয়েছে।
খ্রিস্টান কে।

17

অন্য উত্তরে যে বাগটি উল্লেখ করা হয়েছে তা অ্যাপাচি এইচটিটিপি কম্পোনেন্টের ইউআরআইইটিস দ্বারা সম্বোধন করা হয়েছে

public static URI resolve(URI baseURI,
                          String reference)

একটি বেস ইউআরআইয়ের বিরুদ্ধে একটি ইউআরআই রেফারেন্স সমাধান করে। Java.net.URI- এ বাগের জন্য কর্মক্ষেত্র


সমাধানের পদ্ধতিটি বেস এবং আপেক্ষিক পথ থেকে নিখুঁত ইউআরআই তৈরি করে না? এই পদ্ধতিটি কীভাবে সহায়তা করবে?
চেজ

17

জাভা In এবং তারপরে আপনি কেবল ব্যবহার করতে পারেন (এবং এর বিপরীতে URIএটি বাগ ফ্রি):

Path#relativize(Path)

10

যদি আপনি জানেন দ্বিতীয় স্ট্রিংটি প্রথমটির একটি অংশ:

String s1 = "/var/data/stuff/xyz.dat";
String s2 = "/var/data";
String s3 = s1.substring(s2.length());

বা যদি আপনি সত্যিই আপনার উদাহরণ হিসাবে শুরুতে সময়কাল চান:

String s3 = ".".concat(s1.substring(s2.length()));

3
স্ট্রিং এস 3 = "।" + s1.substring (s2.leth ()); কিছুটা বেশি পঠনযোগ্য আইএমও
ডোনাল

10

পুনরাবৃত্তি একটি ছোট সমাধান উত্পাদন করে। ফলাফলটি অসম্ভব হলে (উদাহরণস্বরূপ বিভিন্ন উইন্ডোজ ডিস্ক) বা অবৈজ্ঞানিক (মূলটি কেবল সাধারণ ডিরেক্টরি)

/**
 * Computes the path for a file relative to a given base, or fails if the only shared 
 * directory is the root and the absolute form is better.
 * 
 * @param base File that is the base for the result
 * @param name File to be "relativized"
 * @return the relative name
 * @throws IOException if files have no common sub-directories, i.e. at best share the
 *                     root prefix "/" or "C:\"
 */

public static String getRelativePath(File base, File name) throws IOException  {
    File parent = base.getParentFile();

    if (parent == null) {
        throw new IOException("No common directory");
    }

    String bpath = base.getCanonicalPath();
    String fpath = name.getCanonicalPath();

    if (fpath.startsWith(bpath)) {
        return fpath.substring(bpath.length() + 1);
    } else {
        return (".." + File.separator + getRelativePath(parent, name));
    }
}

getCanonicalPath ভারী ওজন হতে পারে, সুতরাং যখন আপনার লক্ষ লক্ষ রেকর্ড প্রক্রিয়া করা প্রয়োজন তখন এই সমাধানটি সুপারিশ করা যায় না। উদাহরণস্বরূপ আমার কাছে কয়েকটি তালিকাভুক্ত ফাইল রয়েছে যার মিলিয়ন রেকর্ড রয়েছে এবং এখন আমি এগুলি বহনযোগ্যতার জন্য আপেক্ষিক পথ ব্যবহার করতে সরাতে চাই।
ব্যবহারকারী2305886

8

এখানে অন্যান্য গ্রন্থাগার বিনামূল্যে একটি সমাধান রয়েছে:

Path sourceFile = Paths.get("some/common/path/example/a/b/c/f1.txt");
Path targetFile = Paths.get("some/common/path/example/d/e/f2.txt"); 
Path relativePath = sourceFile.relativize(targetFile);
System.out.println(relativePath);

আউটপুট

..\..\..\..\d\e\f2.txt

[সম্পাদনা] আসলে এটি আরও বেশি আউটপুট করে .. \ উত্সের কারণে ফাইলটি ডিরেক্টরি নয়। আমার মামলার সঠিক সমাধান হ'ল:

Path sourceFile = Paths.get(new File("some/common/path/example/a/b/c/f1.txt").parent());
Path targetFile = Paths.get("some/common/path/example/d/e/f2.txt"); 
Path relativePath = sourceFile.relativize(targetFile);
System.out.println(relativePath);

6

আমার সংস্করণটি আলগাভাবে ম্যাট এবং স্টিভের সংস্করণগুলির উপর ভিত্তি করে :

/**
 * Returns the path of one File relative to another.
 *
 * @param target the target directory
 * @param base the base directory
 * @return target's path relative to the base directory
 * @throws IOException if an error occurs while resolving the files' canonical names
 */
 public static File getRelativeFile(File target, File base) throws IOException
 {
   String[] baseComponents = base.getCanonicalPath().split(Pattern.quote(File.separator));
   String[] targetComponents = target.getCanonicalPath().split(Pattern.quote(File.separator));

   // skip common components
   int index = 0;
   for (; index < targetComponents.length && index < baseComponents.length; ++index)
   {
     if (!targetComponents[index].equals(baseComponents[index]))
       break;
   }

   StringBuilder result = new StringBuilder();
   if (index != baseComponents.length)
   {
     // backtrack to base directory
     for (int i = index; i < baseComponents.length; ++i)
       result.append(".." + File.separator);
   }
   for (; index < targetComponents.length; ++index)
     result.append(targetComponents[index] + File.separator);
   if (!target.getPath().endsWith("/") && !target.getPath().endsWith("\\"))
   {
     // remove final path separator
     result.delete(result.length() - File.separator.length(), result.length());
   }
   return new File(result.toString());
 }

2
+1 আমার পক্ষে কাজ করে। শুধু ছোটখাট সংশোধন: পরিবর্তে "/".length()আপনি separator.length ব্যবহার করা উচিত
leonbloy

5

ম্যাট বি এর সমাধানটি ডিরেক্টরিকে ভুল করে ব্যাকট্র্যাক করার জন্য সংখ্যাটি পেয়েছে - এটি সাধারণ পথের উপাদানগুলির বিয়োগ পথের বিয়োগের দৈর্ঘ্য হওয়া উচিত, বিয়োগ একটি (শেষ পাথ উপাদানটির জন্য যা হয় কোনও ফাইলের নাম বা একটি ""দ্বারা নির্মিত ট্রেলিং split) । এটা দিয়ে কাজ করতে ঘটবে /a/b/c/এবং /a/x/y/, কিন্তু সঙ্গে আর্গুমেন্ট প্রতিস্থাপন /m/n/o/a/b/c/এবং /m/n/o/a/x/y/এবং আপনি সমস্যাটি দেখতে হবে।

এছাড়াও, এটি else breakলুপের জন্য প্রথমটির অভ্যন্তরে প্রয়োজন , অথবা এটি পাথগুলি ভুলভাবে পরিচালনা করবে যেগুলির সাথে মিলবে ডিরেক্টরিগুলির নাম, যেমন এবং /a/b/c/d/এবং /x/y/c/z- cউভয় অ্যারেতে একই স্লটে রয়েছে, তবে এটি প্রকৃত মিল নয়।

এই সমস্ত সমাধানগুলির মধ্যে এমন পাথগুলি পরিচালনা করার দক্ষতার অভাব রয়েছে যা একে অপরের সাথে পুনরায় সংযুক্ত হতে পারে না কারণ তাদের বেমানান শিকড় রয়েছে যেমন C:\foo\barএবং D:\baz\quux। সম্ভবত উইন্ডোজ শুধুমাত্র একটি সমস্যা, কিন্তু লক্ষণীয়।

আমি এটির চেয়ে অনেক বেশি সময় ব্যয় করেছি, তবে এটি ঠিক আছে। কাজের জন্য আমার আসলে এটির প্রয়োজন ছিল, তাই যারা চিমেছ তারা সবাইকে ধন্যবাদ, এবং আমি নিশ্চিত যে এই সংস্করণেও সংশোধন হবে!

public static String getRelativePath(String targetPath, String basePath, 
        String pathSeparator) {

    //  We need the -1 argument to split to make sure we get a trailing 
    //  "" token if the base ends in the path separator and is therefore
    //  a directory. We require directory paths to end in the path
    //  separator -- otherwise they are indistinguishable from files.
    String[] base = basePath.split(Pattern.quote(pathSeparator), -1);
    String[] target = targetPath.split(Pattern.quote(pathSeparator), 0);

    //  First get all the common elements. Store them as a string,
    //  and also count how many of them there are. 
    String common = "";
    int commonIndex = 0;
    for (int i = 0; i < target.length && i < base.length; i++) {
        if (target[i].equals(base[i])) {
            common += target[i] + pathSeparator;
            commonIndex++;
        }
        else break;
    }

    if (commonIndex == 0)
    {
        //  Whoops -- not even a single common path element. This most
        //  likely indicates differing drive letters, like C: and D:. 
        //  These paths cannot be relativized. Return the target path.
        return targetPath;
        //  This should never happen when all absolute paths
        //  begin with / as in *nix. 
    }

    String relative = "";
    if (base.length == commonIndex) {
        //  Comment this out if you prefer that a relative path not start with ./
        //relative = "." + pathSeparator;
    }
    else {
        int numDirsUp = base.length - commonIndex - 1;
        //  The number of directories we have to backtrack is the length of 
        //  the base path MINUS the number of common path elements, minus
        //  one because the last element in the path isn't a directory.
        for (int i = 1; i <= (numDirsUp); i++) {
            relative += ".." + pathSeparator;
        }
    }
    relative += targetPath.substring(common.length());

    return relative;
}

এবং বেশ কয়েকটি কেস কভার করার জন্য এখানে পরীক্ষা দেওয়া হচ্ছে:

public void testGetRelativePathsUnixy() 
{        
    assertEquals("stuff/xyz.dat", FileUtils.getRelativePath(
            "/var/data/stuff/xyz.dat", "/var/data/", "/"));
    assertEquals("../../b/c", FileUtils.getRelativePath(
            "/a/b/c", "/a/x/y/", "/"));
    assertEquals("../../b/c", FileUtils.getRelativePath(
            "/m/n/o/a/b/c", "/m/n/o/a/x/y/", "/"));
}

public void testGetRelativePathFileToFile() 
{
    String target = "C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";
    String base = "C:\\Windows\\Speech\\Common\\sapisvr.exe";

    String relPath = FileUtils.getRelativePath(target, base, "\\");
    assertEquals("..\\..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);
}

public void testGetRelativePathDirectoryToFile() 
{
    String target = "C:\\Windows\\Boot\\Fonts\\chs_boot.ttf";
    String base = "C:\\Windows\\Speech\\Common";

    String relPath = FileUtils.getRelativePath(target, base, "\\");
    assertEquals("..\\..\\Boot\\Fonts\\chs_boot.ttf", relPath);
}

public void testGetRelativePathDifferentDriveLetters() 
{
    String target = "D:\\sources\\recovery\\RecEnv.exe";
    String base   = "C:\\Java\\workspace\\AcceptanceTests\\Standard test data\\geo\\";

    //  Should just return the target path because of the incompatible roots.
    String relPath = FileUtils.getRelativePath(target, base, "\\");
    assertEquals(target, relPath);
}

4

আসলে আমার অন্য উত্তরটি কাজ করে না যদি লক্ষ্য পথটি বেস পাথের শিশু না হয়।

এই কাজ করা উচিত.

public class RelativePathFinder {

    public static String getRelativePath(String targetPath, String basePath, 
       String pathSeparator) {

        // find common path
        String[] target = targetPath.split(pathSeparator);
        String[] base = basePath.split(pathSeparator);

        String common = "";
        int commonIndex = 0;
        for (int i = 0; i < target.length && i < base.length; i++) {

            if (target[i].equals(base[i])) {
                common += target[i] + pathSeparator;
                commonIndex++;
            }
        }


        String relative = "";
        // is the target a child directory of the base directory?
        // i.e., target = /a/b/c/d, base = /a/b/
        if (commonIndex == base.length) {
            relative = "." + pathSeparator + targetPath.substring(common.length());
        }
        else {
            // determine how many directories we have to backtrack
            for (int i = 1; i <= commonIndex; i++) {
                relative += ".." + pathSeparator;
            }
            relative += targetPath.substring(common.length());
        }

        return relative;
    }

    public static String getRelativePath(String targetPath, String basePath) {
        return getRelativePath(targetPath, basePath, File.pathSeparator);
    }
}

public class RelativePathFinderTest extends TestCase {

    public void testGetRelativePath() {
        assertEquals("./stuff/xyz.dat", RelativePathFinder.getRelativePath(
                "/var/data/stuff/xyz.dat", "/var/data/", "/"));
        assertEquals("../../b/c", RelativePathFinder.getRelativePath("/a/b/c",
                "/a/x/y/", "/"));
    }

}

2
ফাইল.পথসপেটের পরিবর্তে ফাইল.স্যাপারেটর হওয়া উচিত। পাথসেটরেটরটি কেবল বিভাজনের জন্য ব্যবহার করা উচিত (রেজেজ), যেমন "////" রেজেক্স (উইন পাথ রেজেক্স), ফলাফলের পথটি ভুল হবে।
অ্যালেক্স আইভ্যাসিভ

3

Cool !! লিনাক্স মেশিনে ডিরেক্টরি পাথের তুলনা করার জন্য আমার এই জাতীয় কোডটি দরকার। আমি দেখতে পেয়েছি যে এটি এমন পরিস্থিতিতে কাজ করছে না যেখানে পিতামাতার ডিরেক্টরি ছিল।

পদ্ধতির ডিরেক্টরি বান্ধব সংস্করণ এখানে:

 public static String getRelativePath(String targetPath, String basePath, 
     String pathSeparator) {

 boolean isDir = false;
 {
   File f = new File(targetPath);
   isDir = f.isDirectory();
 }
 //  We need the -1 argument to split to make sure we get a trailing 
 //  "" token if the base ends in the path separator and is therefore
 //  a directory. We require directory paths to end in the path
 //  separator -- otherwise they are indistinguishable from files.
 String[] base = basePath.split(Pattern.quote(pathSeparator), -1);
 String[] target = targetPath.split(Pattern.quote(pathSeparator), 0);

 //  First get all the common elements. Store them as a string,
 //  and also count how many of them there are. 
 String common = "";
 int commonIndex = 0;
 for (int i = 0; i < target.length && i < base.length; i++) {
     if (target[i].equals(base[i])) {
         common += target[i] + pathSeparator;
         commonIndex++;
     }
     else break;
 }

 if (commonIndex == 0)
 {
     //  Whoops -- not even a single common path element. This most
     //  likely indicates differing drive letters, like C: and D:. 
     //  These paths cannot be relativized. Return the target path.
     return targetPath;
     //  This should never happen when all absolute paths
     //  begin with / as in *nix. 
 }

 String relative = "";
 if (base.length == commonIndex) {
     //  Comment this out if you prefer that a relative path not start with ./
     relative = "." + pathSeparator;
 }
 else {
     int numDirsUp = base.length - commonIndex - (isDir?0:1); /* only subtract 1 if it  is a file. */
     //  The number of directories we have to backtrack is the length of 
     //  the base path MINUS the number of common path elements, minus
     //  one because the last element in the path isn't a directory.
     for (int i = 1; i <= (numDirsUp); i++) {
         relative += ".." + pathSeparator;
     }
 }
 //if we are comparing directories then we 
 if (targetPath.length() > common.length()) {
  //it's OK, it isn't a directory
  relative += targetPath.substring(common.length());
 }

 return relative;
}

2

আমি ধরে নিচ্ছি আপনার কাছ থেকে পাথ (ফোল্ডারের জন্য এক নিখুঁত পথ), এবং টোপথ (ফোল্ডার / ফাইলের জন্য একটি পরম পথ), এবং আপনি এমন একটি পথ খুঁজছেন যা ফাইল / ফোল্ডারটিকে টুথপথের সাথে আপেক্ষিক পথ হিসাবে উপস্থাপন করে পাঠপথ থেকে (আপনার বর্তমান ওয়ার্কিং ডিরেক্টরিটি পাথ থেকে রয়েছে ) তবে এর মতো কিছু কাজ করা উচিত:

public static String getRelativePath(String fromPath, String toPath) {

  // This weirdness is because a separator of '/' messes with String.split()
  String regexCharacter = File.separator;
  if (File.separatorChar == '\\') {
    regexCharacter = "\\\\";
  }

  String[] fromSplit = fromPath.split(regexCharacter);
  String[] toSplit = toPath.split(regexCharacter);

  // Find the common path
  int common = 0;
  while (fromSplit[common].equals(toSplit[common])) {
    common++;
  }

  StringBuffer result = new StringBuffer(".");

  // Work your way up the FROM path to common ground
  for (int i = common; i < fromSplit.length; i++) {
    result.append(File.separatorChar).append("..");
  }

  // Work your way down the TO path
  for (int i = common; i < toSplit.length; i++) {
    result.append(File.separatorChar).append(toSplit[i]);
  }

  return result.toString();
}

1

ইতিমধ্যে এখানে প্রচুর উত্তর, তবে আমি পেয়েছি যে তারা সমস্ত মামলা পরিচালনা করে নি, যেমন বেস এবং লক্ষ্য একই। এই ফাংশনটি একটি বেস ডিরেক্টরি এবং একটি লক্ষ্য পাথ গ্রহণ করে এবং আপেক্ষিক পাথ ফেরত দেয়। যদি কোনও আপেক্ষিক পথ উপস্থিত না থাকে তবে লক্ষ্যযুক্ত পথটি ফিরে আসে। File.separator অপ্রয়োজনীয়।

public static String getRelativePath (String baseDir, String targetPath) {
    String[] base = baseDir.replace('\\', '/').split("\\/");
    targetPath = targetPath.replace('\\', '/');
    String[] target = targetPath.split("\\/");

    // Count common elements and their length.
    int commonCount = 0, commonLength = 0, maxCount = Math.min(target.length, base.length);
    while (commonCount < maxCount) {
        String targetElement = target[commonCount];
        if (!targetElement.equals(base[commonCount])) break;
        commonCount++;
        commonLength += targetElement.length() + 1; // Directory name length plus slash.
    }
    if (commonCount == 0) return targetPath; // No common path element.

    int targetLength = targetPath.length();
    int dirsUp = base.length - commonCount;
    StringBuffer relative = new StringBuffer(dirsUp * 3 + targetLength - commonLength + 1);
    for (int i = 0; i < dirsUp; i++)
        relative.append("../");
    if (commonLength < targetLength) relative.append(targetPath.substring(commonLength));
    return relative.toString();
}

0

এখানে এমন একটি পদ্ধতি যা ভিত্তি পথ থেকে আপেক্ষিক পথটি সমাধান করে তা নির্বিশেষে তারা একই বা ভিন্ন মূলের:

public static String GetRelativePath(String path, String base){

    final String SEP = "/";

    // if base is not a directory -> return empty
    if (!base.endsWith(SEP)){
        return "";
    }

    // check if path is a file -> remove last "/" at the end of the method
    boolean isfile = !path.endsWith(SEP);

    // get URIs and split them by using the separator
    String a = "";
    String b = "";
    try {
        a = new File(base).getCanonicalFile().toURI().getPath();
        b = new File(path).getCanonicalFile().toURI().getPath();
    } catch (IOException e) {
        e.printStackTrace();
    }
    String[] basePaths = a.split(SEP);
    String[] otherPaths = b.split(SEP);

    // check common part
    int n = 0;
    for(; n < basePaths.length && n < otherPaths.length; n ++)
    {
        if( basePaths[n].equals(otherPaths[n]) == false )
            break;
    }

    // compose the new path
    StringBuffer tmp = new StringBuffer("");
    for(int m = n; m < basePaths.length; m ++)
        tmp.append(".."+SEP);
    for(int m = n; m < otherPaths.length; m ++)
    {
        tmp.append(otherPaths[m]);
        tmp.append(SEP);
    }

    // get path string
    String result = tmp.toString();

    // remove last "/" if path is a file
    if (isfile && result.endsWith(SEP)){
        result = result.substring(0,result.length()-1);
    }

    return result;
}

0

ডোনালের পরীক্ষাগুলি পাস করে, একমাত্র পরিবর্তন - যদি সাধারণ রুট না হয় তবে এটি লক্ষ্য পথে ফিরে আসে (এটি ইতিমধ্যে আপেক্ষিক হতে পারে)

import static java.util.Arrays.asList;
import static java.util.Collections.nCopies;
import static org.apache.commons.io.FilenameUtils.normalizeNoEndSeparator;
import static org.apache.commons.io.FilenameUtils.separatorsToUnix;
import static org.apache.commons.lang3.StringUtils.getCommonPrefix;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.join;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class ResourceUtils {

    public static String getRelativePath(String targetPath, String basePath, String pathSeparator) {
        File baseFile = new File(basePath);
        if (baseFile.isFile() || !baseFile.exists() && !basePath.endsWith("/") && !basePath.endsWith("\\"))
            basePath = baseFile.getParent();

        String target = separatorsToUnix(normalizeNoEndSeparator(targetPath));
        String base = separatorsToUnix(normalizeNoEndSeparator(basePath));

        String commonPrefix = getCommonPrefix(target, base);
        if (isBlank(commonPrefix))
            return targetPath.replaceAll("/", pathSeparator);

        target = target.replaceFirst(commonPrefix, "");
        base = base.replaceFirst(commonPrefix, "");

        List<String> result = new ArrayList<>();
        if (isNotEmpty(base))
            result.addAll(nCopies(base.split("/").length, ".."));
        result.addAll(asList(target.replaceFirst("^/", "").split("/")));

        return join(result, pathSeparator);
    }
}

0

আপনি যদি একটি ম্যাভেন প্লাগইন লেখার থাকে, তাহলে আপনি ব্যবহার করতে পারেন জালক 'PathTool :

import org.codehaus.plexus.util.PathTool;

String relativeFilePath = PathTool.getRelativeFilePath(file1, file2);

0

জেআরই 1.5 রানটাইম বা মাভেন প্লাগইনের জন্য যদি পাথগুলি উপলব্ধ না থাকে

package org.afc.util;

import java.io.File;
import java.util.LinkedList;
import java.util.List;

public class FileUtil {

    public static String getRelativePath(String basePath, String filePath)  {
        return getRelativePath(new File(basePath), new File(filePath));
    }

    public static String getRelativePath(File base, File file)  {

        List<String> bases = new LinkedList<String>();
        bases.add(0, base.getName());
        for (File parent = base.getParentFile(); parent != null; parent = parent.getParentFile()) {
            bases.add(0, parent.getName());
        }

        List<String> files = new LinkedList<String>();
        files.add(0, file.getName());
        for (File parent = file.getParentFile(); parent != null; parent = parent.getParentFile()) {
            files.add(0, parent.getName());
        }

        int overlapIndex = 0;
        while (overlapIndex < bases.size() && overlapIndex < files.size() && bases.get(overlapIndex).equals(files.get(overlapIndex))) {
            overlapIndex++;
        }

        StringBuilder relativePath = new StringBuilder();
        for (int i = overlapIndex; i < bases.size(); i++) {
            relativePath.append("..").append(File.separatorChar);
        }

        for (int i = overlapIndex; i < files.size(); i++) {
            relativePath.append(files.get(i)).append(File.separatorChar);
        }

        relativePath.deleteCharAt(relativePath.length() - 1);
        return relativePath.toString();
    }

}

-1

org.apache.ant একটি getRelativePath পদ্ধতি সহ একটি ফাইলUtils ক্লাস আছে। এখনও নিজে চেষ্টা করে দেখিনি, তবে এটি পরীক্ষা করে দেখার পক্ষে উপযুক্ত হতে পারে।

http://javadoc.haefelinger.it/org.apache.ant/1.7.1/org/apache/tools/ant/util/FileUtils.html#getRelativePath(java.io.File , java.io.File)


-1
private String relative(String left, String right){
    String[] lefts = left.split("/");
    String[] rights = right.split("/");
    int min = Math.min(lefts.length, rights.length);
    int commonIdx = -1;
    for(int i = 0; i < min; i++){
        if(commonIdx < 0 && !lefts[i].equals(rights[i])){
            commonIdx = i - 1;
            break;
        }
    }
    if(commonIdx < 0){
        return null;
    }
    StringBuilder sb = new StringBuilder(Math.max(left.length(), right.length()));
    sb.append(left).append("/");
    for(int i = commonIdx + 1; i < lefts.length;i++){
        sb.append("../");
    }
    for(int i = commonIdx + 1; i < rights.length;i++){
        sb.append(rights[i]).append("/");
    }

    return sb.deleteCharAt(sb.length() -1).toString();
}

-2

Psuedo-কোড:

  1. পাথ পৃথককারী দ্বারা স্ট্রিংগুলি বিভক্ত করুন ("/")
  2. বিভক্ত স্ট্রিংয়ের ফলাফলের মাধ্যমে পুনরাবৃত্তি করে সর্বাধিক সাধারণ পাথ সন্ধান করুন (যাতে আপনি আপনার দুটি উদাহরণে "/ var / ডেটা" বা "/ a" দিয়ে শেষ করতে চান)
  3. return "." + whicheverPathIsLonger.substring(commonPath.length);

2
এই উত্তরটি সেরাভাবে একটি হ্যাক। জানালা সম্পর্কে কি?
কিউক্স - মনিকা
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.