net.freeutils.util.Files Maven / Gradle / Ivy
Show all versions of jelementary Show documentation
/*
* Copyright © 2003-2024 Amichai Rothman
*
* This file is part of JElementary - the Java Elementary Utilities package.
*
* JElementary is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* JElementary is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JElementary. If not, see .
*
* For additional info see https://www.freeutils.net/source/jelementary/
*/
package net.freeutils.util;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The {@code Files} class contains static file-related utility methods.
*/
public class Files {
/**
* The {@code ExtensionFilenameFilter} is a FilenameFilter which filters
* a filename according to its file extension.
*/
public static class ExtensionFilenameFilter implements FilenameFilter {
final String extension;
/**
* Constructs a ExtensionFilenameFilter with a given file extension.
*
* If the given extension does not begin with a period character,
* one will be prepended to it.
*
* @param extension the file extension to filter by (filenames which have this extension will be accepted)
*/
public ExtensionFilenameFilter(String extension) {
if (extension.charAt(0) != '.')
extension = '.' + extension;
this.extension = extension;
}
@Override
public boolean accept(File dir, String name) {
return name.endsWith(extension);
}
}
/**
* The {@code RegexFilenameFilter} is a FilenameFilter which filters
* a filename according to a given regular expression.
*/
public static class RegexFilenameFilter implements FilenameFilter {
final Pattern pattern;
/**
* Constructs a RegexFilenameFilter with a given regex pattern.
*
* The pattern is matched using {@link Matcher#matches()}, so it must match the entire filename
* and not just a partial match.
*
* @param pattern the regex pattern to filter by (filenames which match the pattern will be accepted)
*/
public RegexFilenameFilter(String pattern) {
this.pattern = Pattern.compile(pattern);
}
@Override
public boolean accept(File dir, String name) {
return pattern.matcher(name).matches();
}
}
/**
* Private constructor to avoid external instantiation.
*/
private Files() {}
/**
* Reads the contents of a file as a String in the given encoding.
*
* @param file the file to read
* @param enc the encoding to use
* @return the file contents as a String in the given encoding
* @throws IOException if an error occurs
*/
public static String readString(File file, String enc) throws IOException {
return Streams.readString(new FileInputStream(file), enc);
}
/**
* Reads the contents of a file as a String in the given encoding.
*
* @param filename the file to read
* @param enc the encoding to use
* @return the file contents as a String in the given encoding
* @throws IOException if an error occurs
*/
public static String readString(String filename, String enc) throws IOException {
return Streams.readString(new FileInputStream(filename), enc);
}
/**
* Writes a String to a file as bytes in the given encoding.
*
* @param file the file to write to
* @param str the string to write
* @param enc the encoding to use
* @throws IOException if an error occurs
*/
public static void writeString(File file, String str, String enc) throws IOException {
Streams.transfer(new ByteArrayInputStream(str.getBytes(enc)),
new FileOutputStream(file), -1, false, true);
}
/**
* Reads the contents of a file as an array of text lines in the given encoding.
*
* @param file the file from which the lines are read
* @param enc the encoding to use
* @param lenient if true, either an LF or a CRLF can end the line;
* if false, only a CRLF can end the line
* @return the lines read from the file
* @throws IOException if an error occurs
*/
public static String[] readLines(File file, String enc, boolean lenient) throws IOException {
return Strings.splitLines(readString(file, enc), lenient);
}
/**
* Reads the contents of a file as an array of bytes.
*
* @param file the file to read
* @return the file contents as an array of bytes
* @throws IOException if an error occurs
*/
public static byte[] readBytes(File file) throws IOException {
return Streams.readBytes(new FileInputStream(file));
}
/**
* Writes bytes to a file.
* If the file already exists, it is overwritten.
*
* @param file the file to write to
* @param bytes the bytes to write
* @throws IOException if an error occurs
*/
public static void writeBytes(File file, byte[] bytes) throws IOException {
Streams.transfer(new ByteArrayInputStream(bytes), new FileOutputStream(file), -1, false, true);
}
/**
* Serializes the given object to a file.
* If the file already exists, it is overwritten.
*
* @param the object type
* @param file the file to serialize to
* @param obj the object to serialize
* @throws IOException if an error occurs
*/
public static void serialize(File file, T obj) throws IOException {
serialize(file, obj, true);
}
/**
* Serializes the given object to a file.
*
* @param the object type
* @param file the file to serialize to
* @param obj the object to serialize
* @param overwrite specifies the behavior if the given file exists:
* if true, it is overwritten; otherwise, and exception is thrown
* @throws IOException if an error occurs
*/
public static void serialize(File file, T obj, boolean overwrite)
throws IOException {
if (!overwrite && file.exists())
throw new IOException("target file exists and overwrite is not allowed");
Streams.serialize(new FileOutputStream(file), obj, true);
}
/**
* Deserializes an object from a file.
*
* @param the object type
* @param file the file to deserialize from
* @return the deserialized object
* @throws FileNotFoundException if the file is not found
* @throws IOException if an error occurs
*/
public static T deserialize(File file) throws IOException {
return Streams.deserialize(new FileInputStream(file), true);
}
/**
* Calls {@link FileDescriptor#sync() sync} on a FileDescriptor for the
* given filename, which flushes all pending file modifications to
* the physical disk.
*
* @param filename the filename to sync
* @throws IOException if an error occurs
*/
public static void sync(String filename) throws IOException {
RandomAccessFile f = new RandomAccessFile(filename, "rws");
f.getFD().sync();
f.close();
}
/**
* Returns the given path with all file separators replaced with the system
* file separator, and with an optional final separator added or removed.
*
* @param path the path
* @param separator the path separator character to use
* @param endsWithSeparator specifies whether the returned path will
* end in a single path separator or with no separator
* @return the standardized path
*/
public static String toPath(String path, char separator, boolean endsWithSeparator) {
path = path.replace('\\', separator);
path = path.replace('/', separator);
path = Strings.trimRight(path, separator); // remove excess separators
return endsWithSeparator ? path + separator : path;
}
/**
* Returns the given path with all file separators replaced with the system
* file separator, and with an optional final separator added or removed.
*
* @param path the path
* @param endsWithSeparator specifies whether the returned path will
* end in a single path separator or with no separator
* @return the standardized path
*/
public static String toPath(String path, boolean endsWithSeparator) {
return toPath(path, File.separatorChar, endsWithSeparator);
}
/**
* Splits the given file into the path, name, and extension components.
*
* @param f the file to split
* @return an array of size 3, containing the path, name and extension components,
* respectively, or empty strings for components that do not exist
*/
public static String[] splitPath(File f) {
String path;
String name;
String ext = "";
if (f.isDirectory()) {
path = f.getPath();
name = "";
} else {
path = f.getParent();
name = f.getName();
int dot = name.lastIndexOf('.');
if (dot > -1) {
ext = name.substring(dot + 1);
name = name.substring(0, dot);
}
}
return new String[] { Strings.safe(path), Strings.safe(name), Strings.safe(ext) };
}
/**
* Deletes the given file or directory recursively.
*
* @param file the file or directory to delete
* @return true if and only if the file or directory
* is successfully deleted; false otherwise
*/
public static boolean delete(File file) {
if (file.isDirectory())
for (File f : file.listFiles())
delete(f); // recursive call
return file.delete(); // will fail if some recursion failed
}
/**
* Returns all files under the given directory whose names match the given regex pattern.
*
* The pattern is matched using {@link Matcher#matches()}, so it must match the entire filename
* and not just a partial match.
*
* @param dir the directory under which the files are stored
* @param pattern the regex pattern to filter by (filenames which match the pattern will be accepted)
* @return all files under the given directory whose names match the given regex pattern,
* or an empty array if none are found, or null if the given file is not a directory
* or if an I/O error occurs
*/
public static File[] listFilesRegex(File dir, String pattern) {
return dir.listFiles(new Files.RegexFilenameFilter(pattern));
}
}