Suppose I create a txt file and save it as "Untitled1". I enter eclipse and type the following:
import java.io.*;
public class Test {
public static void main(String[] args){
File f = new File("Untitled1.txt");
boolean isDeleted = f.delete();
System.out.println(isDeleted);
}
}
False was returned from the delete method indicating the file was not deleted. I understand that a file object represents the location of a file and NOT the contents of the file. But then what is actually being deleted? How do you delete a location of a file, without deleting the contents of a file itself?
I also entered the file Path for the Untitled1 file as a parameter to the File objects constructor, that did not delete the Untitled1.txt file either.
A file is identified by its path through the file system, beginning from the root node.
Representation of the path depends on the system. e.g. in windows C:\foo\bar while in linux /home/foo/bar.
So in below code, string path would be converted into abstract pathname and it would create the File instance and when you call the delete method it will try to delete the node. Basically content and path are not really different.
File f = new File("Untitled1.txt");
So, the file itself is deleted. False could be returned for a variety of reasons.
public boolean delete()
Deletes the file or directory denoted by this abstract pathname. If this pathname denotes a directory, then the directory must be empty in order to be deleted.
Note that the Files class defines the delete method to throw an IOException when a file cannot be deleted. This is useful for error reporting and to diagnose why a file cannot be deleted.
Returns:
true if and only if the file or directory is successfully deleted; false otherwise
Throws:
SecurityException - If a security manager exists and its SecurityManager.checkDelete(java.lang.String) method denies delete access to the file
Catch the SecurityException and you will probably find that you are disallowed from directly deleting the file programmatically.
First, please note that File is an old, bad class, that should not be used nowadays.
It's a lot better to use the Files class, more specifically its delete method.
As for what exactly is deleted: a file is a bunch of bytes that sits in a hard disk. The disk - or one of its partitions - is formatted into a filesystem, which is a data structure that organizes directories and files. The particular filesystem dictates how the file is broken up into pieces, how these pieces can be located or added by operations such as seek, read, write etc.
A filesystem has directories, which give you start points for the files. The file path in the hierarchy of directories tells the system where to find the file, including all its information (like pointer to its start, read/write permissions, etc.) The information in the directory that tells where the actual file contents is is sometimes called a "link" (at least in Unix file systems).
When you delete a file, the usual thing that happens is that the particular link to that file from that directory is removed. If that is the last link (the file can be linked from more than one directory, at least in some file systems), the blocks that belong to the file are also marked as free so that they can be allocated to another file.
So your File object tells the system where the file is, but the delete operation ultimately tells the system both to unlink the file from the directory (the penultimate part of the path), and if that's the last link, it also tells the system to go to the file contents and mark it as free.
This is a general description. The exact details and what happens when the content is marked as free is dependent on the particular filesystem used (e.g. ext4,reiserFS... (Linux), HFS+ (MacOS X), NTFS,FAT32... (Windows)).
Related
So I have a project, and this is one of the demands:
You should have a class named Project3, containing a main method.
This program reads the levels information from a file whose name is
specified as a command-line parameter (The file should also be
relative to the class-path as described here:)
All the file names specified in the levels and block definition files
should be relative to the class path. The reason we want them to be
relative to the class path is that later we will be able to read the
files from inside a jar, something we can not do with regular File
references.
To get an input stream relative to the class path (even if it's inside
a jar), use the following:
InputStream is =
ClassLoader.getSystemClassLoader().getResourceAsStream("image.png");
The idea is to keep a folder with files(definitions and images) and
then add that folder to the class path when running the JVM:
java -cp bin:resources ... If you don't add the resources folder to
you class path you wont be able to load them with the command from
above.
When run without parameters, your program should read a default level
file, and run the game accordingly. The location of the default level
file should be hard-coded in your code, and be relative to the
classpath_.
When run without parameters, your program should read a default level file, and run the game accordingly. The location of the default level file should be hard-coded in your code, and be relative to the classpath_.
The part of the code that handles the input is:
public Void run() throws IOException {
LevelReader level = new LevelReader();
List<level> chosenLevels = new ArrayList<>();
if (args.length >= 1) {
File f = new File(args[0]);
if (f.exists()) {
chosenLevels = level.makeLevel(args[0]);
}
}
if (chosenLevels.size() == 0) {
game.runLevels(defaultLevels);
} else {
game.runLevels(chosenLevels);
}
return null;
}
So my question is:
An argument should be the full path of a file which means:
D:\desktop\level3.txt
Is it possible to read a file from every location on my computer?
Because right now I can do it only if my text file is in the
project's directory (not even in the src folder).
I can't understand the rest of their demands. What does is mean "should be hard-coded in your code, and be relative to the
classpath_." and why is it related to InputStream method(?)
I'm confused all over this.
Thanks.
A classpath resource is not the same as a file.
As you have correctly stated, the full path of a file is something like D:\desktop\level3.txt.
But if ever want to distribute your application so it can run on other computers, which probably won’t have that file in that location, you have two choices:
Ask the user to tell the program where to find the file on their computer.
Bundle the file with the compiled program.
If you place a non-.class file in the same place as .class files, it’s considered a resource. Since you don’t know at runtime where your program’s class files are located,¹ you use the getResource or getResourceAsStream method, which is specifically designed to look in the classpath.
The getResource* methods have the additional benefit that they will work both when you are developing, and when the program is packaged as a .jar file. Individual entries in a .jar file are not separate files and cannot be read using the File or FileInputStream classes.
If I understand your assignment correctly, the default level file should be an application resource, and the name of that resource is what should be hard-coded in your program. Something like:
InputStream is;
if (args.length > 0) {
is = new BufferedInputStream(
new FileInputStream(args[0]));
} else {
// No argument provided, so use program's default level data.
is = ClassLoader.getSystemClassLoader().getResourceAsStream("defaultlevel.txt");
}
chosenLevels = level.makeLevel(is);
¹ You may find some pages that claim you can determine the location of a running program’s code using getProtectionDomain().getCodeSource(), but getCodeSource() may return null, depending on the JVM and ClassLoader implementation, so this is not reliable.
To answer your first question, it doesn't seem like they're asking you to read from anywhere on disk, just from within your class path. So that seems fine.
The second question, "What does is mean 'should be hard-coded in your code, and be relative to the classpath'?". You are always going to have a default level file in your project directory. Define the path to this file as a String in your program and that requirement will be satisfied. It's related to the InputStream because the stream requires a location to read in from.
I have the below code whereby I create a File type based on a pre-created file "test.brd" and also call the getAbsolutePath() method on this File, this all works correctly. However, when I run the exists() method, this is deemed as not existing.
When I debug, the status of the File is null and the path is also null, yet the getAbsolutePath() method works. I have debugged and it goes to the Security section of the exists() method.
Please see below:
File inputFile = new File("/Users/myname/Desktop/project_name/test.brd");
// The below works and returns the path
System.out.println(inputFile.getAbsolutePath());
if (inputFile.exists()) {
System.out.println("Exists");
}
else {
System.out.println("Invalid");
}
Even when I construct the file without the absolute path and just give the file name as a parameter (stored locally with Java file) the correct absolute path is provided.
Hope this makes sense. All I want to do is read a pre-created file into an Array, each character is an element in the array, I was intending on using scanner to read the file, but inputFile does not exist to be read.
The two methods are about different aspects of the file:
getAbsolutePath() is about file name. In a way, this is a "string manipulation method" completely separated from the actual file system
exists() is about the actual file. It checks whether or not the file is present in the file system at the location identified by the given path.
Note that getAbsolutePath() and other path manipulation methods of File must work even without the file or the folder being present in the actual file system. Otherwise, the API would not be able to support file creation, e.g. through createNewFile().
If you take a look at the javadoc, you can find the following sentence
Instances of this class may or may not denote an actual file-system object such as a file or a directory.
Proving that the instance in memory of a File object is not necessarily a real file or directory existing in the file system.
File inputFile = new File("/Users/myname/Desktop/project_name/test.brd");
The line above doesn't create a new File and hence it doesn't exists.
If you want to create a file you can use method inputFile.createNewFile().
The method getAbsolutePath() works on the inputFile object and is completely different from file creation.
The following code file.renameTo(new File(newPath)); dosen't rename all the files properly it skips over some I have even used Files.move(file.toPath(), Paths.get(newPath)); but i get an exception error in eclipse saying java.nio.file.FileAlreadyExistsException which i think is occurring because there are sets of files that when they are cut off they will have the same name is there a way to bypass this error in eclipse or fine tune the renameTo()?
I also have tried .substring(0,22);, name.replaceFirst("-2017.*", ""); and
name.substring(0, file.getName().indexOf("-2017") same result.
example:
orginal file name: 3-M-ALABAMA-SUIQUARTER2-2017200346-CD6140
Console Output: 3-M-ALABAMA-SUIQUARTER2
some of Files in folder unchanged: 3-M-ALABAMA-SUIQUARTER2-2017200346-CD6140
for(File file:filesInDir) {
String name = file.getName().substring(0, file.getName().indexOf("-2017"));
String newName = name;
System.out.println(newName); // prints prints to file
String newPath = absolutePathOne + "\\" + newName;
file.renameTo(new File(newPath));
or
Files.move(file.toPath(), Paths.get(newPath));
You can not rename a particular file to a file name that already exists within the folder you are renaming the file in. IMHO ... Even if you can, you shouldn't for a bunch of common sense reasons.
In other words, if we have a folder (directory) named: All_My_Files and in this folder we have two text files, one is named MyFile-2016.txt and the other is named MyFile-2017.txt. Now, we want to rename each of these two files so that the dash and the year (ie: -2016 or -2017) from each file name no longer exists. Essentially what you will end up trying to do is have both file names be MyFile.txt which is not allowed. Your first rename will be fine since on the first go at it there is no file within the folder named MyFile.txt but once the second rename attempt is done on the second file name it's simply going to fail since the name MyFile.txt already exists within that folder which was done from the first rename attempt. This not a code problem, this is an issue with the local file system. Those are the rules of the local file system (No file can have the same name within the same folder). Look at the file names you are going to rename, are there any that will actually create the very same file name once you remove the unwanted text? If there are then those will fail to rename.
The same applies to Moving files. You can not move a file to a folder (directory) that already contains a file with the very same name. The file system rule above applies. You can however overwrite the existing duplicate file name within the destination path if it exists during a move if you tell the Files.move() method to do so:
Files.move(sourcePathAndFileName, destinationPathAndFileName,
StandardCopyOption.REPLACE_EXISTING);
You will need to import:
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
Keep in mind though, before you blatantly overwrite an existing file you better make pretty sure that this is what you want to do. Prompting the User to carry out an overwrite would be a normal course of action but doesn't necessarily need to be the case for specific in house operations.
I want to know why the JVM doesn't throw any exception when it is asked to create an invalid path such "C:invalidPath".
For example :
public static void main(String[] args) {
File f = new File("C:invalidPath");
f.mkdir();
}
The previous example create a folder named "invalidPath" in the current folder. I think that this is not normal .
The path you have specified is a legal path on Microsoft Windows. It is specified as path relative to the current working directory on the volume C. On Windows, as on DOS before it, each volume has a separate working directory associated with it, and additionally there is the notion of the current working volume.
This is not the JVM's fault or responsibility.
It is up to the underlying filesystem to convert a path specification (i.e. string) into the actual logical files that the name represents. The JVM just passes through this string to the filesystem, which determines how to handle it.
So the real question is "why does NTFS [assuming that's what you're using] interpret 'C:invalidPath' as creating that file in the current directory?". And the reason is simply, that's how paths are defined for this filesystem.
From the documentation:
If a file name begins with only a disk designator but not the backslash after the colon, it is interpreted as a relative path to the current directory on the drive with the specified letter. Note that the current directory may or may not be the root directory depending on what it was set to during the most recent "change directory" operation on that disk. Examples of this format are as follows:
"C:tmp.txt" refers to a file named "tmp.txt" in the current directory on drive C.
In a unit test I am overwriting a config file to test handling bad property values.
I am using Apache Commons IO:
org.apache.commons.io.FileUtils.copyFile(new File(configDir, "xyz.properties.badValue"), new File(configDir, "xyz.properties"), false)
When investigating the file system I can see that xyz.properties is in fact overwritten - size is updated and the content is the same as that of xyz.properties.badValue.
When I complete the test case which goes through code that reads the file into a Properties object (using a FileReader object) I get the properties of the original xyz.properties file, not the newly copied version.
Through debugging where I single step and investigate the file I can rule out it being a timing issue of writing to the file system.
Does the copy step somehow hold a file handle? If so how would I release it again?
If not, does anybody have any idea why this happens and how to resolve it?
Thanks.
If you initialized the FileReader object before this object, then it will have already stored a temp copy of the old version.
You'll need to reset it:
FileReader f = new FileReader("the.file");
// Copy and overwrite "the.file"
f = new FileReader("the.file");
In the Unix filesystem model, the inode containing the file's contents will persist as long as someone has an open filehandle into the file, or there is a directory entry pointing to it.
Replacing the file's name in the directory, does not remove the inode (contents of the file), so your already-open filehandle can continue to be used.
This is actually exploitable to create temporary files that never need to be cleaned up: create the file, then unlink it immediately, while keeping it open. When you close the file handle, the inode is reaped
I realize that this doesn't answer your question directly, but I think that it would be better to maintain two separate files, and arrange for your code to have the name of the configuration file configurable / injected at runtime. That way, your tests can specify which config file to use, rather than overwriting a single file.