I have around 500 text files inside a directory with each with the same prefix in their filename, for example: dailyReport_.
The latter part of the file is the date of the file. (For example dailyReport_08262011.txt, dailyReport_08232011.txt)
I want to delete these files using a Java procedure. (I could go for a shell script and add it a job in the crontab but the application is meant to used by laymen).
I can delete a single file using something like this:
try{
File f=new File("dailyReport_08232011.txt");
f.delete();
}
catch(Exception e){
System.out.println(e);
}
but can I delete the files having a certain prefix? (e.g. dailyReport08 for the 8th month) I could easily do that in shell script by using rm -rf dailyReport08*.txt .
But File f=new File("dailyReport_08*.txt"); doesnt work in Java (as expected).
Now is anything similar possible in Java without running a loop that searches the directory for files?
Can I achieve this using some special characters similar to * used in shell script?
No, you can't. Java is rather low-level language -- comparing with shell-script -- so things like this must be done more explicetly. You should search for files with required mask with folder.listFiles(FilenameFilter), and iterate through returned array deleting each entry. Like this:
final File folder = ...
final File[] files = folder.listFiles( new FilenameFilter() {
#Override
public boolean accept( final File dir,
final String name ) {
return name.matches( "dailyReport_08.*\\.txt" );
}
} );
for ( final File file : files ) {
if ( !file.delete() ) {
System.err.println( "Can't remove " + file.getAbsolutePath() );
}
}
You can use a loop
for (File f : directory.listFiles()) {
if (f.getName().startsWith("dailyReport_")) {
f.delete();
}
}
Java 8 :
final File downloadDirectory = new File("directoryPath");
final File[] files = downloadDirectory.listFiles( (dir,name) -> name.matches("dailyReport_.*?" ));
Arrays.asList(files).stream().forEach(File::delete)
With Java 8:
public static boolean deleteFilesForPathByPrefix(final String path, final String prefix) {
boolean success = true;
try (DirectoryStream<Path> newDirectoryStream = Files.newDirectoryStream(Paths.get(path), prefix + "*")) {
for (final Path newDirectoryStreamItem : newDirectoryStream) {
Files.delete(newDirectoryStreamItem);
}
} catch (final Exception e) {
success = false;
e.printStackTrace();
}
return success;
}
Simple version:
public static void deleteFilesForPathByPrefix(final Path path, final String prefix) {
try (DirectoryStream<Path> newDirectoryStream = Files.newDirectoryStream(path, prefix + "*")) {
for (final Path newDirectoryStreamItem : newDirectoryStream) {
Files.delete(newDirectoryStreamItem);
}
} catch (final Exception e) { // empty
}
}
Modify the Path/String argument as needed. You can even convert between File and Path. Path is preferred for Java >= 8.
I know I'm late to the party. However, for future reference, I wanted to contribute a java 8 stream solution that doesn't involve a loop.
It may not be pretty. I welcome suggestions to make it look better. However, it does the job:
Files.list(deleteDirectory).filter(p -> p.toString().contains("dailyReport_08")).forEach((p) -> {
try {
Files.deleteIfExists(p);
} catch (Exception e) {
e.printStackTrace();
}
});
Alternatively, you can use Files.walk which will traverse the directory depth-first. That is, if the files are buried in different directories.
Use FileFilter like so:
File dir = new File(<path to dir>);
File[] toBeDeleted = dir.listFiles(new FileFilter() {
boolean accept(File pathname) {
return (pathname.getName().startsWith("dailyReport_08") && pathname.getName().endsWith(".txt"));
}
for (File f : toBeDeleted) {
f.delete();
}
There isn't a wildcard but you can implement a FilenameFilter and check the path with a startsWith("dailyReport_"). Then calling File.listFiles(filter) gives you an array of Files that you can loop through and call delete() on.
I agree with BegemoT.
However, just one optimization:
If you need a simple FilenameFilter, there is a class in the Google packages.
So, in this case you do not even have to create your own anonymous class.
import com.google.common.io.PatternFilenameFilter;
final File folder = ...
final File[] files = folder.listFiles(new PatternFilenameFilter("dailyReport_08.*\\.txt"));
// loop through the files
for ( final File file : files ) {
if ( !file.delete() ) {
System.err.println( "Can't remove " + file.getAbsolutePath() );
}
}
Enjoy !
You can't do it without a loop. But you can enhance this loop. First of all, ask you a question: "what's the problem with searching and removing in the loop?" If it's too slow for some reason, you can just run your loop in a separate thread, so that it will not affect your user interface.
Other advice - put your daily reports in a separate folder and then you will be able to remove this folder with all content.
or in scala
new java.io.File(<<pathStr>>).listFiles.filter(_.getName.endsWith(".txt")).foreach(_.delete())
Have a look at Apache FileUtils which offers many handy file manipulations.
Related
I need to traverse files in a folder, and find all images. I need to include subdirectories, as well as made shortcuts (on windows), aliases (for mac users) or symbolic links.
I found working API that only works for the symbolic links.. I can't seem to determine if a file is an Alias or a shortcut..
Code I have:
private List<Path> getFiles(String folder) throws IOException {
List<Path> files = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(folder))) {
for (Path p : stream) {
System.out.println(p);
if(isLink(p.toFile())) {
files.addAll(getFiles(p.toFile().getCanonicalPath()));
}
String mimeType = Files.probeContentType(p);
if (StringUtils.isNotBlank(mimeType) && mimeType.contains("image")) {
files.add(p);
}
}
return files;
}
and
public static boolean isLink(File file) {
try {
if (!file.exists())
return true;
else {
String cnnpath = file.getCanonicalPath();
String abspath = file.getAbsolutePath();
return !abspath.equals(cnnpath);
}
} catch (IOException ex) {
System.err.println(ex);
return true;
}
}
Some help would be greatly appreciated!
Thanks!
To avoid toFile() calls you could also use Files.isSymbolicLink(Path p) and p.toRealPath() to determine the target of symbolic links for the paths you use.
Windows shortcuts are not modelled in Java File / Path APIs. An implementation which reads Windows shortcut binary format can be found here in another SO question - it better if using drive letters not using UNC pathnames.
I can't help for MacOS alias.
You should keep track of the directories visited in getFiles to avoid infinite loop when following links.
So I am trying to copy some files with Files.copy(), and while it is working fine in Mac, and works to copy text files on Windows, when I try to copy a binary file I just get the error:
java.nio.file.NoSuchFileException: C:\path\to\file
My function is:
private void copyFiles()
{
ArrayList<String> temp = new ArrayList<>(); //This is set up outside the function
temp.add("file1");
temp.add("file2"); //etc
String AlphaSimFileName = "folderName"; //This is actually set outside the function
String currentDir = System.getProperty("user.dir");
Path baseAlphaName = Paths.get(currentDir, AlphaSimFileName);
Path baseDirectoryName = Paths.get(currentDir, name);
System.out.println(""+baseAlphaName.toString());
System.out.println(""+baseDirectoryName.toString());
// for (String l: MyFunctions.getFilesAsString()) //gives the list of files to copy.
for (String l: temp)
{
Path p1 = Paths.get(baseAlphaName.toString(), l);
Path p2 = Paths.get(baseDirectoryName.toString(), l);
try
{
Files.copy(p1, p2, StandardCopyOption.REPLACE_EXISTING);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
Anyone have any idea why this is happening?
(Posted on behalf of the OP).
The reason for this was that Windows was automatically hiding file extensions, so the file that I was trying to move (i.e. without the extension) didn't exist.
Try doing it manually first. Use \\ instead of \ when you hardcode the path for Windows using Java. Also, the file extension may be missing.
I want to create shortcuts inwindows with code, I use the library here:
http://alumnus.caltech.edu/~jimmc/jshortcut/jshortcut/README.html
also the corresponding codes:
import net.jimmc.jshortcut.JShellLink;
public class remove {
public static void main(String[] args) throws Exception{
String path = new String ("/home/test.csv");
readAndDelete(path, true, StandardCharsets.UTF_8);
}
private static void readAndDelete(String path, boolean ignoreHeader,Charset encoding) throws IOException {
File file = new File(path);
CSVParser parser = CSVParser.parse(file, encoding,CSVFormat.DEFAULT.withHeader());
List<CSVRecord> records = parser.getRecords();
List<String> docRecord = new ArrayList<String>();
List<String> shortpath = new ArrayList<String>();
for (CSVRecord doctype : records){
docRecord.add(doctype.get(0).toString());
shortpath.add(doctype.get(1).toString());
}
int recordlength = docRecord.size();
for(String eachdocRecord:docRecord){
try {
Path pathtemp=Paths.get(eachdocRecord);
Files.delete(pathtemp);
} catch (NoSuchFileException x) {
System.err.format("%s: no such" + " file or directory%n", path);
} catch (DirectoryNotEmptyException x) {
System.err.format("%s not empty%n", path);
} catch (IOException x) {
// File permission problems are caught here.
System.err.println(x);
}
}
for(int i=0; i<recordlength; i++){
JShellLink link = new JShellLink();
String pointpath=shortpath.get(i);
String originalpath = docRecord.get(i);
String[] parts = pointpath.split("\\\\");
int partssize= parts.length;
String name=parts[partssize-1];
String[] originalparts = originalpath.split("\\\\");
int originalsize = originalparts.length;
int lastlength = originalparts[originalsize-1].length();
String foldername = originalpath.substring(0,originalpath.length()-lastlength);
link.setFolder(foldername);
link.setName(name);
link.setPath(pointpath);
link.save();
}
}
}
I run it in windows command prompt, but always exception:
Exception in thread "main" java.lang.NoClassDefFoundError:net/jimmc/jshortcut/JShellLink
I compiled the .class successfully ...
Anyone could save nme ... thanks a lot
Regardless of anything else that might be wrong (I did not read the entire code) the exception is quite clear - JShellLink is not on your classpath. How to do it best depends on your use case - README suggests editing Manifest file when you build to .jar, it should also be possible to use Maven to take care of that, and any IDE should have been able to take care of classpaths for you. As far as I can tell however your use case looks something like
javac remove.java
java remove
(By the way, class names and class file names should start with upper case, that's the standard)
In that case the simplest way to do that would be to use:
java -cp .;jshortcut.jar remove
We add current directory (due to JShortcut wanting to have it's dll on classpath, just to be sure) as well as the jar containing classes you use to the classpath. If you're on Unix system use : instead of ;.
I would like to check whether an existing file is in a specific directory or a subdirectory of that.
I have two File objects.
File dir;
File file;
Both are guaranteed to exist. Let's assume
dir = /tmp/dir
file = /tmp/dir/subdir1/subdir2/file.txt
I want this check to return true
For now i am doing the check this way:
String canonicalDir = dir.getCanonicalPath() + File.separator;
boolean subdir = file.getCanonicalPath().startsWith(canonicalDir);
This seems to work with my limited tests, but i am unsure whether this might make problems on some operating systems. I also do not like that getCanonicalPath() can throw an IOException which i have to handle.
Is there a better way? Possibly in some library?
Thanks
In addition to the asnwer from rocketboy, use getCanonicalPath() instad of getAbsolutePath() so \dir\dir2\..\file is converted to \dir\file:
boolean areRelated = file.getCanonicalPath().contains(dir.getCanonicalPath() + File.separator);
System.out.println(areRelated);
or
boolean areRelated = child.getCanonicalPath().startsWith(parent.getCanonicalPath() + File.separator);
Do not forget to catch any Exception with try {...} catch {...}.
NOTE: You can use FileSystem.getSeparator() instead of File.separator. The 'correct' way of doing this will be to get the getCanonicalPath() of the directory that you are going to check against as a String, then check if ends with a File.separator and if not then add File.separator to the end of that String, to avoid double slashes. This way you skip future odd behaviours if Java decides to return directories with a slash in the end or if your directory string comes from somewhere else than Java.io.File.
NOTE2: Thanx to #david for pointing the File.separator problem.
I would create a small utility method:
public static boolean isInSubDirectory(File dir, File file) {
if (file == null)
return false;
if (file.equals(dir))
return true;
return isInSubDirectory(dir, file.getParentFile());
}
This method looks pretty solid:
/**
* Checks, whether the child directory is a subdirectory of the base
* directory.
*
* #param base the base directory.
* #param child the suspected child directory.
* #return true, if the child is a subdirectory of the base directory.
* #throws IOException if an IOError occured during the test.
*/
public boolean isSubDirectory(File base, File child)
throws IOException {
base = base.getCanonicalFile();
child = child.getCanonicalFile();
File parentFile = child;
while (parentFile != null) {
if (base.equals(parentFile)) {
return true;
}
parentFile = parentFile.getParentFile();
}
return false;
}
Source
It is similar to the solution by dacwe but doesn't use recursion (though that shouldn't make a big difference in this case).
If you plan to works with file and filenames heavly check apache fileutils and filenameutils libraries. Are full of useful (and portale if portability is mamdatory) functions
public class Test {
public static void main(String[] args) {
File root = new File("c:\\test");
String fileName = "a.txt";
try {
boolean recursive = true;
Collection files = FileUtils.listFiles(root, null, recursive);
for (Iterator iterator = files.iterator(); iterator.hasNext();) {
File file = (File) iterator.next();
if (file.getName().equals(fileName))
System.out.println(file.getAbsolutePath());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
You can traverse File Tree starting from your specific DIR.
At Java 7, there is Files.walkFileTree method. You have only to write your own visitor
to check if current node is searched file. More doc:
http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#walkFileTree%28java.nio.file.Path,%20java.util.Set,%20int,%20java.nio.file.FileVisitor%29
You can do this, however it won't catch every use case e.g. dir = /somedir/../tmp/dir/etc..., unless that's how the file was defined also.
import java.nio.file.Path;
import java.nio.file.Paths;
public class FileTest {
public static void main(final String... args) {
final Path dir = Paths.get("/tmp/dir").toAbsolutePath();
final Path file = Paths.get("/tmp/dir/subdir1/subdir2/file.txt").toAbsolutePath();
System.out.println("Dir: " + dir);
System.out.println("File: " + file);
final boolean valid = file.startsWith(dir);
System.out.println("Valid: " + valid);
}
}
In order for the checks to work correctly, you really need to map these using toRealPath() or, in your example, getCanonicalPath(), but you then have to handle exceptions for these examples which is absolutely correct that you should do so.
Since Java 7+ you can just do this:
file.toPath().startsWith(dir.toPath());
How about comparing the paths?
boolean areRelated = file.getAbsolutePath().contains(dir.getAbsolutePath());
System.out.println(areRelated);
or
boolean areRelated = child.getAbsolutePath().startsWith(parent.getAbsolutePath())
Hi right now I have the following method I am using to read one file at a time in a the same directory as the class that has this method:
private byte[][] getDoubleByteArrayOfFile(String fileName, Region region)
throws IOException
{
BufferedImage image = ImageIO.read(getClass().getResource(fileName));
byte[][] alphaInputData =
new byte[region.getInputXAxisLength()][region.getInputYAxisLength()];
for (int x = 0; x < alphaInputData.length; x++)
{
for (int y = 0; y < alphaInputData[x].length; y++)
{
int color = image.getRGB(x, y);
alphaInputData[x][y] = (byte)(color >> 23);
}
}
return alphaInputData;
}
I was wondering how I can make it so that instead of having "fileName" as a argument I can but a directory name as a argument and then iterate through all of the files within that directory and perform the same operation on it. Thanks!
If you are using Java 7, then you need to take a look at NIO.2.
Specifically, take a look at the Listing a Directory's Contents section.
Path dir = Paths.get("/directory/path");
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
for (Path file: stream) {
getDoubleByteArrayOfFile(file.getFileName(), someRegion);
}
} catch (IOException | DirectoryIteratorException x) {
// IOException can never be thrown by the iteration.
// In this snippet, it can only be thrown by newDirectoryStream.
System.err.println(x);
}
Here is a quick example that may help:
private ArrayList<byte[][]> getDoubleByteArrayOfDirectory(String dirName,
Region region) throws IOException {
ArrayList<byte[][]> results = new ArrayList<byte[][]>();
File directory = new File(dirName);
if (!directory.isDirectory()) return null //or handle however you wish
for (File file : directory.listFiles()) {
results.add(getDoubleByteArrayOfFile(file.getName()), region);
}
return results;
}
Not exactly what you asked for since it's wrapping your old method rather than re-writing it, but I find it a bit cleaner this way, and leaves you with the option of still processing a single file. Be sure to tweak the return type and how to handle the region based on your actual requirements (hard to tell from the question).
It is rather simple, using the File#listFiles() which returns a list of files in the specified File, which must be a directory. To make sure that the File is a directory, simply use File#isDirectory(). The problem occurs where you decide how to return the byte buffer. Since the method returns a 2d buffer, it is necessary to use a 3d byte buffer array, or in this case a List seems to me like the best choice since an unknown number of files will exist in the directory in question.
private List getDoubleByteArrayOfDirectory(String directory, Region region) throws IOException {
File directoryFile = new File(directory);
if(!directoryFile.isDirectory()) {
throw new IllegalArgumentException("path must be a directory");
}
List results = new ArrayList();
for(File temp : directoryFile.listFiles()) {
if(temp.isDirectory()) {
results.addAll(getDoubleByteArrayOfDirectory(temp.getPath(), region));
}else {
results.add(getDoubleByteArrayOfFile(temp.getPath(), region));
}
}
return results;
}
You can, see the list and listFiles documentation for how to do this.
We can use recursion to process a directory with subdirectories also. Here I am deleting file one by one, you can call any other function to process it.
public static void recursiveProcess(File file) {
//to end the recursive loop
if (!file.exists())
return;
//if directory, go inside and call recursively
if (file.isDirectory()) {
for (File f : file.listFiles()) {
//call recursively
recursiveProcess(f);
}
}
//call processing function, for example here I am deleting
file.delete();
System.out.println("Deleted (Processed) file/folder: "+file.getAbsolutePath());
}