How to get a random file from a random folder - java

This is not a duplicate of "Counting the number of files in a directory using Java", since the main question here is "How to get a random file from a random folder". Counting the number of files was just an optional question for improvement.
I have a folder "testfiles" in my project directory, which contains a lot of others folders. Those folders all have names like "alice-g" (firstname and then first letter from lastname). Each of those folders contains some other folders, not always the same number. And in each of those last folders, i got a bunch of files, all named with number ("1.", "2.", "3." and so on).
What i want to do is going into this "testfiles" directory, randomly select a folder, get his name, then randomly select another folder in it and finally select a random file in this folder (and get his name).
I have found this post which say we can pick a random file from a folder this way:
File[] files = dir.listFiles();
Random rand = new Random();
File file = files[rand.nextInt(files.length)];
This sound clear and easy to me. However, since all my files have "easy numbered names", i wonder if there is a way to do this without making a list of all files. Getting the total number of files in the directory should be enough to me, since their names are just number with a "."
Second, this solve the problem of selecting a random file, but not of selecting a random directory (and get his name).
EDIT:
I also found this post which says that a "good solution" (fast) to count the number of files OR directories in a directory is:
File hugeDir = new File("/tmp/huge-dir");
int numberFiles = hugeDir.list().length;
I totally didn't know that we could create a File object with a directory. Seems weird to me. However, i guess it can solve both of my question: to select a random directory and get his name, i do:
//Get a random client name according to directories names:
private static String getRandomClient() {
File testFiles = new File("testfiles"); //directory where all my testfiles are (in other directories)
Random rand = new Random();
String[] dirNames = testFiles.list(); //making a list() seems to be faster than making a listFiles().
File randomNamedDirectory = new File(dirNames[rand.nextInt(dirNames.length)]);
String randomDirName = randomNamedDirectory.getName();
return randomDirName;
}
Then i want to get a random file from a random directory from this client Directory:
//Get a random File Path from the client Directory:
private static String getRandomFilePath(String clientDirectory) {
File clientDir = new File("testfiles\\"+clientDirectory); //client Directory.
System.out.println(clientDir);
Random rand = new Random();
String[] dirNames = clientDir.list(); //This return null!?
System.out.println(dirNames);
File randomDirectory = new File(dirNames[rand.nextInt(dirNames.length)]);
int numberFiles = randomDirectory.list().length;
String randomFile = rand.nextInt(numberFiles) + "."; //All files are named with their number and a .
String filePath = clientDir + "\\" + randomDirectory +"\\" + randomFile;
return filePath;
}
The first function works well; however, in the second one, the list of Directories Names is null. Since it's just the same code as before, i don't understand why.

