tags:

views:

3434

answers:

7

In my Java application I am renaming files to a file name provided in a String parameter. There is a method

boolean OKtoRename(String oldName, String newName)

which basically checks whether the newName isn't already taken by some other file, as I wouldn't want to bury existing ones.

It now occurred to me that perhaps the newName String will not denote a valid file name. So I thought to add this check to the method:

if (new File(newName).isFile()) { 
    return false; 
}

Which obviously isn't the right way to do it, since in most cases the newFile does not yet exist and therefore although it is OKtoRename, the function returns false.

I was wondering, is there a method (I know there isn't for the java.io.File objects) like canExist()? Or would I have to resort to regex to make sure the newFile String does not contain invalid characters (e.g. ?, *, ", :)? I wonder if there is perhaps a function hidden somewhere in the JDK that would tell me if a string could possibly denote a valid file name.

+12  A: 

Use createNewFile(), which will atomically create the file only if it doesn't yet exist.

If the file is created, the name is valid and it is not clobbering an existing file. You can then open the files and efficiently copy data from one to the other with FileChannel.transferXXX operations.

An important thing to keep in mind that, in general, the check and the creation should be atomic. If you first check whether an operation is safe, then perform the operation as a separate step, conditions may have changed in the meantime, making the operation unsafe.

Additional food for thought is available at this related post: "Move/Copy operations in Java."

erickson
You'll want to delete the file you just created, though, because the function OKtoRename isn't supposed to actually change the file system, just answer if the file name would work.
Matt Poush
@Matt: Ooor, you just create the file and if createNewFile return false, it means the file name was not valid ( ... or could not be created :-S ) ¬¬
OscarRyz
exactly, I think I'll go with this solution but I don't like the idea of creating an empty file just to delete it immediately afterwards...
Peter Perháč
You really shouldn't delete the file if there's any possibility that another process or thread could be creating a file of the same name. (And if there isn't, why are you bothering to check in the first place?)
erickson
Sorry for the focus on concurrency; I originally interpreted that as the primary question. However, actually creating the file is the only way to see if the name is valid. You can check out my question http://stackoverflow.com/questions/122400/what-are-reserved-filenames-for-various-platforms about reserved names on different platforms if you are interested in trying to go that route.
erickson
@Oscar I agree, it makes me feel wasteful to touch and then delete a file. The problem with not deleting it is you're adding side effects to a boolean function. The best bet would be to move createNewFile out of the OKto function, and if the createNewFile returns false, just bail out with 'return' (or throw an exception, etc).
Matt Poush
@Matt Poush, @MasterPeter: I don't think erickson was suggesting to create a file then delete it. It seems like the proper approach is to try to rename the file in one step and not test for "ok" first. If that step fails it wasn't ok. If it succeeds it was ok.
Mr. Shiny and New
That's right. Let me re-emphasize: **"in general,** *the check and the creation should be atomic. If you first check whether an operation is safe, then perform the operation as a separate step, conditions may have changed in the meantime, making the operation unsafe."* If this doesn't seem to fit a particular situation, you should be able to articulate a clear defense for making an exception and opening the door to concurrency problems.
erickson
A: 

Look here: The Java Developers Almanac 1.4

boolean exists = (new File("filename")).exists();
if (exists) {
    // File or directory exists
} else {
    // File or directory does not exist
}
mr.sverrir
this doesn't help me. OKtoRename("valid.file", "$:??") would check if $:?? exists (no) then the method would return true, since if the new filename is not yet taken, the old file can be renamed to the new file name
Peter Perháč
What about race conditions where another process runs between the existence check and the creation of the file?
erickson
Yeah, that would be a mess, but there is no such danger.
Peter Perháč
A: 

Use exists() method.

cartman
A: 

Determining If a File or Directory Exists

boolean exists = (new File("filename")).exists();
Gordian Yuan
+4  A: 

I assembled a list of illegal filename characters (considering UNIX, Mac OS X and Windows systems) based on some online research a couple of months ago. If the new filename contains any of these, there's a risk that it might not be valid on all platforms.

private static final char[] ILLEGAL_CHARACTERS = { '/', '\n', '\r', '\t', '\0', '\f', '`', '?', '*', '\\', '<', '>', '|', '\"', ':' };

EDIT: I would like to stress, that this is not a complete solution: as a commenter pointed out, even though it passes this test your file name could still be a Windows specific keyword like COM, PRN, etc. However, if your file name contains any of these characters, it will certainly cause trouble in a cross-platform environment.

Zsolt Török
But don't forget keywords, like trying to create a file called COM, COM1, COM2, PRN, etc. (at least on Windows platforms)
Instantsoup
Point taken, haven't even thought about that...
Zsolt Török
while i realize this isnt a complete solution, this worked great for me (where only a part of the file name is dynamic and so keywords are not a risk).thank you for compiling this list.
hatchetman82
Added a warning to indicate that this is only a partial solution.
Zsolt Török
+1  A: 

To me it appears to be an OS dependent problem. You may simply want to check for some invalid character in the file name. Windows does this when you try to rename the file, it pops a message saying that a file cannot contain any of the following characters: \ / : * ? < > | I am not sure if your question is "is there a library doing the job for me?" in that case I don't know any.

svachon
one should probably also check the length of the filename.
Paulo Guedes
A: 

Using

String validName = URLEncoder.encode( fileName , "UTF-8");

File newFile = new File( validName );

Does the work.

I have just found today. I'm not sure if it works 100% of the time, but so far, I have been able to create valid file names.

OscarRyz
Sorry, but if there were stars in the fileName, your code would not help. System.out.println(URLEncoder.encode( "*hello world*", "UTF-8")); prints out *hello+world* And that would not be a valid file name.
Peter Perháč
Great!.. You are correct, actually I was about to ask this. :)
OscarRyz