Java'da joker karakter dizesiyle eşleşen dosyalar nasıl bulunur?


157

Bu gerçekten basit olmalı. Eğer böyle bir dize varsa:

../Test?/sample*.txt

bu kalıpla eşleşen dosyaların bir listesini almanın genel kabul görmüş bir yolu nedir? (örneğin o aynı olmalıdır ../Test1/sample22b.txtve ../Test4/sample-spiffy.txtancak ../Test3/sample2.blahya ../Test44/sample2.txt)

Bir göz attım org.apache.commons.io.filefilter.WildcardFileFilterve doğru canavar gibi görünüyor ama göreli bir dizin yolunda dosyaları bulmak için nasıl kullanılacağından emin değilim.

Sanırım joker için kaynak arayabildiğim için joker karakter sözdizimi kullanıyor, ancak burada oldukça açık bir şey eksik olmalıyım.

( edit : yukarıdaki örnek sadece örnek bir durumdu. Çalışma zamanında joker karakterler içeren genel yolları ayrıştırma yolunu arıyorum. Mmyers'ın önerisine dayanarak nasıl yapılacağını anladım ama bu biraz sinir bozucu. java JRE, bana zaman ve güçlük "kurtarmak" için tek bir argüman ana (String [] argümanları) basit joker karakterleri otomatik olarak ayrıştırmak gibi görünüyor ... Sadece dosya olmayan argümanlar yoktu sevindim karıştırın.)


2
Bu, joker karakterleri ayrıştıran kabuk, Java değil. Onlardan kaçabilirsiniz, ancak tam biçim sisteminize bağlıdır.
Michael Myers

2
Hayır değil. Windows joker karakterleri ayrıştırmaz *. Bunu aynı sözdizimini kukla bir toplu iş dosyasında çalıştırarak ve Test / *. Nesnesinin .obj dosyalarıyla dolu bir dizine işaret ettiği 1 numaralı argümanı yazdırarak kontrol ettim. "Test / *. Obj" çıktısını alır. Java burada garip bir şey yapıyor gibi görünüyor.
Jason S

Ha, haklısın; neredeyse tüm yerleşik kabuk komutları joker karakterleri genişletir, ancak kabuğun kendisi genişletmez. Her neyse, Java'nın joker karakterleri ayrıştırmasını önlemek için argümanı tırnak içine alabilirsiniz: java MyClass "Test / *. Obj"
Michael Myers

3
6+ yıl sonra, kaydırmadan nefret eden ve Java> = 7 sıfır dep çözümünü isteyenler için, @Vadzim'in cevabına bakın ve oy verin veya docs.oracle.com/javase/tutorial/essential/io üzerinde ayrıntılı bir şekilde
earcam

Yanıtlar:


81

Apache Ant'ten DirectorySource'u düşünün:

DirectoryScanner scanner = new DirectoryScanner();
scanner.setIncludes(new String[]{"**/*.java"});
scanner.setBasedir("C:/Temp");
scanner.setCaseSensitive(false);
scanner.scan();
String[] files = scanner.getIncludedFiles();

Ant.jar'a (karınca 1.7.1 için ~ 1.3 MB) başvurmanız gerekir.


1
mükemmel! btw, scanner.getIncludedDirectories () dizinlere ihtiyacınız varsa aynı şeyi yapar. (getIncludedFiles çalışmaz)
Tilman


1
@Moreaki ayrı bir yanıta ait, yorum değil
Jason S

Bu tam DirectoryScannerolarak pleksus-utils (241Kb) içinde bulunur. Hangisi daha küçük ant.jar(1.9Mb).
Verhagen

Bu çalışıyor. Ama lsaynı dosya deseni ( ls <pattern>DirectoryScanner kullanırken dakika kullanarak milisaniye kullanarak) ile karşılaştırıldığında son derece yavaş görünüyor ...
dokaspar

121

Deneyin FileUtilsgelen Apache commons-io ( listFilesve iterateFilesyöntemlerle):

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]);
}

TestXKlasörlerle ilgili sorununuzu çözmek için önce klasörler listesi üzerinden yineleme yaparım:

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"));
   }
}

Oldukça 'kaba kuvvet' bir çözüm ama iyi çalışması gerekir. Bu ihtiyaçlarınızı karşılamıyorsa, her zaman RegexFileFilter kullanabilirsiniz .


