comandos - ¿Cómo encontrar archivos que coincidan con una cadena comodín en Java?
clusvcadm (15)
Esto debería ser realmente simple. Si tengo una cadena como esta:
../Test?/sample*.txt
entonces, ¿cuál es una forma generalmente aceptada de obtener una lista de archivos que coinciden con este patrón? (Por ejemplo, debe coincidir con ../Test1/sample22b.txt
y ../Test4/sample-spiffy.txt
pero no con ../Test3/sample2.blah
o ../Test44/sample2.txt
)
He echado un vistazo a org.apache.commons.io.filefilter.WildcardFileFilter
y parece ser la bestia correcta, pero no estoy seguro de cómo usarla para buscar archivos en una ruta de directorio relativa.
Supongo que puedo buscar el origen de la hormiga ya que usa la sintaxis comodín, pero me falta algo bastante obvio aquí.
( Editar : el ejemplo anterior fue solo un caso de muestra. Estoy buscando la forma de analizar las rutas generales que contienen comodines en tiempo de ejecución. Me di cuenta de cómo hacerlo basándome en la sugerencia de mmyers, pero es un poco molesto. Sin mencionar que el java JRE parece analizar automáticamente comodines simples en el argumento principal (String []) de un único argumento para "salvarme" el tiempo y la molestia ... Estoy contento de no haber tenido argumentos sin archivos en el mezcla.)
Aquí hay ejemplos de listados de archivos por patrones impulsados por Java 7 nio globbing y Java 8 lambdas:
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(
Paths.get(".."), "Test?/sample*.txt")) {
dirStream.forEach(path -> System.out.println(path));
}
o
PathMatcher pathMatcher = FileSystems.getDefault()
.getPathMatcher("regex:Test.[///]sample//w+//.txt");
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(
new File("..").toPath(), pathMatcher::matches)) {
dirStream.forEach(path -> System.out.println(path));
}
Considere DirectoryScanner de Apache Ant:
DirectoryScanner scanner = new DirectoryScanner();
scanner.setIncludes(new String[]{"**/*.java"});
scanner.setBasedir("C:/Temp");
scanner.setCaseSensitive(false);
scanner.scan();
String[] files = scanner.getIncludedFiles();
Tendrá que hacer referencia a ant.jar (~ 1.3 MB para ant 1.7.1).
Debería poder usar WildcardFileFilter
. Simplemente use System.getProperty("user.dir")
para obtener el directorio de trabajo. Prueba esto:
public static void main(String[] args) {
File[] files = (new File(System.getProperty("user.dir"))).listFiles(new WildcardFileFilter(args));
//...
}
No debería necesitar reemplazar *
con [.*]
, Suponiendo que el filtro comodín usa java.regex.Pattern
. No lo he probado, pero utilizo patrones y filtros de archivos constantemente.
Desde Java 8 puede usar Files#find
method directamente desde java.nio.file
.
public static Stream<Path> find(Path start,
int maxDepth,
BiPredicate<Path, BasicFileAttributes> matcher,
FileVisitOption... options)
Ejemplo de uso
Files.find(startingPath,
Integer.MAX_VALUE,
(path, basicFileAttributes) -> path.toFile().getName().matches(".*.pom")
);
El filtro Apache está diseñado para iterar archivos en un directorio conocido. Para permitir comodines en el directorio también, debe dividir la ruta en '' /
'' o '' /
'' y hacer un filtro en cada parte por separado.
Implemente la interfaz JDK FileVisitor. Aquí hay un ejemplo de http://wilddiary.com/list-files-matching-a-naming-pattern-java/
La biblioteca de comodines hace de forma eficiente coincidencias de nombre de archivo glob y regex:
http://code.google.com/p/wildcard/
La implementación es sucinta: JAR tiene solo 12.9 kilobytes.
Método de Util:
public static boolean isFileMatchTargetFilePattern(final File f, final String targetPattern) {
String regex = targetPattern.replace(".", "//."); //escape the dot first
regex = regex.replace("?", ".?").replace("*", ".*");
return f.getName().matches(regex);
}
jUnit Test:
@Test
public void testIsFileMatchTargetFilePattern() {
String dir = "D://repository//org/my//modules//mobile//mobile-web//b1605.0.1";
String[] regexPatterns = new String[] {"_*.repositories", "*.pom", "*-b1605.0.1*","*-b1605.0.1", "mobile*"};
File fDir = new File(dir);
File[] files = fDir.listFiles();
for (String regexPattern : regexPatterns) {
System.out.println("match pattern [" + regexPattern + "]:");
for (File file : files) {
System.out.println("/t" + file.getName() + " matches:" + FileUtils.isFileMatchTargetFilePattern(file, regexPattern));
}
}
}
Salida:
match pattern [_*.repositories]:
mobile-web-b1605.0.1.pom matches:false
mobile-web-b1605.0.1.war matches:false
_remote.repositories matches:true
match pattern [*.pom]:
mobile-web-b1605.0.1.pom matches:true
mobile-web-b1605.0.1.war matches:false
_remote.repositories matches:false
match pattern [*-b1605.0.1*]:
mobile-web-b1605.0.1.pom matches:true
mobile-web-b1605.0.1.war matches:true
_remote.repositories matches:false
match pattern [*-b1605.0.1]:
mobile-web-b1605.0.1.pom matches:false
mobile-web-b1605.0.1.war matches:false
_remote.repositories matches:false
match pattern [mobile*]:
mobile-web-b1605.0.1.pom matches:true
mobile-web-b1605.0.1.war matches:true
_remote.repositories matches:false
Manera simple sin usar ninguna importación externa es usar este método
Creé archivos csv nombrados con billing_201208.csv, billing_201209.csv, billing_201210.csv y parece funcionar bien.
La salida será la siguiente si existen archivos mencionados anteriormente
found billing_201208.csv
found billing_201209.csv
found billing_201210.csv
//Use Import ->import java.io.File public static void main(String[] args) { String pathToScan = "."; String target_file ; // fileThatYouWantToFilter File folderToScan = new File(pathToScan);
File[] listOfFiles = folderToScan.listFiles();
for (int i = 0; i < listOfFiles.length; i++) {
if (listOfFiles[i].isFile()) {
target_file = listOfFiles[i].getName();
if (target_file.startsWith("billing")
&& target_file.endsWith(".csv")) {
//You can add these files to fileList by using "list.add" here
System.out.println("found" + " " + target_file);
}
}
}
}
Por qué no usar hacer algo como:
File myRelativeDir = new File("../../foo");
String fullPath = myRelativeDir.getCanonicalPath();
Sting wildCard = fullPath + File.separator + "*.txt";
// now you have a fully qualified path
Entonces no tendrás que preocuparte por las rutas relativas y puedes hacer tus comodines según sea necesario.
Pruebe FileUtils
desde Apache commons-io (métodos listFiles
y iterateFiles
):
File dir = new File(".");
FileFilter fileFilter = new WildcardFileFilter("sample*.java");
File[] files = dir.listFiles(fileFilter);
for (int i = 0; i < files.length; i++) {
System.out.println(files[i]);
}
Para resolver su problema con las carpetas de TestX
, primero iteraría a través de la lista de carpetas:
File[] dirs = new File(".").listFiles(new WildcardFileFilter("Test*.java");
for (int i=0; i<dirs.length; i++) {
File dir = dirs[i];
if (dir.isDirectory()) {
File[] files = dir.listFiles(new WildcardFileFilter("sample*.java"));
}
}
Bastante una solución de "fuerza bruta", pero debería funcionar bien. Si esto no se ajusta a sus necesidades, siempre puede usar RegexFileFilter .
Puede convertir su cadena comodín en una expresión regular y usarla con el método de matches
de String. Siguiendo tu ejemplo:
String original = "../Test?/sample*.txt";
String regex = original.replace("?", ".?").replace("*", ".*?");
Esto funciona para tus ejemplos:
Assert.assertTrue("../Test1/sample22b.txt".matches(regex));
Assert.assertTrue("../Test4/sample-spiffy.txt".matches(regex));
Y contra ejemplos:
Assert.assertTrue(!"../Test3/sample2.blah".matches(regex));
Assert.assertTrue(!"../Test44/sample2.txt".matches(regex));
Puede que no lo ayude en este momento, pero JDK 7 está destinado a tener correspondencia de nombre de archivo global y regular como parte de "Más funciones de NIO".
Tal como se publicó en otra respuesta, la biblioteca de comodines funciona para la coincidencia de nombre de archivo gloge y regex: http://code.google.com/p/wildcard/
Utilicé el siguiente código para unir los patrones globales, incluidos los sistemas de archivos de estilo absoluto y relativo en:
String filePattern = String baseDir = "./";
// If absolute path. TODO handle windows absolute path?
if (filePattern.charAt(0) == File.separatorChar) {
baseDir = File.separator;
filePattern = filePattern.substring(1);
}
Paths paths = new Paths(baseDir, filePattern);
List files = paths.getFiles();
Pasé un tiempo tratando de obtener los métodos FileUtils.listFiles en la biblioteca de Apache commons io (ver la respuesta de Vladimir) para hacer esto pero no tuve éxito (me doy cuenta ahora / creo que solo puede manejar patrones que coincidan con un directorio o archivo a la vez) .
Además, el uso de filtros regex (ver la respuesta de Fabian) para procesar patrones glob de tipo absoluto suministrados arbitrariamente por el usuario sin buscar todo el sistema de archivos requeriría un preprocesamiento del glob proporcionado para determinar el prefijo no regex / glob más grande.
Por supuesto, Java 7 puede manejar la funcionalidad solicitada muy bien, pero desafortunadamente estoy atascado con Java 6 por ahora. La biblioteca es relativamente minúscula con un tamaño de 13.5kb.
Nota para los revisores: intenté agregar lo anterior a la respuesta existente mencionando esta biblioteca pero la edición fue rechazada. No tengo suficientes representantes para agregar esto como un comentario tampoco. ¿No hay una mejor manera ...
Glob of Java7: Encontrar archivos . ( Sample )