views:

1131

answers:

2

Hello,

I have problems with moving files in directories in Java. The problem is that I cannot understand why the program behaves the way it behaves. Below is a (slight) modification of my real program.

I traverse directories of a directory. In each of these traversed directories there are text files, which I want to move into two subdirectories of the traversed directory. I create these two directories (trainingData and testData). I want 30 of the files to be moved into the testData directory and 60 of them in the trainingData directory. For this purpose I make two for loops.

In the code below, I have put first the loop moving the files into trainingData. And the good news is that all these 60 files really are moved into trainingData. However, the second loop seems not to make anything - no file of any of these 30 remaining files is moved. These 30 files continue to stay in the original (traversed) directory.

Moreover, something very strange is that when I exchange the two loops - put the one moving the 30 files on the first place, and the other one after it, then the 30 files are correctly moved into testData, however, 30 of the other 60 files are moved into the trainingData directory and the rest 30 files stay in the original (traversed) directory.

The program still does not do what I want (only partly), however, what bothers me is that I don't understand why is this difference(??) when I exchange the places of the two loops. The code is identical, and should work the same, shouldn't it?

Thanks for the time of looking into the code, and if necessary I am willing to provide more code and explanations.

File[] reviews = null;
for(File sortedRevDir : sortedRevDirs) {
     reviews = sortedRevDir.listFiles();
     int numFiles = 90;
     int numTwoThirds = 60;
     int numOneThirds = numFiles - numTwoThirds;     

     String trainingDir = sortedRevDir.getAbsolutePath() + "/trainingData";
     File trDir = new File(trainingDir);
     trDir.mkdir();
     String testDir = sortedRevDir.getAbsolutePath() + "/testData";
     File tsDir = new File(testDir);
     tsDir.mkdir();

     for(int i = 0; i < numTwoThirds; i++) {
         File review = reviews[i];
         if(!review.isDirectory()) {
              File reviewCopied = new File(trDir + "/" + review.getName());
              review.renameTo(reviewCopied);
         } 
     }
     for(int j = 0; j < numOneThird; j++) {
         File review = reviews[j];
         if(!review.isDirectory()) {
       File reviewCopied = new File(tsDir + "/" + review.getName());
       review.renameTo(reviewCopied);
         }
     }
 }
+1  A: 

Do the second loop as follows:

for(int j = numTwoThirds; j < numTwoThirds + numOneThird; j++) {

The problem is that, in both loops, you index the same array of Files. When you physically move the file, it doesn't get removed from the array. It simply stays there. In the second loop, it tries to move files that were already moved. So that's why, in the second loop, your index variable has to continue from the final value in the first loop.

It also explains why, when you exchange both loops, only 30 files are copied from the original directory: the first 30 are ignored because they were already copied; the remaining 30 are copied as intended.

Alternatively, you could do another reviews = sortedRevDir.listFiles(); between the two loops to keep the loops simpler, but that's a bit wasteful performance-wise, since it's another IO operation that's not really necessary.

jqno
Hey jqno, thanks a lot! I have changed the for loop as you suggested and it works perfectly! Thank you also for the explanation, it is clear to me now what was happening before.
Actually, why didn't I get an error message, when the program tried to remove a file (in the second loop), which was already removed? You said, it ignored such removed files (it did). In the second loop, the variable review points to reviews[i], let's say reviews[2]. Since the array hasn't changed, this would mean that we create now a second reference to the same object, right? (the first reference is from the first loop). I don't understand why removeTo() doesn't complain when it cannot remove the file.
File.renameTo doesn't throw exceptions, it returns a boolean. My guess is that these calls were returning false, but you weren't noticing that as your code isn't checking for errors. No, I don't know why java.io methods tend to return booleans rather than use exceptions, but this problem has bitten me before as well.
jsight
+2  A: 

Keep in mind that File.renameTo(dest) may (likely will) fail if your destination directory is not on the same filesystem.

In that case you'll need to implement a copy and delete semantic;

jottos
Thanks. Why will it fail?
Because the underlying rename process that is used is not guaranteed to work across filesystems. From the javadocs: "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 return value should always be checked to make sure that the rename operation was successful. "
jsight