2
Tamam, şimdi soruyu gönderirken Jason S'nin tam olarak nerede olduğunu öğrendiniz.
Michael Myers

tam olarak değil. Kullanılabilen RegexFileFilter da var (ancak kişisel olarak bunu yapma ihtiyacı hiç olmadı).
Vladimir

57

Java 7 nio globbing ve Java 8 lambdas tarafından desteklenen desenlere göre dosya listeleme örnekleri :

    try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(
            Paths.get(".."), "Test?/sample*.txt")) {
        dirStream.forEach(path -> System.out.println(path));
    }

veya

    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));
    }

13
VeyaFiles.walk(Paths.get("..")).filter(matcher::matches).forEach(System.out::println);
amoebe

@Qstnr_La, evet, auxilar lambdas ve yöntem referansları hariç.
Vadzim

29

Joker karakter dizenizi normal bir ifadeye dönüştürebilir ve bunu String'in matchesyöntemiyle kullanabilirsiniz. Örneğinizi takip edin:

String original = "../Test?/sample*.txt";
String regex = original.replace("?", ".?").replace("*", ".*?");

Bu örnekleriniz için geçerlidir:

Assert.assertTrue("../Test1/sample22b.txt".matches(regex));
Assert.assertTrue("../Test4/sample-spiffy.txt".matches(regex));

Ve karşı örnekler:

Assert.assertTrue(!"../Test3/sample2.blah".matches(regex));
Assert.assertTrue(!"../Test44/sample2.txt".matches(regex));

3
Bu, (, + veya $
djjeck

'String regex = "^" + s.replace ("?", ".?"). Replace (" ", ". ?") + "$"' (Yıldızlar yorumumda herhangi bir nedenle kayboldu. ..)
Jouni Aro

2
Neden * yerine '. *? ? public static boolean isFileMatchTargetFilePattern (son Dosya f, son Dize targetPattern) {`` String regex = targetPattern.replace (".", "\\."); ` regex = regex.replace("?", ".?").replace("* ", ".*"); return f.getName().matches(regex); }
Tony

OP "joker karakterler içeren genel yollar" istediğinden, daha özel karakterler vermeniz gerekir. Pattern.quote kullanmayı tercih ederim:StringBuffer regexBuffer = ...; Matcher matcher = Pattern.compile("(.*?)([*?])").matcher(original); while (matcher.find()) { matcher.appendReplacement(regexBuffer, (Pattern.quote(matcher.group(1)) + (matcher.group(2).equals("*") ? ".*?" : ".?")).replace("\\", "\\\\").replace("$", "\\$")); } matcher.appendTail(regexBuffer);
EndlosSchleife

Zeyilname: "?" zorunlu bir karakteri belirtir, .yerine bununla değiştirilmelidir .?.
EndlosSchleife

23

Java 8'den beri Files#findyöntemi doğrudan kullanabilirsiniz java.nio.file.

public static Stream<Path> find(Path start,
                                int maxDepth,
                                BiPredicate<Path, BasicFileAttributes> matcher,
                                FileVisitOption... options)

Örnek kullanım

Files.find(startingPath,
           Integer.MAX_VALUE,
           (path, basicFileAttributes) -> path.toFile().getName().matches(".*.pom")
);

1
Akışta tutulan ilk maçın yolunu yazdırmak için örneği genişletebilir misiniz?
jxramos

18

Şu anda size yardımcı olmayabilir, ancak JDK 7'nin "Diğer NIO Özellikleri" nin bir parçası olarak glob ve regex dosya adlarının eşleştirilmesi amaçlanmıştır.


3
Java 7'de: Files.newDirectoryStream (yol, glob-pattern)
Pat Niemeyer


10

Herhangi bir harici içe aktarma kullanmadan basit yöntem bu yöntemi kullanmaktır

Billing_201208.csv, billing_201209.csv, billing_201210.csv ile adlandırılan csv dosyaları oluşturdum ve iyi çalışıyor gibi görünüyor.

Yukarıda listelenen dosyalar varsa çıktı aşağıdaki gibi olacaktır

found billing_201208.csv
found billing_201209.csv
found billing_201210.csv

    // Import -> import java.io.File komutunu kullanın
        public static void main (Dize [] args) {
        Dize pathToScan = ".";
        String target_file; // fileThatYouWantToFilter
        File folderToScan = yeni Dosya (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); 
                }
           }
     }    
}