So, after some research, i found out that:
1. It seems that there is not really a better solution than making a list of the files. However, using list() instead of listFiles() seems to be a bit faster (see the post cited in the EDIT part of the question)
The "partial solution" i give in my question have some stupid mistakes in it (like, using \ instead of / ). Here is a "minimal and complete" solution:
.
import java.io.File;
public class main {
public static void main(String[] args) throws Exception{
String client = getRandomClient();
String filePath = getRandomFilePath(client);
File file = new File(filePath);
}
//Get a random client name according to directories names:
private static String getRandomClient() {
File testFiles = new File("maildir"); //directory where all my testfiles are (in other directories)
Random rand = new Random();
String[] dirNames = testFiles.list();
File randomNamedDirectory = new File(dirNames[rand.nextInt(dirNames.length)]);
String randomDirName = randomNamedDirectory.getName();
return randomDirName;
}
//Get a random File Path from the client Directory:
private static String getRandomFilePath(String clientDirectory) {
File clientDir = new File("maildir/"+clientDirectory); //client Directory.
System.out.println(clientDir);
Random rand = new Random();
String[] dirNames = clientDir.list();
File randomDirectory = new File(clientDir +"/" + dirNames[rand.nextInt(dirNames.length)]);
int numberFiles = randomDirectory.list().length;
String randomFile = rand.nextInt(numberFiles) + "."; //All files are named with their number and a .
String filePath = randomDirectory + "/" + randomFile;
return filePath;
}

Related

(Processing / Java) Converting a singular file function into a function that takes an array

I am trying to create a program that uploads multiple files and stores their name and BPM tag into an ArrayList ready for comparison between the files. I have found two functions to help me but I am unable to combine them to get the function that I need.
The first function takes a singular mp3 file and outputs its data into the console (using mp3agic library):
File file = new File(dataPath("") + "/Song.mp3");
Mp3File mp3file = new Mp3File(file.getPath());
if (mp3file.hasId3v2Tag()) {
ID3v2 id3v2Tag = mp3file.getId3v2Tag();
println("Track: " + id3v2Tag.getTrack());
println("Artist: " + id3v2Tag.getArtist());
println("BPM: " + id3v2Tag.getBPM());
println("Album artist: " + id3v2Tag.getAlbumArtist());
}
The second function takes a data path and outputs the directory containing the names and info of the files in the folder
void setup() {
String path = "Desktop/mp3folder";
println("Listing all filenames in a directory: ");
String[] filenames = listFileNames(path);
printArray(filenames);
println("\nListing info about all files in a directory: ");
File[] files = listFiles(path);
for (int i = 0; i < files.length; i++) {
File f = files[i];
println("Name: " + f.getName());
println("Is directory: " + f.isDirectory())
println("-----------------------");
}
}
// This function returns all the files in a directory as an array of Strings
String[] listFileNames(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
String names[] = file.list();
return names;
} else {
// If it's not a directory
return null;
}
}
// This function returns all the files in a directory as an array of File objects
// This is useful if you want more info about the file
File[] listFiles(String dir) {
File file = new File(dir);
if (file.isDirectory()) {
File[] files = file.listFiles();
return files;
} else {
// If it's not a directory
return null;
}
}
The function I am trying to create combines the two. I need the Artist, Track and BPM from the first function to work with an array list of files from a directory.
Any guidance would be appreciated. Any advice on another way to go about it would also be appreciated.
One way to approach this is to use classes to encapsulate the data you want to track.
For example, here's a simplified class that contains information about artist, track, and bpm:
public class TrackInfo{
private String artist;
private String track;
int bpm;
}
I would also take a step back, break your problem down into smaller steps, and then take those pieces on one at a time. Can you create a function that takes a File argument and prints out the MP3 data of that File?
void printMp3Info(File file){
// print out data about file
}
Get that working perfectly before moving on. Try calling it with hard-coded File instances before you try to use it with an ArrayList of multiple File instances.
Then if you get stuck, you can post a MCVE along with a specific technical question. Good luck.

Create directory using a variable as the name

