简体   繁体   中英

java.io: Rename all elements of an absolute File path

I am writing a little tool for "normalizing" file and directory names to make them safe for portability, interoperability and general reduction of stupid and painful errors due to file names with non-ASCII characters, spaces and other evil characters etc. (see this SuperUser answer for a concise summary of the few truly safe characters).

To do that, I want to rename an file/directory with an unsafe name to a safe name, including renaming all parent directories accordingly. Shouldn't be a problem, even with java.io that I need to use; however I cannot seem to rename directories containing other directories (containing files works fine).

This is my method:

public static File renameSafe(File unsafe) throws IOException {
    if (!unsafe.exists()) {
        throw new FileNotFoundException(unsafe.toString());
    }

    // create full "safe" path
    File safe = toSafeFile(unsafe);
    File origSafe = safe;

    while (unsafe != null) {
        // rename the lowest unsafe level
        File unsafeParent = unsafe.getParentFile();
        File safeTarget = new File(unsafeParent, safe.getName());
        if (!unsafe.renameTo(safeTarget)) {
            throw new IOException("Could not rename " + unsafe + " to " + safe);
        }

        unsafe = unsafeParent;
        safe = safe.getParentFile();
    }

    return origSafe;
}

When I run this main method:

public static void main(String[] args) throws IOException {
    File file = new File("\\test\\-bad.folder NAME ÄÖÜßéÂÌ\\.bad folder 2\\test filename-ÄÖÜ.txt");
    System.out.println(file);
    System.out.println(toSafeFileName(file));
    renameSafe(file);
}

I get the following output:

\test\-bad.folder NAME ÄÖÜßéÂÌ\.bad folder 2\test filename-ÄÖÜ.txt
\test\_bad_folder_name_aou-eai\_bad_folder_2\test_filename-aou.txt
Exception in thread "main" java.io.IOException: Could not rename \test\-bad.folder NAME ÄÖÜßéÂÌ to \test\_bad_folder_name_aou-eai
    at SafeFileName.renameSafe(SafeFileName.java:77)
    at SafeFileName.main(SafeFileName.java:90)

The first two levels, everything is renamed as is; reaching the leaf file's grandparent, renameTo() suddenly returns false .

So my question is, what am I doing wrong? I tried with other file names (including with 100% input file names), but it always seems that File.renameTo() can not seem to rename directories containing other directories. However, my personal bet would be some stupidity of myself, paired with some obscure API details.

EDIT:

Okay, here's the SSCCE:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

public class RenameTest
{
    public static File toSafeFileName(File file) {
        File safe = new File(file.getName().replace("bad", "good"));

        for (;;) {
            file = file.getParentFile();
            if (file == null) {
                return safe;
            }

            if (file.toString().equals(File.separator)) {
                safe = new File("", safe.getPath());
            } else {
                safe = new File(
                        file.getName().replace("bad", "good"),
                        safe.getPath());
            }
        }
    }

    public static File renameSafe(File unsafe) throws IOException {
        if (!unsafe.exists()) {
            throw new FileNotFoundException(unsafe.toString());
        }

        File safe = toSafeFileName(unsafe);
        File origSafe = safe;

        while (unsafe != null && !unsafe.equals(safe)) {
            File unsafeParent = unsafe.getParentFile();
            File safeTarget = new File(unsafeParent, safe.getName());

            System.out.print(unsafe + " -> " + safeTarget + "? ");

            if (unsafe.renameTo(safeTarget)) {
                System.out.println("OK.");
            } else {
                System.out.println("Error!");
                throw new IOException();
            }

            unsafe = unsafeParent;
            safe = safe.getParentFile();
        }

        return origSafe;
    }

    public static void main(String[] args) throws IOException {
        File unsafePath = new File("\\test\\bad1\\bad2\\bad.txt");
        if (!unsafePath.getParentFile().mkdirs()) {
            throw new IOException("can't create " + unsafePath.getParentFile());
        }
        if (!unsafePath.createNewFile()) {
            throw new IOException("can't create dummy file");
        }

        File safePath = renameSafe(unsafePath);
        if (safePath.exists()) {
            System.out.println("Renamed " + unsafePath + " to " + safePath);
        } else {
            throw new IOException("Safe path " + safePath + " does not exist.");
        }
    }
}

On first run, with a clean D:\\test directory, everything runs fine:

\test\bad1\bad2\bad.txt -> \test\bad1\bad2\good.txt? OK.
\test\bad1\bad2 -> \test\bad1\good2? OK.
\test\bad1 -> \test\good1? OK.
Renamed \test\bad1\bad2\bad.txt to \test\good1\good2\good.txt

So OK, Java can rename directories containing directories.

BUT, on the second run (without cleaning D:\\test first), I get the following:

\test\bad1\bad2\bad.txt -> \test\bad1\bad2\good.txt? OK.
\test\bad1\bad2 -> \test\bad1\good2? OK.
\test\bad1 -> \test\good1? Error!
Exception in thread "main" java.io.IOException
    at funky.core.io.RenameTest2.renameSafe(RenameTest2.java:46)
    at funky.core.io.RenameTest2.main(RenameTest2.java:65)

So it seems that the problems really was I didn't clean up my test directory, and that Java can't rename a directory to an already existing directory -- as the good1 was already created on the first run... As this is stated in the renameTo() API, this was indeed just my own stupidity. Sorry for wasting your time.

To me, this...

File unsafeParent = unsafe.getParentFile();
File safeTarget = new File(unsafeParent, safe.getName());

So, using \\test\\-bad.folder NAME ÄÖÜßéÂÌ\\.bad folder 2\\test filename-ÄÖÜ.txt as the base path, unsafeParent would equal \\test\\-bad.folder NAME ÄÖÜßéÂÌ\\.bad folder 2 , but safeTarget is now unsafeParent + safe.getName() , so you're not trying to rename the directory, but a file in unsafeParent named safe.getName() ...

Instead of working bottom up, work top down. This means you can rename the directory and using the renamed reference, list the files in it and process them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM