ฉันจะแสดงรายการไฟล์ทั้งหมดซ้ำในไดเรกทอรีใน Java ซ้ำได้อย่างไร เฟรมเวิร์กมียูทิลิตีใด ๆ หรือไม่?
ฉันเห็นการใช้งานแฮ็คจำนวนมาก แต่ไม่มีจากกรอบหรือnio
ฉันจะแสดงรายการไฟล์ทั้งหมดซ้ำในไดเรกทอรีใน Java ซ้ำได้อย่างไร เฟรมเวิร์กมียูทิลิตีใด ๆ หรือไม่?
ฉันเห็นการใช้งานแฮ็คจำนวนมาก แต่ไม่มีจากกรอบหรือnio
คำตอบ:
Java 8 จัดเตรียมสตรีมที่ดีเพื่อประมวลผลไฟล์ทั้งหมดในแผนผัง
Files.walk(Paths.get(path))
.filter(Files::isRegularFile)
.forEach(System.out::println);
นี่เป็นวิธีที่เป็นธรรมชาติในการสำรวจไฟล์ เนื่องจากเป็นสตรีมคุณสามารถดำเนินการสตรีมที่ดีกับผลลัพธ์เช่นขีด จำกัด การจัดกลุ่มการทำแผนที่ออกจากต้นเป็นต้น
UPDATE : ฉันอาจชี้ให้เห็นว่ายังมีFiles.findซึ่งใช้BiPredicateซึ่งอาจมีประสิทธิภาพมากกว่าหากคุณต้องการตรวจสอบคุณสมบัติของไฟล์
Files.find(Paths.get(path),
Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile())
.forEach(System.out::println);
โปรดทราบว่าในขณะที่ JavaDoc หลบเลี่ยงว่าวิธีนี้อาจมีประสิทธิภาพมากกว่าFiles.walkแต่ก็เหมือนกันอย่างมีประสิทธิภาพความแตกต่างของประสิทธิภาพการทำงานสามารถสังเกตได้หากคุณกำลังเรียกค้นแอตทริบิวต์ไฟล์ภายในตัวกรองของคุณ ในที่สุดหากคุณต้องการกรองข้อมูลที่ใช้Files.findมิฉะนั้นใช้Files.walkส่วนใหญ่เป็นเพราะมีมากเกินไปและสะดวกกว่า
การทดสอบ : ตามที่ร้องขอฉันได้ให้การเปรียบเทียบประสิทธิภาพของคำตอบมากมาย ตรวจสอบโครงการ Github ซึ่งมีผลและกรณีทดสอบ
Files.walk
กับกระแสข้อมูลแบบขนานที่ดีที่สุดตามมาด้วยFiles.walkFileTree
ซึ่งจะช้าลงเพียงเล็กน้อยเท่านั้น คำตอบที่ได้รับการยอมรับโดยใช้คอมมอนส์ - io คือการทดสอบของฉันช้าที่สุดถึง 4 เท่า
Exception in thread "main" java.io.UncheckedIOException: java.nio.file.AccessDeniedException
แต่ฉันได้รับการยกเว้น ฉันจะแก้ไขให้ถูกต้องได้อย่างไร
FileUtilsมีiterateFiles
และlistFiles
วิธีการ ลองพวกเขา (จากคอมมอนส์ - io )
แก้ไข: คุณสามารถตรวจสอบที่นี่สำหรับเกณฑ์มาตรฐานของวิธีการที่แตกต่างกัน ดูเหมือนว่าวิธีการคอมมอนส์ - io ช้าดังนั้นให้เลือกวิธีที่เร็วกว่าจากที่นี่ (ถ้ามันสำคัญ)
FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE)
ที่ใดdir
คือวัตถุไฟล์ที่ชี้ไปยังไดเรกทอรีฐาน
listFilesAndDirs()
เนื่องจากlistFiles()
จะไม่ส่งคืนโฟลเดอร์ว่าง
FileUtils.listFiles(dir, true, true)
เช่นนั้น ใช้FileUtils.listFiles(dir, null, true)
จะโยนข้อยกเว้นในขณะที่FileUtils.listFiles(dir, true, null)
จะแสดงรายการไฟล์ทั้งหมดโดยไม่ต้องดูในไดเรกทอรีย่อย
// พร้อมที่จะวิ่ง
import java.io.File;
public class Filewalker {
public void walk( String path ) {
File root = new File( path );
File[] list = root.listFiles();
if (list == null) return;
for ( File f : list ) {
if ( f.isDirectory() ) {
walk( f.getAbsolutePath() );
System.out.println( "Dir:" + f.getAbsoluteFile() );
}
else {
System.out.println( "File:" + f.getAbsoluteFile() );
}
}
}
public static void main(String[] args) {
Filewalker fw = new Filewalker();
fw.walk("c:\\" );
}
}
-> .
ที่ชี้ไปยัง
"/"
, "./"
หรือ"../"
ไดเรกทอรีรากไดเรกทอรีการทำงานปัจจุบันและไดเรกทอรีแม่ตามลำดับ
Java 7 จะต้องมีFiles.walkFileTree :
หากคุณระบุจุดเริ่มต้นและผู้เยี่ยมชมไฟล์มันจะเรียกใช้วิธีการต่าง ๆ เกี่ยวกับผู้เยี่ยมชมไฟล์ขณะที่ดำเนินการผ่านไฟล์ในแผนผังไฟล์ เราคาดว่าผู้คนจะใช้สิ่งนี้หากพวกเขากำลังพัฒนาสำเนาแบบเรียกซ้ำการย้ายแบบเรียกซ้ำการลบแบบเรียกซ้ำหรือการดำเนินการแบบเรียกซ้ำที่กำหนดสิทธิ์หรือดำเนินการอื่นในแต่ละไฟล์
ขณะนี้มีบทช่วยสอนของ Oracleทั้งหมดเกี่ยวกับคำถามนี้
ไม่จำเป็นต้องใช้ไลบรารีภายนอก
ส่งกลับคอลเลกชันเพื่อให้คุณสามารถทำสิ่งที่คุณต้องการกับมันหลังจากการโทร
public static Collection<File> listFileTree(File dir) {
Set<File> fileTree = new HashSet<File>();
if(dir==null||dir.listFiles()==null){
return fileTree;
}
for (File entry : dir.listFiles()) {
if (entry.isFile()) fileTree.add(entry);
else fileTree.addAll(listFileTree(entry));
}
return fileTree;
}
ฉันจะไปกับสิ่งที่ชอบ:
public void list(File file) {
System.out.println(file.getName());
File[] children = file.listFiles();
for (File child : children) {
list(child);
}
}
System.out.println อยู่ที่นั่นเพื่อระบุว่าจะทำอะไรกับไฟล์ ไม่จำเป็นต้องแยกความแตกต่างระหว่างไฟล์และไดเรกทอรีเนื่องจากไฟล์ปกติจะมีศูนย์ย่อย
listFiles()
:“ ถ้าชื่อพา ธ นามธรรมนี้ไม่แสดงถึงไดเรกทอรีวิธีนี้จะส่งกลับnull
”
ฉันชอบใช้คิวมากกว่าการสอบถามซ้ำสำหรับการสำรวจแบบง่าย ๆ :
List<File> allFiles = new ArrayList<File>();
Queue<File> dirs = new LinkedList<File>();
dirs.add(new File("/start/dir/"));
while (!dirs.isEmpty()) {
for (File f : dirs.poll().listFiles()) {
if (f.isDirectory()) {
dirs.add(f);
} else if (f.isFile()) {
allFiles.add(f);
}
}
}
เพียงแค่เขียนมันเองโดยใช้การเรียกซ้ำง่าย ๆ :
public List<File> addFiles(List<File> files, File dir)
{
if (files == null)
files = new LinkedList<File>();
if (!dir.isDirectory())
{
files.add(dir);
return files;
}
for (File file : dir.listFiles())
addFiles(files, file);
return files;
}
ฉันคิดว่าสิ่งนี้ควรทำงาน:
File dir = new File(dirname);
String[] files = dir.list();
วิธีนี้คุณมีไฟล์และ dirs ตอนนี้ใช้การเรียกซ้ำและทำเช่นเดียวกันสำหรับ dirs ( File
คลาสมีisDirectory()
เมธอด)
ด้วย Java 7 คุณสามารถใช้คลาสต่อไปนี้:
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
public class MyFileIterator extends SimpleFileVisitor<Path>
{
public MyFileIterator(String path) throws Exception
{
Files.walkFileTree(Paths.get(path), this);
}
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attributes) throws IOException
{
System.out.println("File: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attributes) throws IOException
{
System.out.println("Dir: " + dir);
return FileVisitResult.CONTINUE;
}
}
ใน Java 8 ตอนนี้เราสามารถใช้ยูทิลิตี Files เพื่อเดินแผนผังไฟล์ ง่ายมาก.
Files.walk(root.toPath())
.filter(path -> !Files.isDirectory(path))
.forEach(path -> System.out.println(path));
รหัสนี้พร้อมที่จะเรียกใช้
public static void main(String... args) {
File[] files = new File("D:/").listFiles();
if (files != null)
getFiles(files);
}
public static void getFiles(File[] files) {
for (File file : files) {
if (file.isDirectory()) {
getFiles(file.listFiles());
} else {
System.out.println("File: " + file);
}
}
}
นอกเหนือจากการสำรวจเส้นทางแบบวนซ้ำสามารถใช้วิธีการของผู้เยี่ยมชมได้เช่นกัน
โค้ดด้านล่างใช้วิธีตามผู้เยี่ยมชมสำหรับการสำรวจเส้นทางคาดว่าการป้อนข้อมูลไปยังโปรแกรมเป็นไดเรกทอรีรูทการสำรวจ
public interface Visitor {
void visit(DirElement d);
void visit(FileElement f);
}
public abstract class Element {
protected File rootPath;
abstract void accept(Visitor v);
@Override
public String toString() {
return rootPath.getAbsolutePath();
}
}
public class FileElement extends Element {
FileElement(final String path) {
rootPath = new File(path);
}
@Override
void accept(final Visitor v) {
v.visit(this);
}
}
public class DirElement extends Element implements Iterable<Element> {
private final List<Element> elemList;
DirElement(final String path) {
elemList = new ArrayList<Element>();
rootPath = new File(path);
for (File f : rootPath.listFiles()) {
if (f.isDirectory()) {
elemList.add(new DirElement(f.getAbsolutePath()));
} else if (f.isFile()) {
elemList.add(new FileElement(f.getAbsolutePath()));
}
}
}
@Override
void accept(final Visitor v) {
v.visit(this);
}
public Iterator<Element> iterator() {
return elemList.iterator();
}
}
public class ElementWalker {
private final String rootDir;
ElementWalker(final String dir) {
rootDir = dir;
}
private void traverse() {
Element d = new DirElement(rootDir);
d.accept(new Walker());
}
public static void main(final String[] args) {
ElementWalker t = new ElementWalker("C:\\temp");
t.traverse();
}
private class Walker implements Visitor {
public void visit(final DirElement d) {
System.out.println(d);
for(Element e:d) {
e.accept(this);
}
}
public void visit(final FileElement f) {
System.out.println(f);
}
}
}
คุณสามารถใช้รหัสด้านล่างเพื่อรับรายการไฟล์ของโฟลเดอร์เฉพาะหรือไดเรกทอรีซ้ำ
public static void main(String args[]) {
recusiveList("D:");
}
public static void recursiveList(String path) {
File f = new File(path);
File[] fl = f.listFiles();
for (int i = 0; i < fl.length; i++) {
if (fl[i].isDirectory() && !fl[i].isHidden()) {
System.out.println(fl[i].getAbsolutePath());
recusiveList(fl[i].getAbsolutePath());
} else {
System.out.println(fl[i].getName());
}
}
}
คำตอบที่ได้รับการยอมรับเป็นสิ่งที่ดี แต่มันหยุดลงเมื่อคุณต้องการที่จะทำ IO ภายในแลมบ์ดา
นี่คือสิ่งที่คุณสามารถทำได้ถ้าการกระทำของคุณประกาศ IOExceptions
คุณสามารถปฏิบัติต่อสตรีมที่กรองเป็นIterable
และจากนั้นดำเนินการของคุณในลูปปกติสำหรับแต่ละวง วิธีนี้คุณไม่ต้องจัดการกับข้อยกเว้นภายในแลมบ์ดา
try (Stream<Path> pathStream = Files.walk(Paths.get(path))
.filter(Files::isRegularFile)) {
for (Path file : (Iterable<Path>) pathStream::iterator) {
// something that throws IOException
Files.copy(file, System.out);
}
}
พบเคล็ดลับที่นี่: https://stackoverflow.com/a/32668807/1207791
BFS แบบไม่เรียกซ้ำพร้อมรายการเดียว (ตัวอย่างที่เจาะจงคือการค้นหาไฟล์ * .eml):
final FileFilter filter = new FileFilter() {
@Override
public boolean accept(File file) {
return file.isDirectory() || file.getName().endsWith(".eml");
}
};
// BFS recursive search
List<File> queue = new LinkedList<File>();
queue.addAll(Arrays.asList(dir.listFiles(filter)));
for (ListIterator<File> itr = queue.listIterator(); itr.hasNext();) {
File file = itr.next();
if (file.isDirectory()) {
itr.remove();
for (File f: file.listFiles(filter)) itr.add(f);
}
}
รุ่นของฉัน (แน่นอนฉันสามารถใช้ built-in walk ใน Java 8 ;-)):
public static List<File> findFilesIn(File rootDir, Predicate<File> predicate) {
ArrayList<File> collected = new ArrayList<>();
walk(rootDir, predicate, collected);
return collected;
}
private static void walk(File dir, Predicate<File> filterFunction, List<File> collected) {
Stream.of(listOnlyWhenDirectory(dir))
.forEach(file -> walk(file, filterFunction, addAndReturn(collected, file, filterFunction)));
}
private static File[] listOnlyWhenDirectory(File dir) {
return dir.isDirectory() ? dir.listFiles() : new File[]{};
}
private static List<File> addAndReturn(List<File> files, File toAdd, Predicate<File> filterFunction) {
if (filterFunction.test(toAdd)) {
files.add(toAdd);
}
return files;
}
นี่เป็นวิธีแก้ปัญหาที่เรียบง่าย แต่ใช้งานได้อย่างสมบูรณ์แบบโดยใช้recursion
:
public static List<Path> listFiles(String rootDirectory)
{
List<Path> files = new ArrayList<>();
listFiles(rootDirectory, files);
return files;
}
private static void listFiles(String path, List<Path> collectedFiles)
{
File root = new File(path);
File[] files = root.listFiles();
if (files == null)
{
return;
}
for (File file : files)
{
if (file.isDirectory())
{
listFiles(file.getAbsolutePath(), collectedFiles);
} else
{
collectedFiles.add(file.toPath());
}
}
}
private void fillFilesRecursively(File file, List<File> resultFiles) {
if (file.isFile()) {
resultFiles.add(file);
} else {
for (File child : file.listFiles()) {
fillFilesRecursively(child, resultFiles);
}
}
}
ฉันมากับสิ่งนี้สำหรับการพิมพ์ไฟล์ / ชื่อไฟล์ทั้งหมดซ้ำ
private static void printAllFiles(String filePath,File folder) {
if(filePath==null) {
return;
}
File[] files = folder.listFiles();
for(File element : files) {
if(element.isDirectory()) {
printAllFiles(filePath,element);
} else {
System.out.println(" FileName "+ element.getName());
}
}
}
ตัวอย่างเอาต์พุตไฟล์ * .csv ในไดเร็กทอรีการค้นหาแบบเรียกซ้ำไดเร็กทอรีย่อยโดยใช้ Files.find () จาก java.nio:
String path = "C:/Daten/ibiss/ferret/";
logger.debug("Path:" + path);
try (Stream<Path> fileList = Files.find(Paths.get(path), Integer.MAX_VALUE,
(filePath, fileAttr) -> fileAttr.isRegularFile() && filePath.toString().endsWith("csv"))) {
List<String> someThingNew = fileList.sorted().map(String::valueOf).collect(Collectors.toList());
for (String t : someThingNew) {
t.toString();
logger.debug("Filename:" + t);
}
}
การโพสต์ตัวอย่างนี้เนื่องจากฉันมีปัญหาในการทำความเข้าใจวิธีส่งพารามิเตอร์ชื่อไฟล์ในตัวอย่าง # 1 ที่ได้รับจาก Bryan โดยใช้ foreach ใน Stream-result -
หวังว่านี่จะช่วยได้
Kotlin มีFileTreeWalk
จุดประสงค์นี้ ตัวอย่างเช่น:
dataDir.walkTopDown().filter { !it.isDirectory }.joinToString("\n") {
"${it.toRelativeString(dataDir)}: ${it.length()}"
}
จะสร้างรายการข้อความของไฟล์ที่ไม่ใช่ไดเร็กทอรีทั้งหมดภายใต้รูทที่กำหนดหนึ่งไฟล์ต่อบรรทัดที่มีพา ธ ที่สัมพันธ์กับรูทและความยาว
อีกวิธีหนึ่งที่คุณสามารถทำได้แม้ว่าจะมีใครให้ Java 8 walk
ไฟล์นี้จะให้ไฟล์ทั้งหมดซ้ำ
private Stream<File> files(File file) {
return file.isDirectory()
? Arrays.stream(file.listFiles()).flatMap(this::files)
: Stream.of(file);
}
ตามคำตอบ stacker นี่คือโซลูชันที่ทำงานใน JSP โดยไม่มีไลบรารีภายนอกดังนั้นคุณจึงสามารถวางมันไว้เกือบทุกที่บนเซิร์ฟเวอร์ของคุณ:
<!DOCTYPE html>
<%@ page session="false" %>
<%@ page import="java.util.*" %>
<%@ page import="java.io.*" %>
<%@ page contentType="text/html; charset=UTF-8" %>
<%!
public List<String> files = new ArrayList<String>();
/**
Fills files array with all sub-files.
*/
public void walk( File root ) {
File[] list = root.listFiles();
if (list == null) return;
for ( File f : list ) {
if ( f.isDirectory() ) {
walk( f );
}
else {
files.add(f.getAbsolutePath());
}
}
}
%>
<%
files.clear();
File jsp = new File(request.getRealPath(request.getServletPath()));
File dir = jsp.getParentFile();
walk(dir);
String prefixPath = dir.getAbsolutePath() + "/";
%>
จากนั้นคุณก็ทำสิ่งที่ชอบ:
<ul>
<% for (String file : files) { %>
<% if (file.matches(".+\\.(apk|ipa|mobileprovision)")) { %>
<li><%=file.replace(prefixPath, "")%></li>
<% } %>
<% } %>
</ul>