Locate a file regardless of Casing? - java

I'm working on a program. However, the program is that I'm on a Linux based operating system and it wants perfect case-match names for all of the files, and considering the artist has some named with Caps, some not, some have ".png" some are ".Png" and some are ".PNG", etc; this is becoming a very difficult task. There's a little over a thousand Sprites, or renaming them wouldn’t be a problem. This is for a 2D RPG Hobby project that I'm doing for learning, purposes that I've been working on for awhile now.
Anyhow, my question is if we can make the 'Compiler'(I think is the right way to word this) ignore the file ending character-casing? If I want to load the following items
1.jpg
2.Jpg
3.JPg
4.JPG
5.jpG
I would like to be able to do it in a single line.

You cannot make the compiler ignore case; this is a filesystem characteristic. Note that NTFS is case-insensitive but it is case-preserving nonetheless.
Using Java 7 you can use a DirectoryStream.Filter<Path> to collect the relevant paths; then rename if appropriate:
final DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>()
{
#Override
public boolean accept(final Path entry)
{
return Files.isRegularFile(entry)
&& entry.getFileName().toString().toLowerCase().endsWith(".jpg");
}
};
final List<Path> collected = new ArrayList<Path>();
try (
final DirectoryStream<Path> entries = Files.newDirectoryStream(dir, filter);
) {
for (final Path entry: entries)
collected.add(entry);
}
Path dst;
String targetName;
for (final Path src: collected) {
targetName = src.getFileName().toString().toLowerCase();
dst = src.resolveSibling(targetName);
if (!Files.isSameFile(src, dst))
Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE);
}
With Java 8 you would probably use Files.walk() and lambdas instead.

If you know the exact directory for the file, you could use File.list() to get an String[] of all files in this directory. By iterating over those and using toLowerCase() on the filenames you can find your desired file.
String filename = "1.jpg";
String targetFilename = null;
File directory = new File("/some/path");
for(String maybeTargetName : directory.list()) {
if(filename.equals(maybeTargetName.toLowerCase()) {
targetFilename = maybeTargetName;
break;
}
}
if(targetFilename != null) {
File targetFile = new File(directory, targetFilename);
}

Related

Exclude file extentions for Java based compiler?

I usually find a workaround for problems, but this time I cannot seem to find one.
I am making a compiler for a self-designed language using JavaCC. Before I simply used System.in to read files, so this way I know my compiler can use any text-based file of any extension.
This project must ONLY open files with a custom extension (.bait). From my research, there are plenty of ways in Java to get a file's extension, but they all require a full path. My compiler is supposed to run from any place in the user's disk through a terminal (CMD), so I do not think Java's options are useful.
The question: How can I filter the file extension of a given file that the compiler rejects the source if it's not .bait?
The original code I use is pretty simple:
hook analizador = new hook (System.in);
analizador.RunLexer();
'hook' being the class and RunLexer() is a method for lexical analysis. The code allows any text-based code to be analyzed. For the extention rule I thought of using *.bait regular expresion as in:
hook analizador = new hook (new FileInputStream("*.bait"));
analizador.codigo();
and
InputStream input = new FileInputStream("*.bait");
hook analizador = new hook (input);
with no luck, so far. Can anybody guide me with this? An explanation of the answer will be gladly appreciated.
EDIT: Thanks to sepp2k and MeetTitan.
System.in was not an option, so instead the filename (used as argument) can be used for all the verifications needed:
String arc = args[0];
if(arc.endsWith(".bait")){ //checks file extention
File src = new File(arc); //created just to use exists()
if(src.exists()){
FileReader fr = new FileReader(arc); //used instead of System.in
hook analizador = new hook(fr);
} else System.out.println("File not found");
} else System.out.println("Invalid filetype");
As for the way to use the program, using terminal (CMD)
java hook file.bait
This code doesn't let the user run .bait files out of the hook directory as intended, so it's safe even if there are several copies of the file in different locations.
Hope it can be of any use to someone, and thank you again, sepp2k and MeetTitan!
Why can't you do something like this?
//this method takes a String and returns a substring containing the characters between the last occurrence of '.' and the end of the String
//For example, getExtension("test/your.file.bait"); will return "bait".
public static String getExtension(String fileNameOrPath) {
return fileNameOrPath.substring(fileNameOrPath.lastIndexOf('.')+1);
}
//this method compares equality of "bait" and the returned extension from our other method
public static boolean isBait(String fileNameOrPath) {
return "bait".equals(getExtension(fileNameOrPath));
}
You can use isBait(String) on any path, relative or absolute, or a filename.
You could also simply leverage String.endsWith(String).
Like so:
public static boolean isBait(String str) {
return str.endsWith(".bait");
}
EDIT
To get a listing of all files in a folder with a specific extension, you'd use a FilenameFilter with File.listFiles()
Like so:
File dir = new File("path/to/folder");
File[] baitFiles = dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".bait");
}
});
EDIT to recurse over EVERY subfolder and only get certain files:
public static List<File> recurseGetBait(File dir) { //need method for recursion
List<File> baitFilesList = new ArrayList<>(); //make a new ArrayList that we will populate and return
File[] baitFiles = dir.listFiles(new FilenameFilter() { //get all bait files with previously shown snippet
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".bait");
}
});
for(File baitFile : baitFiles) {
baitFilesList.add(baitFile); //add every file from baitFiles to baitFilesList
}
String[] dirs = file.list(new FilenameFilter() { //get all subfolders of current folder
public boolean accept(File dir, String name) {
return new File(dir, name).isDirectory();
}
});
for(File dir : dirs) { //iterate over all subfolders
List<File> returned = recursiveGetBait(dir); //run this same method on this subfolder (which will recurse until there are no sub folders)
baitFilesList.addAll(returned); // add all of the previously returned bait files to baitFilesList so we populate and return
}
return baitFilesList; //either returns our list to the previous recurse or returns the fully built list to our original caller
}

