Java에서 와일드 카드 문자열과 일치하는 파일을 찾는 방법은 무엇입니까?


157

이것은 정말 간단해야합니다. 다음과 같은 문자열이있는 경우 :

../Test?/sample*.txt

그렇다면이 패턴과 일치하는 파일 목록을 얻는 데 일반적으로 허용되는 방법은 무엇입니까? (예를 들어,이 일치해야 ../Test1/sample22b.txt하고 ../Test4/sample-spiffy.txt있지만 ../Test3/sample2.blah../Test44/sample2.txt)

살펴본 org.apache.commons.io.filefilter.WildcardFileFilter결과 올바른 짐승처럼 보이지만 상대 디렉토리 경로에서 파일을 찾는 데 어떻게 사용하는지 잘 모르겠습니다.

와일드 카드 구문을 사용하기 때문에 개미의 소스를 볼 수 있다고 생각하지만 여기에 분명한 것이 빠져 있어야합니다.

( 편집 : 위의 예제는 샘플 사례 일뿐입니다. 런타임에 와일드 카드가 포함 된 일반 경로를 구문 분석하는 방법을 찾고 있습니다 .mmyers의 제안을 기반으로하는 방법을 알아 냈지만 성가신 종류입니다. Java JRE는 단일 인수에서 main (String [] arguments)의 간단한 와일드 카드를 자동 구문 분석하여 시간과 번거 로움을 "저장"하는 것 같습니다 ... 파일에 인수가 아닌 인수가 없었기 때문에 기쁩니다. 혼합.)


2
이것이 자바가 아닌 와일드 카드를 파싱하는 쉘입니다. 당신은 그들을 벗어날 수 있지만 정확한 형식은 시스템에 따라 다릅니다.
Michael Myers

2
아닙니다. Windows는 와일드 카드 *를 구문 분석하지 않습니다. 더미 배치 파일에서 동일한 구문을 실행하고 .obj 파일로 가득 찬 디렉토리를 가리키는 Test / *. obj 인 인수 # 1을 인쇄하여 이것을 확인했습니다. "Test / *. obj"를 출력합니다. Java는 여기서 이상한 일을하는 것 같습니다.
Jason S

허, 네 말이 맞아. 거의 모든 내장 쉘 명령은 와일드 카드를 확장하지만 쉘 자체는 확장하지 않습니다. 어쨌든, 자바를 와일드 카드 파싱하는 것을 막기 위해 인수를 따옴표로 묶을 수있다 : java MyClass "Test / *. obj"
Michael Myers

3
6 년 후, 스크롤링을 싫어하고 Java> = 7 제로 뎁스 솔루션을 원하는 사람들을 위해 @Vadzim의 답변을 아래 에서 보거나 공표하거나 docs.oracle.com/javase/tutorial/essential/io를
earcam

답변:


81

Apache Ant의 DirectoryScanner를 고려하십시오.

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

ant.jar를 참조해야합니다 (ant 1.7.1의 경우 ~ 1.3MB).


1
우수한! btw, scanner.getIncludedDirectories ()는 디렉토리가 필요한 경우 동일하게 수행합니다. (getIncludedFiles가 작동하지 않습니다)
틸만 하우 쉐어를

1
github의 와일드 카드 프로젝트는 매력처럼 작동합니다 : github.com/EsotericSoftware/wildcard
Moreaki

1
댓글이 아닌 별도의 답변으로 속한 @Moreaki
Jason S

plexus-utils (241Kb) DirectoryScanner에서도 이와 동일 합니다. 어느 것이 (1.9Mb) 보다 작습니다 . ant.jar
Verhagen

작동합니다. 그러나 ls같은 파일 패턴 ( ls <pattern>DirectoryScanner를 사용할 때 몇 분을 사용하는 밀리 초)과 비교할 때 속도가 매우 느린 것 같습니다 ...
dokaspar

121

Apache commons-io ( 및 메소드) FileUtils에서 시도하십시오 .listFilesiterateFiles

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

TestX폴더 관련 문제를 해결하기 위해 먼저 폴더 목록을 반복합니다.

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

상당히 '브 루트 포스'솔루션이지만 제대로 작동합니다. 이것이 귀하의 요구에 맞지 않으면 언제든지 RegexFileFilter를 사용할 수 있습니다 .


2
자, 이제 당신은 Jason S가 질문을 올렸을 때의 정확한 위치에 도달했습니다.
Michael Myers

좀 빠지는. 사용할 수있는 RegexFileFilter도 있습니다 (그러나 개인적으로는 그렇게 할 필요가 없었습니다).
Vladimir

57

다음은 Java 7 nio globbing 및 Java 8 람다로 구동되는 패턴별로 파일을 나열하는 예입니다 .

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

또는

    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
또는Files.walk(Paths.get("..")).filter(matcher::matches).forEach(System.out::println);
amoebe

보조 람다 및 메소드 참조를 제외하고 @Qstnr_La, 예.
Vadzim

29

와일드 카드 문자열을 정규식으로 변환하고이를 String의 matches메소드 와 함께 사용할 수 있습니다. 귀하의 예를 따르십시오 :

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

이것은 당신의 예를 위해 작동합니다 :

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

그리고 반례 :

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