6

Başka bir yanıtta belirtildiği gibi, joker karakter kitaplığı hem glob hem de regex dosya adı eşleşmesi için çalışır: http://code.google.com/p/wildcard/

Ben * nix tarzı dosya sistemlerinde mutlak ve göreli dahil glob desenleri eşleştirmek için aşağıdaki kodu kullandım:

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();

Bunu yapmak için Apache commons io kütüphanesinde FileUtils.listFiles yöntemlerini almaya çalışırken biraz zaman geçirdim (Vladimir'in cevabına bakın), ancak başarılı olamadım (şimdi fark ediyorum / sadece bir dizinde veya dosyada bir kerede eşleşen desen işleyebileceğini düşünüyorum) .

Ayrıca, tüm dosya sistemini aramadan rastgele kullanıcı tarafından sağlanan mutlak tip glob kalıplarını işlemek için normal ifade filtrelerinin kullanılması (Fabian'ın cevabına bakın), en büyük normal olmayan / glob önekini belirlemek için verilen globun bir miktar önişlemini gerektirecektir.

Tabii ki, Java 7 istenen işlevselliği güzel bir şekilde işleyebilir, ancak ne yazık ki şimdilik Java 6 ile sıkıştım. Kütüphane, 13.5kb boyutunda nispeten küçüktür.

Hakemlere not: Yukarıda bu kütüphaneden bahseden mevcut cevaba eklemeye çalıştım ancak düzenleme reddedildi. Ben de bu bir yorum olarak eklemek için yeterli temsilcisi yok. Daha iyi bir yol yok mu ...


Projenizi başka bir yere taşımayı planlıyor musunuz? Bkz. Code.google.com/p/support/wiki/ReadOnlyTransition
Luc M

1
Bu benim projem değil ve zaten taşınmış gibi görünüyor: github.com/EsotericSoftware/wildcard
Oliver Coleman

5

Kullanabilmeniz gerekir WildcardFileFilter. Sadece System.getProperty("user.dir")çalışma dizinini almak için kullanın . Bunu dene:

public static void main(String[] args) {
File[] files = (new File(System.getProperty("user.dir"))).listFiles(new WildcardFileFilter(args));
//...
}

Sen değiştirmeniz gerekiyor olmamalıdır *ile [.*]joker filtre kullanır varsayarak java.regex.Pattern. Bunu test etmedim, ancak sürekli desen ve dosya filtreleri kullanıyorum.



3

Apache filtresi, bilinen bir dizindeki dosyaları yinelemek için oluşturulmuştur. Dizindeki joker karakterlere de izin vermek için, yolu ' \' veya ' /' üzerine bölmeniz ve her parçaya ayrı ayrı bir filtre uygulamanız gerekir.


1
Bu işe yaradı. Biraz sinir bozucu, ama özellikle sorun eğilimli değildi. Ancak, glok eşleştirmesi için JDK7'nin özelliklerini dört gözle bekliyorum.
Jason S

0

Neden kullanmıyorsunuz:

File myRelativeDir = new File("../../foo");
String fullPath = myRelativeDir.getCanonicalPath();
Sting wildCard = fullPath + File.separator + "*.txt";

// now you have a fully qualified path

O zaman göreli yollar hakkında endişelenmenize gerek kalmaz ve joker karakterinizi gerektiği gibi yapabilirsiniz.


1
Çünkü göreli yolun joker karakterleri de olabilir.
Jason S


0

Util Yöntemi:

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);

    }

Ünite Testi:

@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));
        }
    }
}

Çıktı:

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

yalnızca dosya sistemi yollarıyla metin aramayı kullanamazsınız; aksi takdirde foo/bar.txteşleşir foo?bar.txtve bu doğru değildir
Jason S

Jason Yol içermeyen file.getName () kullandım.
Tony

o zaman verdiğim örnek model için işe yaramıyor:../Test?/sample*.txt
Jason S

0
Path testPath = Paths.get("C:\");

Stream<Path> stream =
                Files.find(testPath, 1,
                        (path, basicFileAttributes) -> {
                            File file = path.toFile();
                            return file.getName().endsWith(".java");
                        });

// Print all files found
stream.forEach(System.out::println);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.