Find file path in windows using Java

Is there a way to find a particular file path in windows. For example say I want to find the location of putty.exe in my local, is there any way I can get the full file path?
I tried the following code using apache commons-io utility , but it is taking a lot of time as there are a lot of files in the local.
File dir = new File("C:\\");
String[] extensions = new String[]{"exe"};
IOFileFilter filter = new SuffixFileFilter(extensions, IOCase.INSENSITIVE);
List<File> fileList = (List<File>) FileUtils.listFiles(dir, filter, DirectoryFileFilter.DIRECTORY);
System.out.println("file list size "+fileList.size());
for (File file : fileList) {
if (file.getName().toLowerCase().contains("putty")) {
System.out.println(file.getPath());
}
}
Is there another faster way?
Edit I want to find putty.exe in particular.
No program is ever expected to scan the entire disk looking for a file it needs.
Programs use one of the following techniques:
Look at the directories in the PATH environment variable, when looking for an executable
Require the user to provide the locations of the files at installation time and store them somewhere known. This could be a .ini file stored in the program's home directory; on Windows: the system registry or a User or System environment variable (which ends up in the registry as well).
The installer creates a launcher shell script that sets an application-specific environment variable which is read by the program.
There are probably several others I haven't thought of. The idea is to limit the places the program has to search.
When they installed the executable in a regular fashion, it is on the PATH variable.
public static Optional<Path> exePath(String exeName) {
String pathVar = System.getenv("PATH");
Pattern varPattern = Pattern.compile("%(\\w+)%");
boolean tryVars = true;
while (tryVars) {
tryVars = false;
Matcher m = varPattern.matcher(pathVar);
StringBuffer sb = new StringBuffer();
while (m.find()) {
tryVars = true;
m.appendReplacement(sb, System.getenv(m.group(1)));
}
m.appendTail(sb);
pathVar = sb.toString();
}
String[] dirs = pathVar.split("\\s*;\\s*");
for (String dir : dirs) {
Path path = Paths.get(dir, exeName);
if (Files.exists(path)) {
return Optional.of(path);
}
}
return Optional.empty();
}
System.out.println(exePath("java.exe"));
Probably the var pattern substitution is done automatically (%JAVA_HOME% and such)

Odd Bug , using File.RenameTo()