3
(같은 특수 정규식 문자를 포함, + 또는 $ 파일이하지 않습니다 일
djjeck

'String regex = "^"+ s.replace ( "?", ".?"). replace ( " ", ". ?") + "$"'를 사용했습니다 (어떤 이유로 주석에서 별표가 사라졌습니다. ..)
Jouni Aro

2
*를 '. *로 바꾸는 이유는 무엇입니까? ? public static boolean isFileMatchTargetFilePattern (최종 파일 f, 최종 문자열 targetPattern) {``문자열 정규 표현식 = targetPattern.replace ( ".", "\\.");` regex = regex.replace("?", ".?").replace("* ", ".*"); return f.getName().matches(regex); }
Tony

OP에서 "와일드 카드가 포함 된 일반 경로"를 요청 했으므로 더 많은 특수 문자를 인용해야합니다. 차라리 Pattern.quote를 사용하고 싶습니다.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

부록 : "?" 는 필수 문자를 나타내므로 .대신 으로 바꿔야 .?합니다.
EndlosSchleife

23

Java 8부터는 Files#find에서 직접 메소드 를 사용할 수 있습니다 java.nio.file.

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

사용법 예

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

1
스트림에 보관 된 첫 번째 일치 경로를 인쇄하도록 예제를 확장 할 수 있습니까?
jxramos

18

지금 당장 도움이되지는 않지만 JDK 7은 "More NIO Features"의 일부로 glob 및 regex 파일 이름이 일치하도록 고안되었습니다.


3
자바 7에서 : Files.newDirectoryStream (path, glob-pattern)
Pat Niemeyer

13

와일드 카드 라이브러리는 glob 및 regex 파일 이름 일치를 효율적으로 수행합니다.

http://code.google.com/p/wildcard/

구현은 간결합니다. JAR은 12.9 킬로바이트에 불과합니다.


2
유일한 단점은 Maven Central에 없다는 것입니다.
yegor256

3
OSS입니다. Maven Central에 올려 놓으십시오. :)
NateS

10

외부 가져 오기를 사용하지 않는 간단한 방법은이 방법을 사용하는 것입니다

billing_201208.csv, billing_201209.csv, billing_201210.csv라는 csv 파일을 만들었으며 정상적으로 작동하는 것 같습니다.

위에 나열된 파일이 존재하면 출력은 다음과 같습니다.

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

    // 가져 오기 사용-> 가져 오기 java.io.File
        공개 정적 무효 main (String [] args) {
        문자열 pathToScan = ".";
        문자열 target_file; // fileThatYouWantToFilter
        파일 folderToScan = 새 파일 (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

다른 답변에 게시 된 것처럼 와일드 카드 라이브러리는 glob 및 정규식 파일 이름 일치에 모두 작동합니다. http://code.google.com/p/wildcard/

* nix 스타일 파일 시스템에서 절대 및 상대를 포함한 glob 패턴을 일치시키기 위해 다음 코드를 사용했습니다.

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

나는 Apache commons io 라이브러리에서 FileUtils.listFiles 메소드를 가져 오는 데 약간의 시간을 보냈지 만 (Vladimir의 답변 참조) 성공하지 못했습니다 (지금은 한 번에 하나의 디렉토리 또는 파일과 일치하는 패턴 만 처리 할 수 ​​있다고 생각합니다) .

또한 전체 파일 시스템을 검색하지 않고 임의의 사용자 제공 절대 유형 glob 패턴을 처리하기 위해 정규 표현식 필터 (Fabian의 답변 참조)를 사용하면 가장 큰 비 정규 표현식 / glob 접두사를 결정하기 위해 제공된 glob의 사전 처리가 필요합니다.

물론 Java 7은 요청 된 기능을 훌륭하게 처리 할 수 ​​있지만 불행히도 현재 Java 6에 붙어 있습니다. 라이브러리의 크기는 13.5kb로 비교적 적습니다.

검토 자 참고 사항 :이 라이브러리를 언급하는 기존 답변에 위의 내용을 추가하려고했지만 편집이 거부되었습니다. 나는 이것을 주석으로 추가 할 담당자가 충분하지 않습니다. 더 좋은 방법은 없습니까?


다른 곳에서 프로젝트를 마이그레이션 할 계획입니까? 참조 code.google.com/p/support/wiki/ReadOnlyTransition
뤽 M

1
'내 프로젝트가 아니며 이미 마이그레이션 된 것처럼 보입니다 : github.com/EsotericSoftware/wildcard
Oliver Coleman

5

를 사용할 수 있어야합니다 WildcardFileFilter. 그냥 사용하는 System.getProperty("user.dir")작업 디렉토리를 얻을 수 있습니다. 이 시도:

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

다음을 교체 할 필요가 없습니다 *[.*]와일드 카드 필터 사용을 가정 java.regex.Pattern. 나는 이것을 테스트하지는 않았지만 패턴과 파일 필터를 지속적으로 사용합니다.



3

Apache 필터는 알려진 디렉토리의 파일을 반복하기 위해 만들어졌습니다. 디렉토리에서 와일드 카드도 허용하려면 ' \'또는 ' /' 의 경로를 분할하고 각 부분에 대해 개별적으로 필터를 수행해야합니다.


1
이것은 효과가 있었다. 약간 성가신 일이지만 특히 문제가 발생하기 쉬운 것은 아닙니다. 그러나 glob matching을위한 JDK7의 기능을 기대합니다.
Jason S

0

사용하지 않는 이유는 다음과 같습니다.

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

// now you have a fully qualified path

그러면 상대 경로에 대해 걱정할 필요가 없으며 필요에 따라 와일드 카드를 수행 할 수 있습니다.


1
상대 경로에도 와일드 카드가있을 수 있기 때문입니다.
Jason S


0

이용 방법 :

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

산출:

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

파일 시스템 경로로 텍스트 검색을 사용할 수는 없습니다. 그렇지 않으면 foo/bar.txt일치 foo?bar.txt하고 정확하지 않습니다
Jason S

Jason 경로가 포함되지 않은 file.getName ()을 사용했습니다.
Tony

다음 예제 패턴에서는 작동하지 않습니다.../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);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.