I'd like to be able to rename a list of folders in order to remove unwanted characters (a dot and double space have to become a single space, for example).
Upon clicking a button in the Gui, you'll see a messagebox with the correctly formatted name appear which indicates that both the formatting is correct and the function is called.
When I look at the test folders I've created, the names aren't changed (not even after refreshing). Using a hardcoded string doesn't work either.
What am I overlooking?
public void cleanFormat() {
for (int i = 0; i < directories.size(); i++) {
File currentDirectory = directories.get(i);
for (File currentFile : currentDirectory.listFiles()) {
String formattedName = "";
formattedName = currentFile.getName().replace(".", " ");
formattedName = formattedName.replace(" ", " ");
currentFile.renameTo(new File(formattedName));
JOptionPane.showMessageDialog(null, formattedName);
}
}
}
For future browsers: This was fixed with Assylias' comment. Below you will find the eventual code which fixed it.
public void cleanFormat() {
for (int i = 0; i < directories.size(); i++) {
File currentDirectory = directories.get(i);
for (File currentFile : currentDirectory.listFiles()) {
String formattedName = "";
formattedName = currentFile.getName().replace(".", " ");
formattedName = formattedName.replace(" ", " ");
Path source = currentFile.toPath();
try {
Files.move(source, source.resolveSibling(formattedName));
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Well, first of all the File.renameTo is trying to rename a file on the same filesystem.
The following is from java doc
Many aspects of the behavior of this method are inherently platform-dependent:
The rename operation might not be able to move a file from one filesystem to
another, it might not be atomic, and it might not succeed if a file with the
destination abstract pathname already exists.
The call to getName() returns just the name of the file and not any directory information. So you may be trying to rename the file to a different directory.
Try adding the containing directory to the file object you pass into rename
currentFile.renameTo(new File(currentDirectory, formattedName));
Also like others have said you should be checking the return value of renameTo which is probably false, or use the new methods in Files class which I've found to throw pretty informative IOExceptions.
First of all check return value, File.renameTo returns true if the renaming succeeded; false otherwise. E.g. you cannot rename / move a file from c: to d: on Windows.
And most importantly, use Java 7's java.nio.file.Files.move instead.
Related
My task is to save a file in the path specified by the end user (must be an absolute path). Am currently facing three different scenarios as in below:
Path name is invalid (cannot be created) - for ex, path name provided by user : sfg rgdf gfggdgfudf
Path name is invalid (but, it can be created) - for ex : C:\Parent\Child\GrandChildren (here C:\Parent exists whereas Child\GrandChildren does not.
Path name is valid (i.e. directory exists) - for ex : C:\Parent\Test
For the first case, I need to save the file in a default location.
For the second, I need to create directories and save the file.
And for the last, I'll save in the specified path.
Below is my code snippet which is saving the file in default location for both first and second scenarios. Am having difficulty to distinguish between first and second. java.io.File.mkdirs works for the second case, but not for the first.
Please ignore my poor code as am new to this programming language. Any inputs would be much appreciated.
//User input must be absolute path
String saveToFolder = "kut igeiguye jh";
String defaultFolder = "C:\\Parent\\Data";
try{
File file = new File(saveToFolder);
if(!file.exists()){
saveToFolder = defaultFolder;
}
file.mkdirs();
}catch(Exception e){
saveToFolder = defaultFolder;
}
//code to save data in path **saveToFolder**
1) For 1st case use regex to determine if path is valid or not
String regularExpression = "([a-zA-Z]:)?(\\[a-zA-Z0-9_-]+)+\\?" ; // this regex for windows. If you are running in linux then regex will be different.
Pattern pattern = Pattern.compile(regularExpression);
boolean isMatched = Pattern.matches(regularExpression,saveToFolder);
2) Check if path is valid or not again using 1st method. If its valid check if folder exists or not.
File f = new File(saveToFolder);
if (f.exists() && f.isDirectory()) {
...
}
3) If path is valid by first method and if it exists second method .
kut igeiguye jh is happily accepted by most file systems out there. If you don't want spaces in your path/filenames, you already need to check the validity by yourself e.g. using a Regular Expression.
Appreciate all those who added their inputs. I made few changes to my code which works. One of my assignments requirement was that my program must take absolute path as input.
With my below code, am able to save data in default location for case 1; New folders are being created and file is being saved in the newly created folder for case 2; And saving the file in specified path for case 3.
String saveToFolder = "kut igeiguye jh";
String defaultFolder = "C:\\Parent\\Data";
try{
File file = new File(saveToFolder);
boolean dirCreated = file.mkdirs();
if(!dirCreated || (file.getParent().equals(null))){
saveToFolder = defaultFolder;
}
}catch(Exception e){
saveToFolder = defaultFolder;
}
System.out.println("save to folder : " +saveToFolder);
you can use isFile() to valid over exists(). I have not checked for all cases but this should help you.
public static void checkPath(){
String saveToFolder = "file/User.txt";
String defaultFolder = "file/data/";
try{
File file = new File(saveToFolder);
if(!file.isFile()){
saveToFolder = defaultFolder;
System.out.println("File not found");
}
file.mkdirs();
}catch(Exception e){
saveToFolder = defaultFolder;
}
}
Java NIO make this simple. The method Files.createDirectories succeeds whether the directory already exists or didn't exist and has been created.
try {
Files.createDirectories(Paths.get(saveToFolder));
}
catch (IOException e) {
System.err.println("Using default folder due to the following exception: " + e);
saveToFolder = defaultFolder;
}
If you really want to check whether the user entered an absolute path, just use Path.isAbsolute().
I'm working on a mod to generate random plants in Unreal World. I posted the initial version of the mod on the game's forum, and I've been told about an issue with the Windows version of the mod. The problem is specifically with the name generation; it just returns null for each name. The user who reported this problem to me posted this stacktrace (it's the same error repeated several times; no need to read them all):
This is the method where the error is occurring:
private static String getName(Random rn, boolean full) {
try {
String name;
int prefix = rn.nextInt(179);
int suffix = rn.nextInt(72);
Scanner sc1 = new Scanner(new File("srcwin\\Prefixes.txt")); // error occurs on this line
for (int i=0; i<prefix; i++) {
sc1.nextLine();
}
name = sc1.next();
if (name.contains("'")) {
name += " ";
}
if (full || rn.nextInt(3) == 0) {
if (rn.nextInt(3) == 0 && !name.contains(" ")) {
name += " ";
}
Scanner sc2 = new Scanner(new File("srcwin\\Suffixes.txt"));
for (int i=0; i<suffix; i++) {
sc2.nextLine();
}
name += sc2.next();
}
return name;
} catch (FileNotFoundException ex) {
Logger.getLogger(UrWPlantMod.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
This program is contained in a JAR containing the directories modwin, srcwin, and META-INF. The class files are contained in modwin, while the source code and txt files are contained in srcwin. The user who reported this problem is running Windows 10 Home (Version 1511, Build 10586). Another user, running Windows 8.1 (no other specifics given), can run this just fine, with no FileNotFoundExceptions or null names.
If it's relevant, I am running Ubuntu 14.04, with the only difference in this code being that the file path is src/Prefixes.txt instead of srcwin\\Prefixes.txt, and it runs just fine for me.
If you'd like to see the other lines of code mentioned in the stacktrace:
berries[i] = new Plant(getName(rn, false) + "berry " + getBerryName(rn), rn.nextInt(4)+1, getImg(rn, "berry"));
and
createBerries(rn); // the above line of code is in the method called here
new Scanner(new File("srcwin\\Prefixes.txt")) will open the file srcwin\Prefixes.txt from the current directory.
Directories inside a Jar file cannot be access this way.
So, either the current directory is not what you think it is, or the files are not there (in folder srcwin on the file system).
To load content of a file inside your Jar (which is on the classpath, we presume), use getResourceAsStream().
try (Scanner sc1 = new Scanner(MyClass.class.getResourceAsStream("/srcwin/Prefixes.txt"))) {
// code here
}
Note the change to the file name. It starts with / and uses /.
Also note that you should always close the Scanner when done with it (except when used with System.in), hence the try-with-resources block.
I am trying to extract list of zip files from folder and then re-zipping them with password. The problem is while re-zipping, the iteration/loop is not stopping. Also, re-zipped files should be a separate zip file each rather than merging all contents to one zip.
Here's what I have tried:
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
public class AddFilesWithAESEncryption2 {
public AddFilesWithAESEncryption2() {
try {
//Extract Zip files as folders
try {
String ZipSourcePath = "E:/EZipTest/";
String ExtractTo = "D:/DZipTest/";
String files1;
File folder1 = new File(ZipSourcePath);
File[] listOfFiles1 = folder1.listFiles();
for (int i = 0; i < listOfFiles1.length; i++) {
if (listOfFiles1[i].isFile()) {
files1 = listOfFiles1[i].getName();
String ZipFiles = "E:/EZipTest/" + files1;
try {
ZipFile zipFile = new ZipFile(ZipFiles);
List fileHeaderList = zipFile.getFileHeaders();
zipFile.extractAll(ExtractTo);
} catch (ZipException e) {
e.printStackTrace();
}
}
}
//Get list of folders
String DirectoryNames;
String ExtractedDirectories1 = "D:/DZipTest/";
File folder2 = new File(ExtractedDirectories1);
File[] listOfFiles2 = folder2.listFiles();
for (int i = 0; i < listOfFiles2.length; i++) {
if (listOfFiles2[i].isDirectory()) {
DirectoryNames = listOfFiles2[i].getName();
String ListOfDirectories = "D:/DZipTest/" + DirectoryNames;
//Get list of files
String ExtractedDirectories = ListOfDirectories;
File folder3 = new File(ExtractedDirectories);
File[] listOfFiles3 = folder3.listFiles();
for (int j = 0; j < listOfFiles3.length; j++) {
File file = listOfFiles3[j];
if (file.isFile()) {
String FileNames = file.getName();
System.out.println(ListOfDirectories + FileNames);
//Compress and zip the files
ZipFile zipFile = new ZipFile("D:/" + listOfFiles2[i].getName() + ".zip");
ArrayList filesToAdd = new ArrayList();
filesToAdd.add(new File(ListOfDirectories + FileNames));
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); // set compression method to deflate compression
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
parameters.setEncryptFiles(true);
parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
parameters.setPassword("test");
zipFile.addFiles(filesToAdd, parameters);
}
}
}
}
} catch (ZipException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new AddFilesWithAESEncryption2();
}
}
Refactoring
Refactoring your code will help you understand what it does. It will reveal the problem and immediately identify a fix. Here's how it goes. Note that this is not a complete tutorial, but I hope you get the point.
First, extract a nice method that does the unzipping. Mark everything inside the first for loop, then right click and choose Refactor / Extract Method.... Name it unzipFile. Note that you now have a nice small, potentially reusable and potentially testable (JUnit) method.
Next, mark everything from ZipParameters parameters to parameters.setPassword("test"); Right click, Refactor / Extract Method.... Name it getEncryptionParameters. Note how 7 lines of code have been removed from the long method and readability is improved.
Right click on parameters and choose Refactor / Inline .... Note how the temporary variable disappears.
See the bug
If you have followed closely, there is a piece of code like this:
//Compress and zip the files
ZipFile zipFile = new ZipFile("D:/" + listOfFiles2[i].getName() + ".zip");
ArrayList filesToAdd = new ArrayList();
filesToAdd.add(new File(ListOfDirectories + FileNames));
zipFile.addFiles(filesToAdd, getEncryptionParameters());
See what it does? It creates a new ZIP file, adds only one file to filesToAdd and that's it. But why? It says FileNames. How can that be one file only?
Looking at
String FileNames = file.getName();
that's really just one file, so the variable name is wrong.
Right click FileNames and choose Refactor/Rename.... Enter fileName. Note how the variable name in your program matches to what it really is. It heavily improves readability of the code.
Simplify
Now that you know you're adding only one file, use addFile() instead of addFiles(). You're getting rid of the ArrayList:
//Compress and zip the files
ZipFile zipFile = new ZipFile("D:/" + listOfFiles2[i].getName() + ".zip");
File fileToAdd = new File(ListOfDirectories + fileName);
zipFile.addFile(fileToAdd, getEncryptionParameters());
Fix the bug
As spotted before, a new ZipFile(...) is created inside the loop and only one file is added to it. Move that line out of the loop pressing Alt+Up.
Continue refactoring
A part of the problem is already fixed (I haven't tried, actually), but your code is still not error-free. Let's go on:
Mark everything from File[] listOfFiles3 to the end of the for loop that follows. Right click, Refactor/Extract Method..., name it rezip. Your big method becomes smaller again.
Right click on ExtractedDirectories, Refactor / Inline .... You just got rid of a unnecessary temporary variable.
See something? Your code should look like this:
//Get list of files
File folder3 = new File(ListOfDirectories);
rezip(listOfFiles2, i, ListOfDirectories, folder3);
Note how folder3 and ListOfDirectories is essentially the same. Let's get rid of it. Move the line File folder3 = new File(ListOfDirectories); into the method, just behind private void rezip(...){ and remove the parameter File folder3 from both, the method call and the method declaration of rezip().
The loop using rezip() now looks like this:
for (int i = 0; i < listOfFiles2.length; i++) {
if (listOfFiles2[i].isDirectory()) {
DirectoryNames = listOfFiles2[i].getName();
String ListOfDirectories = "D:/DZipTest/" + DirectoryNames;
rezip(listOfFiles2, i, ListOfDirectories);
}
}
You might spot that DirectoryNames is actually just one, not many. Right click, Refactor/Rename.... Enter subDirectory.
Right click subDirectory, Refactor / Inline .... Read the error message. Right click References / Workspace. Check the results and find out that this variable is only used within the for loop. Delete the declaration outside and declare it at its first use. Now do the Refactor / Inline ... operation.
Your code looks like this:
for (int i = 0; i < listOfFiles2.length; i++) {
if (listOfFiles2[i].isDirectory()) {
String ListOfDirectories = "D:/DZipTest/" + listOfFiles2[i].getName();
rezip(listOfFiles2, i, ListOfDirectories);
}
}
Again, there's a variable name indicating a list or an Array, but that's not true. Refactor / Rename..., name it directoryToZip.
Inline the following variables in this order: ExtractedDirectories1, folder2, ZipSourcePath, folder1.
Rename in this order listOfFiles1 to zipFiles and listOfFiles2 to extractedDirectories.
Remove files1 since it is never used.
The final bug
The method is now short and readable enough to understand it completely. Does the following make sense?
String ExtractTo = "D:/DZipTest/";
File[] zipFiles = new File("E:/EZipTest/").listFiles();
for (int i = 0; i < zipFiles.length; i++) {
unzipFile(ExtractTo, zipFiles, i);
}
File[] extractedDirectories = new File("D:/DZipTest/").listFiles();
for (int i = 0; i < extractedDirectories.length; i++) {
if (extractedDirectories[i].isDirectory()) {
String directoryToZip = "D:/DZipTest/" + extractedDirectories[i].getName();
rezip(extractedDirectories, i, directoryToZip);
}
}
No it doesn't.
You don't want to extract all archives first but one by one
You don't want to zip subdirectories, you want to zip everything in the ExtractTo directory
Fixing the final bug
The signature of unzipFile() does not look right. If it unzips one file only as the name suggests, why does it get access to all files then?
Replace unzipFile(ExtractTo, zipFiles, i); by unzipFile(ExtractTo, zipFiles[i]);. This breaks the code. Eclipse will mark it red. Fix it by changing the parameters from
private void unzipFile(String ExtractTo, File[] listOfFiles1, int i)
to
private void unzipFile(String ExtractTo, File listOfFiles1)
Inside unzip, replace listOfFiles1[i] by listOfFiles1. Then Refactor/Rename... it to sourceZipFile.
Similar for the rezip method: it should get the directory to zip and the target file name only. Therefore change
rezip(extractedDirectories, i, directoryToZip);
to
rezip(extractedDirectories[i], directoryToZip);
Then adapt the method itself from
private void rezip(File[] listOfFiles2, int i, String ListOfDirectories) throws ZipException
to
private void rezip(File listOfFiles2, String ListOfDirectories) throws ZipException
then change listOfFiles2[i] to listOfFiles2. Rename it to targetFile.
Now you have a nice unzipFile() method and a rezip() method. Let's combine it in a cool way:
String ExtractTo = "D:/DZipTest/";
File[] zipFiles = new File("E:/EZipTest/").listFiles();
for (int i = 0; i < zipFiles.length; i++) {
unzipFile(ExtractTo, zipFiles[i]);
rezip(zipFiles[i], ExtractTo);
// TODO: delete extracted files here
}
Awesome, ain't it?
Notes
Maybe you've seen how much effort it is to understand your code and provide a fix. Actually, too much effort for Stack Overflow. Next time you ask a question, please try to provide code that is at minumum as readable as your code now.
The code is still not as clean as it should be. Spend some more time on it. When you think it's superb, post it on https://codereview.stackexchange.com/ to get even more instructions.
When I'm running this method I immediately get a stack overflow exception so obviously the method keeps recursively calling itself however I'm not sure why. For reference the file structure I'm testing it with is a load of folders and in those folders are files, no other folders
public void files(File[] f)
{
if(f == null){
return;
}
else
{
for(int i = 0; i < f.length; i++)
{
if(f[i].isFile() && (f[i].getName().contains(".mp3") || f[i].getName().contains(".m4a"))) //iterate through files and check if each file matches the required criteria
{
String fullname = f[i].getName();
Log.v("full name", fullname);
String name = null;
if(fullname.contains(".mp3"))
{
name = fullname.substring(0, fullname.lastIndexOf(".mp3"));
}
else if(fullname.contains(".m4a")) //Removing file extensions of music file so they can be displayed using an appropriate name
{
name = fullname.substring(0, fullname.lastIndexOf(".m4a"));
}
list.add(name);
mp3.add(f[i]);
Log.v("added", name);
}
if(f[i].isDirectory())
{
File inner[] = files[i].listFiles();
files(inner);
}
}
}
}
Maybe some of the files are "." and ".." which means , i think, the current folder and back one folder.
So in your isDirectory() part of the if check also check if f[i]!="." and f[i]!=".."
if(f[i].isDirectory() and f[i]!="." and f[i]!="..")
{
File inner[] = files[i].listFiles();
files(inner);
}
EDIT:
As #Jon said, try to add more debug to it and see where it breaks exactly.
LATER EDIT:
For future readers, the problem was here:
//File inner[] = files[i].listFiles();
File inner[] = f[i].listFiles();
So super dumb mistake on my part, when copying the code from a previous non recursive implementation I forgot to change files to f in
if(f[i].isDirectory())
{
File inner[] = files[i].listFiles();
files(inner);
}
If I do this:
File f = new File("c:\\text.txt");
if (f.exists()) {
System.out.println("File exists");
} else {
System.out.println("File not found!");
}
Then the file gets created and always returns "File exists". Is it possible to check if a file exists without creating it?
EDIT:
I forgot to mention that it's in a for loop. So here's the real thing:
for (int i = 0; i < 10; i++) {
File file = new File("c:\\text" + i + ".txt");
System.out.println("New file created: " + file.getPath());
}
When you instantiate a File, you're not creating anything on disk but just building an object on which you can call some methods, like exists().
That's fine and cheap, don't try to avoid this instantiation.
The File instance has only two fields:
private String path;
private transient int prefixLength;
And here is the constructor :
public File(String pathname) {
if (pathname == null) {
throw new NullPointerException();
}
this.path = fs.normalize(pathname);
this.prefixLength = fs.prefixLength(this.path);
}
As you can see, the File instance is just an encapsulation of the path. Creating it in order to call exists() is the correct way to proceed. Don't try to optimize it away.
Starting from Java 7 you can use java.nio.file.Files.exists:
Path p = Paths.get("C:\\Users\\first.last");
boolean exists = Files.exists(p);
boolean notExists = Files.notExists(p);
if (exists) {
System.out.println("File exists!");
} else if (notExists) {
System.out.println("File doesn't exist!");
} else {
System.out.println("File's status is unknown!");
}
In the Oracle tutorial you can find some details about this:
The methods in the Path class are syntactic, meaning that they operate on the Path instance. But eventually you must access the file system to verify that a particular Path exists, or does not exist. You can do so with the exists(Path, LinkOption...) and the notExists(Path, LinkOption...) methods. Note that !Files.exists(path) is not equivalent to Files.notExists(path). When you are testing a file's existence, three results are possible:
The file is verified to exist.
The file is verified to not exist.
The file's status is unknown. This result can occur when the program does not have access to the file.
If both exists and notExists return false, the existence of the file cannot be verified.
Creating a File instance does not create a file on the file system, so the posted code will do what you require.
The Files.exists method has noticeably poor performance in JDK 8, and can slow an application significantly when used to check files that don't actually exist.
This can be applied too for Files.noExists, Files.isDirectory and Files.isRegularFile
According this you can use the following :
Paths.get("file_path").toFile().exists()