I have a program in which i must rename a set of folders. they are all in "ID [Name]" format, and I want to rename them to "Name [ID]". (Its more of a training for me, for learning java))
the problem is, if the number of folders it must rename go beyond 20-24 . the program won't work, and will give the files faulty names. (the renaming process succeeds, but names are wrong)
but if they are below 20 folders, it works perfectly.(tested with the same folders)
here's the whole code:
public class DirRename {
private String parentDir;
private DirectoryStream<Path> fileList;
public DirRename(final Path dir)
{
parentDir = dir.toString();
if(!Files.exists(dir) || !Files.isDirectory(dir) || !Files.isReadable(dir))
System.out.println("Directory Read Error!!!");
//filter to return only directories in parent folder
DirectoryStream.Filter<Path> dirOnlyFilter =
new DirectoryStream.Filter<Path>() {
public boolean accept(Path file) throws IOException {
return (Files.isDirectory(file));
}
};
try
{
fileList = Files.newDirectoryStream(dir,dirOnlyFilter);
}
catch(IOException | DirectoryIteratorException x)
{
System.err.println(x);
}
}
public void rename()
{
for(Path filepath : fileList)
{
String name = filepath.getFileName().toString();
File inFile = filepath.toFile();
if(!inFile.exists() || !inFile.isDirectory() || !inFile.canWrite())
{
System.out.println("Directory is not writeable");
return;
}
Pattern regex = Pattern.compile("((?:[\\w\\d]*(?:\\s|-){0,2}[\\w\\d]+)*)\\s*-*\\s*(?:\\[|\\Q(\\E)(.+)(?:\\]|\\Q)\\E)$");
Matcher match = regex.matcher(name);
while(match.find())
{
String gameID = match.group(1);
String gameName = match.group(2);
String rename = parentDir+File.separator+gameName+" ["+gameID+"]";
File toFile = new File(rename);
if(!Paths.get(rename).isAbsolute())
{
System.out.println("Cannot rename "+name+"to "+rename);
return;
}
if(inFile.renameTo(toFile))
System.out.println("Success!");
else
System.out.println("Renaming Failed!!! for "+rename);
}
}
}
}
I tried checking the names with "system.out.println(toFile.getName())" while deleting the line "inFile.renameTo(toFile)". all names were correct.
but when i added that line back, the same names were printed incorrectly.(although some that were printed correctly were renamed incorrectly)
I'm completely confused. and I'm new to java, and generally less than a noob programmer. can someone please tell me what's going on?
Many thanks
EDIT: I found the problem.the loop:
for(Path filepath : fileList){}
runs 116 times while i only have 64 folders. I can't find any explanation as to why this happens, I use the same loop to print folder names only in the following function and it runs exactly 64 times.( exactly the number of folders I have)
public void printFolders()
{
for(Path filepath : fileList)
System.out.println(filepath.getFileName());
}
okay I finally Fixed my own problem. here's my guess on why this happened (I don't know the inner working of DirectoryStream so its just a guess).
when the folders were more than a few, the stream would read the previously renamed folders and add them as new folders, thus they were getting renamed twice. either changing the name back to original, or deforming it (the renaming wasn't designed to be 100% re-applicable).
In case of a few folders, the loop would be over before the stream had the chance to refresh, thus no problems.
so here's how i fixed it. by adding the following method, and iterating through an array of paths instead of the stream.
private Path[] getVerifiedPaths()
{
ArrayList<Path> verifiedFilePaths= new ArrayList<>();
for(Path filepath : fileList)
verifiedFilePaths.add(filepath);
return verifiedFilePaths.toArray(new Path[0]);
}
Path[] filePaths = getVerifiedPaths();
for(Path filePath : filePaths) { ...rename...}
instead of:
for(Path filepath : fileList){...rename...}
thanks to "JB Nizet" for his suggestion (comment above).

java.nio.file.Path for a classpath resource

Is there an API to get a classpath resource (e.g. what I'd get from Class.getResource(String)) as a java.nio.file.Path? Ideally, I'd like to use the fancy new Path APIs with classpath resources.
This one works for me:
return Path.of(ClassLoader.getSystemResource(resourceName).toURI());
Guessing that what you want to do, is call Files.lines(...) on a resource that comes from the classpath - possibly from within a jar.
Since Oracle convoluted the notion of when a Path is a Path by not making getResource return a usable path if it resides in a jar file, what you need to do is something like this:
Stream<String> stream = new BufferedReader(new InputStreamReader(ClassLoader.getSystemResourceAsStream("/filename.txt"))).lines();
The most general solution is as follows:
interface IOConsumer<T> {
void accept(T t) throws IOException;
}
public static void processRessource(URI uri, IOConsumer<Path> action) throws IOException{
try {
Path p=Paths.get(uri);
action.accept(p);
}
catch(FileSystemNotFoundException ex) {
try(FileSystem fs = FileSystems.newFileSystem(
uri, Collections.<String,Object>emptyMap())) {
Path p = fs.provider().getPath(uri);
action.accept(p);
}
}
}
The main obstacle is to deal with the two possibilities, either, having an existing filesystem that we should use, but not close (like with file URIs or the Java 9’s module storage), or having to open and thus safely close the filesystem ourselves (like zip/jar files).
Therefore, the solution above encapsulates the actual action in an interface, handles both cases, safely closing afterwards in the second case, and works from Java 7 to Java 18. It probes whether there is already an open filesystem before opening a new one, so it also works in the case that another component of your application has already opened a filesystem for the same zip/jar file.
It can be used in all Java versions named above, e.g. to list the contents of a package (java.lang in the example) as Paths, like this:
processRessource(Object.class.getResource("Object.class").toURI(),new IOConsumer<Path>(){
public void accept(Path path) throws IOException {
try(DirectoryStream<Path> ds = Files.newDirectoryStream(path.getParent())) {
for(Path p: ds)
System.out.println(p);
}
}
});
With Java 8 or newer, you can use lambda expressions or method references to represent the actual action, e.g.
processRessource(Object.class.getResource("Object.class").toURI(), path -> {
try(Stream<Path> stream = Files.list(path.getParent())) {
stream.forEach(System.out::println);
}
});
to do the same.
The final release of Java 9’s module system has broken the above code example. The Java versions from 9 to 12 inconsistently return the path /java.base/java/lang/Object.class for Paths.get(Object.class.getResource("Object.class")) whereas it should be /modules/java.base/java/lang/Object.class. This can be fixed by prepending the missing /modules/ when the parent path is reported as non-existent:
processRessource(Object.class.getResource("Object.class").toURI(), path -> {
Path p = path.getParent();
if(!Files.exists(p))
p = p.resolve("/modules").resolve(p.getRoot().relativize(p));
try(Stream<Path> stream = Files.list(p)) {
stream.forEach(System.out::println);
}
});
Then, it will again work with all versions and storage methods. Starting with JDK 13, this work-around is not necessary anymore.
It turns out you can do this, with the help of the built-in Zip File System provider. However, passing a resource URI directly to Paths.get won't work; instead, one must first create a zip filesystem for the jar URI without the entry name, then refer to the entry in that filesystem:
static Path resourceToPath(URL resource)
throws IOException,
URISyntaxException {
Objects.requireNonNull(resource, "Resource URL cannot be null");
URI uri = resource.toURI();
String scheme = uri.getScheme();
if (scheme.equals("file")) {
return Paths.get(uri);
}
if (!scheme.equals("jar")) {
throw new IllegalArgumentException("Cannot convert to Path: " + uri);
}
String s = uri.toString();
int separator = s.indexOf("!/");
String entryName = s.substring(separator + 2);
URI fileURI = URI.create(s.substring(0, separator));
FileSystem fs = FileSystems.newFileSystem(fileURI,
Collections.<String, Object>emptyMap());
return fs.getPath(entryName);
}
Update:
It’s been rightly pointed out that the above code contains a resource leak, since the code opens a new FileSystem object but never closes it. The best approach is to pass a Consumer-like worker object, much like how Holger’s answer does it. Open the ZipFS FileSystem just long enough for the worker to do whatever it needs to do with the Path (as long as the worker doesn’t try to store the Path object for later use), then close the FileSystem.
I wrote a small helper method to read Paths from your class resources. It is quite handy to use as it only needs a reference of the class you have stored your resources as well as the name of the resource itself.
public static Path getResourcePath(Class<?> resourceClass, String resourceName) throws URISyntaxException {
URL url = resourceClass.getResource(resourceName);
return Paths.get(url.toURI());
}
Read a File from resources folder using NIO, in java8
public static String read(String fileName) {
Path path;
StringBuilder data = new StringBuilder();
Stream<String> lines = null;
try {
path = Paths.get(Thread.currentThread().getContextClassLoader().getResource(fileName).toURI());
lines = Files.lines(path);
} catch (URISyntaxException | IOException e) {
logger.error("Error in reading propertied file " + e);
throw new RuntimeException(e);
}
lines.forEach(line -> data.append(line));
lines.close();
return data.toString();
}
You can not create URI from resources inside of the jar file. You can simply write it to the temp file and then use it (java8):
Path path = File.createTempFile("some", "address").toPath();
Files.copy(ClassLoader.getSystemResourceAsStream("/path/to/resource"), path, StandardCopyOption.REPLACE_EXISTING);
You need to define the Filesystem to read resource from jar file as mentioned in https://docs.oracle.com/javase/8/docs/technotes/guides/io/fsp/zipfilesystemprovider.html. I success to read resource from jar file with below codes:
Map<String, Object> env = new HashMap<>();
try (FileSystem fs = FileSystems.newFileSystem(uri, env)) {
Path path = fs.getPath("/path/myResource");
try (Stream<String> lines = Files.lines(path)) {
....
}
}

Java equivalent of Perl's File::Spec->catfile? [duplicate]

Is there a Java equivalent for System.IO.Path.Combine() in C#/.NET? Or any code to accomplish this?
This static method combines one or more strings into a path.
Rather than keeping everything string-based, you should use a class which is designed to represent a file system path.
If you're using Java 7 or Java 8, you should strongly consider using java.nio.file.Path; Path.resolve can be used to combine one path with another, or with a string. The Paths helper class is useful too. For example:
Path path = Paths.get("foo", "bar", "baz.txt");
If you need to cater for pre-Java-7 environments, you can use java.io.File, like this:
File baseDirectory = new File("foo");
File subDirectory = new File(baseDirectory, "bar");
File fileInDirectory = new File(subDirectory, "baz.txt");
If you want it back as a string later, you can call getPath(). Indeed, if you really wanted to mimic Path.Combine, you could just write something like:
public static String combine(String path1, String path2)
{
File file1 = new File(path1);
File file2 = new File(file1, path2);
return file2.getPath();
}
In Java 7, you should use resolve:
Path newPath = path.resolve(childPath);
While the NIO2 Path class may seem a bit redundant to File with an unnecessarily different API, it is in fact subtly more elegant and robust.
Note that Paths.get() (as suggested by someone else) doesn't have an overload taking a Path, and doing Paths.get(path.toString(), childPath) is NOT the same thing as resolve(). From the Paths.get() docs:
Note that while this method is very convenient, using it will imply an assumed reference to the default FileSystem and limit the utility of the calling code. Hence it should not be used in library code intended for flexible reuse. A more flexible alternative is to use an existing Path instance as an anchor, such as:
Path dir = ...
Path path = dir.resolve("file");
The sister function to resolve is the excellent relativize:
Path childPath = path.relativize(newPath);
The main answer is to use File objects. However Commons IO does have a class FilenameUtils that can do this kind of thing, such as the concat() method.
platform independent approach (uses File.separator, ie will works depends on operation system where code is running:
java.nio.file.Paths.get(".", "path", "to", "file.txt")
// relative unix path: ./path/to/file.txt
// relative windows path: .\path\to\filee.txt
java.nio.file.Paths.get("/", "path", "to", "file.txt")
// absolute unix path: /path/to/filee.txt
// windows network drive path: \\path\to\file.txt
java.nio.file.Paths.get("C:", "path", "to", "file.txt")
// absolute windows path: C:\path\to\file.txt
I know its a long time since Jon's original answer, but I had a similar requirement to the OP.
By way of extending Jon's solution I came up with the following, which will take one or more path segments takes as many path segments that you can throw at it.
Usage
Path.combine("/Users/beardtwizzle/");
Path.combine("/", "Users", "beardtwizzle");
Path.combine(new String[] { "/", "Users", "beardtwizzle", "arrayUsage" });
Code here for others with a similar problem
public class Path {
public static String combine(String... paths)
{
File file = new File(paths[0]);
for (int i = 1; i < paths.length ; i++) {
file = new File(file, paths[i]);
}
return file.getPath();
}
}
To enhance JodaStephen's answer, Apache Commons IO has FilenameUtils which does this. Example (on Linux):
assert org.apache.commons.io.FilenameUtils.concat("/home/bob", "work\\stuff.log") == "/home/bob/work/stuff.log"
It's platform independent and will produce whatever separators your system needs.
Late to the party perhaps, but I wanted to share my take on this. I prefer not to pull in entire libraries for something like this. Instead, I'm using a Builder pattern and allow conveniently chained append(more) calls. It even allows mixing File and String, and can easily be extended to support Path as well. Furthermore, it automatically handles the different path separators correctly on both Linux, Macintosh, etc.
public class Files {
public static class PathBuilder {
private File file;
private PathBuilder ( File root ) {
file = root;
}
private PathBuilder ( String root ) {
file = new File(root);
}
public PathBuilder append ( File more ) {
file = new File(file, more.getPath()) );
return this;
}
public PathBuilder append ( String more ) {
file = new File(file, more);
return this;
}
public File buildFile () {
return file;
}
}
public static PathBuilder buildPath ( File root ) {
return new PathBuilder(root);
}
public static PathBuilder buildPath ( String root ) {
return new PathBuilder(root);
}
}
Example of usage:
File root = File.listRoots()[0];
String hello = "hello";
String world = "world";
String filename = "warez.lha";
File file = Files.buildPath(root).append(hello).append(world)
.append(filename).buildFile();
String absolute = file.getAbsolutePath();
The resulting absolute will contain something like:
/hello/world/warez.lha
or maybe even:
A:\hello\world\warez.lha
If you do not need more than strings, you can use com.google.common.io.Files
Files.simplifyPath("some/prefix/with//extra///slashes" + "file//name")
to get
"some/prefix/with/extra/slashes/file/name"
Here's a solution which handles multiple path parts and edge conditions:
public static String combinePaths(String ... paths)
{
if ( paths.length == 0)
{
return "";
}
File combined = new File(paths[0]);
int i = 1;
while ( i < paths.length)
{
combined = new File(combined, paths[i]);
++i;
}
return combined.getPath();
}
This also works in Java 8 :
Path file = Paths.get("Some path");
file = Paths.get(file + "Some other path");
This solution offers an interface for joining path fragments from a String[] array. It uses java.io.File.File(String parent, String child):
public static joinPaths(String[] fragments) {
String emptyPath = "";
return buildPath(emptyPath, fragments);
}
private static buildPath(String path, String[] fragments) {
if (path == null || path.isEmpty()) {
path = "";
}
if (fragments == null || fragments.length == 0) {
return "";
}
int pathCurrentSize = path.split("/").length;
int fragmentsLen = fragments.length;
if (pathCurrentSize <= fragmentsLen) {
String newPath = new File(path, fragments[pathCurrentSize - 1]).toString();
path = buildPath(newPath, fragments);
}
return path;
}
Then you can just do:
String[] fragments = {"dir", "anotherDir/", "/filename.txt"};
String path = joinPaths(fragments);
Returns:
"/dir/anotherDir/filename.txt"
Assuming all given paths are absolute paths. you can follow below snippets to merge these paths.
String baseURL = "\\\\host\\testdir\\";
String absoluteFilePath = "\\\\host\\testdir\\Test.txt";;
String mergedPath = Paths.get(baseURL, absoluteFilePath.replaceAll(Matcher.quoteReplacement(baseURL), "")).toString();
output path is \\host\testdir\Test.txt.

Categories

Resources