有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java不区分大小写文件。在区分大小写的文件系统上等于

我有一个字符串形式的文件路径。在Java中,我需要确定文件系统中是否存在该文件(我们的代码需要跨平台,因为它在Windows、Linux和OS X上运行)

问题是,文件路径和文件本身的大小写可能不匹配,即使它们确实代表同一个文件(可能这是因为它们起源于Windows,并且没有注意到差异)

例如,我有一个文件路径“ABC.txt”。文件系统中存在一个名为“abc.txt”的文件。以下代码将在Windows上返回true,但在Linux上返回false

new File("ABC.txt").exists();

确定文件是否存在的最佳方法是什么,以及是否存在该文件以在文件系统上返回该文件的句柄


共 (6) 个答案

  1. # 1 楼答案

    正如jwaddell所说,看起来非常慢的递归路径检查(显然)是实现这一点的唯一方法。下面是我用java编写的函数,它接受一个字符串,即文件路径。如果文件路径的字符串表示形式存在,并且与windows报告的大小写敏感度相同,则返回true,否则返回false

    public boolean file_exists_and_matches_case(
            String full_file_path) {
    
        //Returns true only if:
        //A. The file exists as reported by .exists() and
        //B. Your path string passed in matches (case-sensitivity) the entire
        //   file path stored on disk.
    
        //This java method was built for a windows file system only,
        //no guarantees for mac/linux/other.
        //It takes a String parameter like this:
        //"C:\\projects\\eric\\snalu\\filename.txt"
        //The double backslashes are needed to escape the one backslash.
    
        //This method has partial support for the following path:
        //"\\\\yourservername\\foo\\bar\\eleschinski\\baz.txt".
        //The problem is it stops recusing at directory 'foo'.
        //It ignores case at 'foo' and above.  So this function 
        //only detects case insensitivity after 'foo'.
    
    
        if (full_file_path == null) {
            return false;
        }
    
        //You are going to have to define these chars for your OS.  Backslash
        //is not specified here becuase if one is seen, it denotes a
        //directory delimiter:  C:\filename\fil\ename
        char[] ILLEGAL_CHARACTERS = {'/', '*', '?', '"', '<', '>', '>', '|'};
        for (char c : ILLEGAL_CHARACTERS) {
            if (full_file_path.contains(c + "")) {
                throw new RuntimeException("Invalid char passed in: "
                        + c + " in " + full_file_path);
            }
        }
    
        //If you don't trim, then spaces before a path will 
        //cause this: 'C:\default\ C:\mydirectory'
        full_file_path = full_file_path.trim();
        if (!full_file_path.equals(new File(full_file_path).getAbsolutePath()))
        {
            //If converting your string to a file changes the directory in any
            //way, then you didn't precisely convert your file to a string.
            //Programmer error, fix the input.
            throw new RuntimeException("Converting your string to a file has " +
                "caused a presumptous change in the the path.  " + full_file_path +
                " to " + new File(full_file_path).getAbsolutePath());
        }
    
        //If the file doesn't even exist then we care nothing about
        //uppercase lowercase.
        File f = new File(full_file_path);
        if (f.exists() == false) {
            return false;
        }
    
        return check_parent_directory_case_sensitivity(full_file_path);
    }
    
    public boolean check_parent_directory_case_sensitivity(
            String full_file_path) {
        //recursively checks if this directory name string passed in is
        //case-identical to the directory name reported by the system.
        //we don't check if the file exists because we've already done
        //that above.
    
        File f = new File(full_file_path);
        if (f.getParent() == null) {
            //This is the recursion base case.
            //If the filename passed in does not have a parent, then we have
            //reached the root directory.  We can't visit its parent like we
            //did the other directories and query its children so we have to
            //get a list of drive letters and make sure your passed in root
            //directory drive letter case matches the case reported
            //by the system.
    
            File[] roots = File.listRoots();
            for (File root : roots) {
                if (root.getAbsoluteFile().toString().equals(
                        full_file_path)) {
                    return true;
                }
            }
            //If we got here, then it was because everything in the path is
            //case sensitive-identical except for the root drive letter:
            //"D:\" does not equal "d:\"
            return false;
    
        }
    
        //Visit the parent directory and list all the files underneath it.
        File[] list = new File(f.getParent()).listFiles();
    
        //It is possible you passed in an empty directory and it has no
        //children.  This is fine.
        if (list == null) {
            return true;
        }
    
        //Visit each one of the files and folders to get the filename which
        //informs us of the TRUE case of the file or folder.
        for (File file : list) {
            //if our specified case is in the list of child directories then
            //everything is good, our case matches what the system reports
            //as the correct case.
    
            if (full_file_path.trim().equals(file.getAbsolutePath().trim())) {
                //recursion that visits the parent directory
                //if this one is found.
                return check_parent_directory_case_sensitivity(
                        f.getParent().toString());
            }
        }
    
        return false;
    
    }
    
  2. # 2 楼答案

    这是我的Java 7解决方案,适用于父路径已知且相对子路径可能与磁盘上的路径不同的情况

    例如,给定文件/tmp/foo/biscuits,该方法将使用以下输入向文件正确返回Path

    • /tmpfoo/biscuits
    • /tmpfoo/BISCUITS
    • /tmpFOO/BISCUITS
    • /tmpFOO/biscuits

    请注意,此解决方案尚未经过可靠的测试,因此应将其视为一个起点,而不是一个可用于生产的片段

    /**
     * Returns an absolute path with a known parent path in a case-insensitive manner.
     * 
     * <p>
     * If the underlying filesystem is not case-sensitive or <code>relativeChild</code> has the same
     * case as the path on disk, this method is equivalent to returning
     * <code>parent.resolve(relativeChild)</code>
     * </p>
     * 
     * @param parent parent to search for child in
     * @param relativeChild relative child path of potentially mixed-case
     * @return resolved absolute path to file, or null if none found
     * @throws IOException
     */
    public static Path getCaseInsensitivePath(Path parent, Path relativeChild) throws IOException {
    
        // If the path can be resolved, return it directly
        if (isReadable(parent.resolve(relativeChild))) {
            return parent.resolve(relativeChild);
        }
    
        // Recursively construct path
        return buildPath(parent, relativeChild);
    }
    
    private static Path buildPath(Path parent, Path relativeChild) throws IOException {
        return buildPath(parent, relativeChild, 0);
    }
    
    /**
     * Recursively searches for and constructs a case-insensitive path
     * 
     * @param parent path to search for child
     * @param relativeChild relative child path to search for
     * @param offset child name component
     * @return target path on disk, or null if none found
     * @throws IOException
     */
    private static Path buildPath(Path parent, Path relativeChild, int offset) throws IOException {
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(parent)) {
            for (Path entry : stream) {
    
                String entryFilename = entry.getFileName().toString();
                String childComponent = relativeChild.getName(offset).toString();
    
                /*
                 * If the directory contains a file or folder corresponding to the current component of the
                 * path, either return the full path (if the directory entry is a file and we have iterated
                 * over all child path components), or recurse into the next child path component if the
                 * match is on a directory.
                 */
                if (entryFilename.equalsIgnoreCase(childComponent)) {
                    if (offset == relativeChild.getNameCount() - 1 && Files.isRegularFile(entry)) {
                        return entry;
                    }
                    else if (Files.isDirectory(entry)) {
                        return buildPath(entry, relativeChild, offset + 1);
                    }
                }
            }
        }
    
        // No matches found; path can't exist
        return null;
    }
    
  3. # 3 楼答案

    此方法将告诉您是否存在具有确切名称的文件(路径部分不区分大小写)

    public static boolean caseSensitiveFileExists(String pathInQuestion) {
      File f = new File(pathInQuestion);
      return f.exists() && f.getCanonicalPath().endsWith(f.getName());
    }
    
  4. # 4 楼答案

    从目录(^{})中获取文件列表,并使用equalsIgnoreCase()比较名称

  5. # 5 楼答案

    至于问题的第一部分:使用Path.toRealPath。它不仅处理大小写敏感,还处理符号链接(取决于作为参数提供的选项),等等。这需要Java 7或更高版本

    至于问题的第二部分:不确定你说的“把手”是什么意思

  6. # 6 楼答案

    如果差异是随机的,那么对我来说,Shimi的解决方案包括递归路径段检查是最好的解决方案。乍一看,这听起来很难看,但您可以将魔法隐藏在一个单独的类中,并实现一个简单的API来返回给定文件名的文件句柄,这样您就可以看到类似于Translator.translate(file)调用的东西

    也许,这种差异是静态的、可预测的。那么我更喜欢一本字典,它可以用来将给定的文件名转换为Windows/Linux文件名。与其他方法相比,这种方法有很大的优势:获得错误文件句柄的风险更小

    如果字典是静态的,那么可以创建并维护一个属性文件。如果它是静态的,但更复杂,比如一个给定的文件名可以被翻译成多个可能的目标文件名,我会用Map<String, Set<String>>数据结构备份这个口述类(Set优先于List,因为没有重复的替代项)