Maybe its something simple i am over looking, but i need to be able to create sub directories using a list of numbers stored in a txt file. When i use a string literal it creates the directory, but when i switch to using the variable being used for the items in the list it will not. Here is the code block.
private static void GetJarDir() throws URISyntaxException {
CodeSource codeSource = NewJFrame.class.getProtectionDomain().getCodeSource();
File jarFile = null;
jarFile = new File(codeSource.getLocation().toURI().getPath());
jarDir = jarFile.getParentFile().getPath().replace("dist", "").replace("build", "");
mainFolder = jarDir + "Invoices\\";
}
this is the method i use to get the directory for the jar file and append the path with the directory i need to create the sub-directories in, i'm sure this works.
BufferedImage dest = image.getSubimage(0, 3377, 465, 80);
String newDir = new OCR().recognizeEverything(dest);
File theDir = new File(mainFolder + newDir);
new File(mainFolder + newDir.mkdirs();
im using an optical character recognition library to grab an invoice number off of a cropped image. So newDir is the invoice number. Ive printed out the path and it is the correct path, it is just not creating the directory. If i change the variable to the actual invoice number it works, any ideas?
new File(mainFolder + "223545").mkdirs();
so sitting here playing with it ive narrowed the problem down to the string returned from the OCR. It has to be a string or it wouldnt compile...but when i try to parse the string to an int it throws an exception. and it is in fact an integer

Change files names in parent and child directories

I am a beginner in Java trying to work with Files and Directories. I wanted to create a program where I could change file names automatically while searching through all the child directories for file names that are not valid. I am actually trying to load a huge amount of files on to a server but the server settings do not allow file names containing special characters. To start with I was able to write the code where if I pass the path to a directory it renames all the files with invalid names in that directory:
public class reNaming {
public static String baseLoc = "C:/Users/Developer/Desktop/.../Data Cleanup";
public static void main(String[] args) {
//LinkedList<File> fileList = new LinkedList<File>();
File obj = new File(baseLoc);
int count = 0;
for (File file: obj.listFiles())
{
String origName = file.getName();
if (origName.contains("&") || origName.contains("#") || origName.contains("#"))
{
System.out.println("Original name: "+origName);
origName = origName.replaceAll("&", "_and_");
origName = origName.replaceAll("#", "_at_");
String newName = origName.replaceAll("#", "_");
System.out.println("New Name: "+newName);
String newLoc = baseLoc+"/"+newName;
File newFile = new File(newLoc);
System.out.println(file.renameTo(newFile));
count++;
}
}
}
}
Now I want to do the same but only this time I want all the files to be reNamed even in the child directories. Can somebody please guide me how I can achieve that?
Recursion is your friend
/**Removes 'invalid' characters (&,#,#) from pathnames in the given folder, and subfolders, and returns the number of files renamed*/
public int renameDirectory(File base){
//LinkedList<File> fileList = new LinkedList<File>();
int count=0;//count the renamed files in this directory + its sub. You wanted to do this?
//Process each file in this folder.
for (File file: base.listFiles()){
String origName = file.getName();
File resultFile=file;
if (origName.contains("&") || origName.contains("#") || origName.contains("#")){
//I would replace the if statement with origName.matches(".*[&##].*") or similar, shorter but more error prone.
System.out.println("Original name: "+origName);
origName = origName.replaceAll("&", "_and_");
origName = origName.replaceAll("#", "_at_");
String newName = origName.replaceAll("#", "_");
System.out.println("New Name: "+newName);
String newLoc = baseLoc+File.separator+newName;//having "/" hardcoded is not cross-platform.
File newFile = new File(newLoc);
System.out.println(file.renameTo(newFile));
count++;
resultFile=newFile;//not sure if you could do file=newFile, tired
}
//if this 'file' in the base folder is a directory, process the directory
if(resultFile.isDirectory()){//or similar function
count+=renameDirectory(resultFile);
}
}
return count;
}
Move the code you have to a utility method (e.g. public void renameAll(File f){}). Have a condition that checks if the file is a directory and recursively call your method with it's contents. After that do what you are currently doing.
public void renameAll(File[] files){
for(File f: files){
if(f.isDirectory){
renameAll(f.listFiles());
}
rename(f);
}
}
public void rename(File f){ }

Text input and output java

I am trying to read 2 files after i read the files i want to get their contents and manipulate the contents of the two files then update a new file which is the output. The files are in the same folder as the program but the program always throws a FileNotFoundException.
Below is my code:-
import java.io.*;
import java.util.Scanner;
public class UpdateMaster {
public static void main(String[] args)
{
String master = "Customer.dat";
String trans = "Transactns.dat";
String newMaster = "Temp.txt";
Scanner inputStreamMaster = null;
Scanner inputStreamTrans = null;
PrintWriter inputStreamNewMaster = null;
try
{
inputStreamMaster = new Scanner(new File(master));
inputStreamTrans = new Scanner(new File(trans));
inputStreamNewMaster = new PrintWriter(newMaster);
}
catch(FileNotFoundException e)
{
System.out.println("Error: you opend a file that does not exist.");
System.exit(0);
}
catch(IOException e)
{
System.out.println("Error.");
System.exit(0);
}
do
{
String transLine = inputStreamTrans.nextLine();
String masterLine = inputStreamMaster.nextLine();
String[] transLineArr = transLine.split(",");
String[] masterLineArr = masterLine.split(",");
int trAccNo = Integer.parseInt(transLineArr[0]);
int sales = Integer.parseInt(transLineArr[1]);
int masterAccNo = Integer.parseInt(masterLineArr[0]);
int balance = Integer.parseInt(masterLineArr[1]);
while(masterAccNo== trAccNo){
inputStreamNewMaster.println(trAccNo+ " , "+masterAccNo);
masterLine = inputStreamMaster.nextLine();
masterLineArr = masterLine.split(",");
masterAccNo = Integer.parseInt(masterLineArr[0]);
balance = Integer.parseInt(masterLineArr[1]);
}
balance = balance + sales;
inputStreamNewMaster.println(masterAccNo+ " , "+balance);
}while(inputStreamTrans.hasNextLine());
inputStreamMaster.close();
inputStreamTrans.close();
inputStreamNewMaster.close();
//System.out.println(" the line were written to "+ newMaster);
}
}
Like #Ankit Rustagi said in the comments, you need the full path of the files if you want to keep the current implementation.
However, there is a solution where you only need the file names: use BufferedReader / BufferedWriter. See here an example on how to use these classes (in the example it uses the full path but it works without it too).
Use absolute path
String master = "C:/Data/Customer.dat";
String trans = "C:/Data/Transactns.dat";
String newMaster = "C:/Data/Temp.txt";
The code works for me, i guess you misspelled some filename(s) or your files are in the wrong folder. I created your files on the same level as the src or the project. Also this is the folder where the files are exspected.
There's nothing wrong with using relative paths like tihis. What's happening is that your program is looking for the files in the directory where you execute the program, which doesn't have to be the folder of the program. You can confirm this by logging the absolute path of the files before you try to read them. For example:
File masterFile = new File(master);
System.out.printf("Using master file '%s'%n", masterFile.getAbsolutePath());
inputStreamMaster = new Scanner(masterFile);
In general you should not hardcode file paths but allow the user to specify them in someway, for example using command line arguments, a configuration file with a well known path, or an interactive user interface.
There is a way to locate the program's class file but it's a little tricky because Java allows classes to be loaded from compressed archives that may be located in remote systems. It's better to solve this problem in some other manner.
Try this:
String current = new java.io.File( "." ).getCanonicalPath();
System.out.println("I look for files in:"+current);
To see what directory your program expects to find its input files. If it shows the correct directory, check spelling of filenames. Otherwise, you have a clue as to what's gone wrong.

How can I write consecutive named files in Java?

I have a method for saving a File, but I don't know how to save files with consecutive names such as file001.txt, file002.txt, file003.txt, filennn.text
How can I achieve this?
You can use the following line of code to create the filenames.
String filename = String.format("file%03d.txt", fileNumber);
Then you will just use that string to create new files:
File file = new File(filename);
The following code will create files numbered 1 - 100:
for (int fileNumber = 1; fileNumber <= 100; fileNumber++) {
String filename = String.format("file%03d.txt", fileNumber);
File file = new File(filename);
}
Or, you will need to have a static variable that you increment every time you create a new file.
private static int fileNumber = 0;
public void createNewFile(){
String filename = String.format("file%03d.txt", fileNumber++);
File file = new File(filename);
}
It may be desirable for you to skip over writing to a file if it already exists.
This could be done easily by placing the following at the beginning of the for loop proposed by Justin 'jjnguy' Nelson, for example:
if(new File(fileName).exists())
{
continue;
}

Categories

Resources