Не найдя пошагового руководства, но многочисленные примеры без объяснений, ПОЧЕМУ и КАК запускать рекурсивный поиск файлов с использованием регулярных выражений и передачи результата в переменную, я пишу один:
ВНИМАНИЕ: Я НОВИЧОК. Я многого не знаю или не совсем понимаю. В этом посте я излагаю самые современные то, что я знаю. Пожалуйста, поймите, что «то, что я знаю» на момент написания может быть неверным и неполным. Как говорится, поправки только приветствуются! Спасибо!
Моя программа предназначена для перемещения в родительскую папку и запуска для получения файлов из дочерних папок, поэтому ей возможно нужно знать свой рабочий каталог. К сожалению, нет (который я нашел) не-хакерского метода для получения этого.
Следующие две строки кода дадут вам путь к рабочему каталогу вашего Java-приложения. ПРИМЕЧАНИЕ: это должно быть расположением, из которого выполняется приложение.
Не забудьте импортировать соответствующие библиотеки. Я изучаю Java, поэтому не заинтересован в изучении чьей-либо любимой библиотеки дополнений, поэтому я придерживаюсь только библиотек Oracle Java. ПОЗЖЕ, когда у меня будет приличный опыт разработки на Java, я начну изучать крутые, удивительные возможности надстроек.
import java.io.*;
import java.io.File.*;
import java.nio.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import static java.nio.file.FileVisitResult.*;
import static java.nio.file.FileVisitOption.*;
import java.util.*;
import java.util.stream.*;
import java.util.regex.*;
ПРИМЕЧАНИЕ. Если вы получаете сообщение «не удается найти символ» или ошибки времени компиляции в методе, который вы захватили из SO или другого онлайн-ресурса, возможно, у вас нет импортированной библиотеки. ...спросите меня, откуда я знаю. ;)
Есть несколько способов получить рабочий каталог, но поскольку мне нужно передать рабочий каталог другой функции, работающей с файлами, путями, файловыми системами и т. д., я хотел использовать что-то «совместимое». Не в этом дело, видимо.
File workingDir = new File(new File(".").getAbsolutePath());
Path workDir = Paths.get(workingDir.getCanonicalPath());
Первая строка определяет место, где может быть создан "новый файл", а затем получает абсолютный путь к этому потенциальному файлу. Имя файла "." - что может быть немного запутанным...
Во второй строке указывается абсолютный путь к файлу "." (исключая имя файла) в переменную. Путь начинается с корневого каталога (т. е. с буквы диска в среде Windows). Дополнительную информацию об абсолютном каноническом пути можно найти примерно на 2/3 здесь: Описание абсолютного/канонического пути
Теперь мое приложение знает, где оно находится, мне нужен итерируемый объект для передачи записей - это переменная, в которую будут переданы файлы, соответствующие моему регулярному выражению:
java.util.List<String> files = new ArrayList<>();
Примечание. Я должен включить «java.util». в объявлении «Список», потому что я импортировал другие библиотеки, которые без него делают объявление неоднозначным. Ни один из этих кодов не актуален, поэтому не включен.
Теперь о рекурсивном поиске. Опять же, нет не-хакерского способа сделать это. Я предполагал, что это будет делаться достаточно часто, чтобы для этого был разработан чистый метод - я имею в виду, что я знаю как минимум 3 разных способа написать цикл for! - но я думаю, это отражает то, что люди в Oracle, разрабатывающие Java, используют больше, чем то, что простые «пользователи» (мы) делают с продуктом.
Я решил использовать PathMatcher, потому что мне также нужно указать папки, чтобы не искать файлы. 'Files.find()' и многочисленные классы, которые талантливые люди пытались запрограммировать, не имели такого простого метода для этого.
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("regex:.*Correct_\\d{4}-\\d{2}-\\d{2}_\\d{2}-\\d{2}\\.txt");
Files.walkFileTree(Paths.get(workDir.toString()), new SimpleFileVisitor<Path>()
{
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
if (dir.getFileName().toString().equals("Reprocess"))
{
return SKIP_SUBTREE;
}
return CONTINUE;
}//end public preVisitDirectory
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
if (matcher.matches(file))
{
files.add(file.toString());
}//end if(matcher)
return FileVisitResult.CONTINUE;
}//end visitFile
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
{
return FileVisitResult.CONTINUE;
}//end visitFileFailed
});//end Files.walkFileTree1
Пара замечаний по моему коду: 1. Как начинающий программист, я считаю полезным отслеживать закрывающие фигурные скобки, чтобы обозначить, кому принадлежит каждая из них; 2. Мое регулярное выражение очень специфично, потому что я ищу программный вывод - каждый раз он будет одним и тем же.
Объявление PathMatcher: Final указывает Java, что это не изменится. «FileSystems.getDefault()» идентифицирует ОС, поэтому косые черты указывают правильный путь (среди прочего). 'getPathMatcher(' выполняет функцию, подобную 'Pattern.compile()'.
«regex:» определяет поиск как «regex», а не как поиск «glob». Я выделил эту строку, потому что было трудно найти пример синтаксиса для регулярного выражения в этом контексте.
Еще одно замечание о моем конкретном шаблоне регулярных выражений: он не работает без «.*», который обозначает «любое количество предшествующих символов». Моя гипотеза заключается в том, что это учитывает рабочий каталог, добавляемый как абсолютный путь к каждому файлу. искал совпадение. ... мне потребовалось некоторое время, чтобы понять, почему работающее регулярное выражение (я тестировал в буферизованном читателе с некоторыми вставленными именами файлов) не возвращало результатов.
Files.walkFileTree: выполнение метода 'walkFileTree' класса Files с 'Paths.get(' в качестве аргумента. 'Paths.get(' имеет рабочий каталог, который мы установили ранее, в качестве аргумента.
Ранее я упоминал, что использование чего-то «совместимого» между файлами, путями и файловыми системами не имеет значения. Что ж, при моем нынешнем уровне навыков единственный способ передать объект «Список» для информации о пути — сначала сделать его строковым объектом. Итак, у нас есть "workDir.toString()". К вашему сведению, жесткое кодирование пути к моему рабочему каталогу также сработало.
ПРИМЕЧАНИЕ. «..» указывает относительный путь к местоположению, из которого было выполнено приложение Java. Вот почему я сказал ранее, что моя программа «может» должна знать свой рабочий каталог — оказывается, моя не знает, а ваша может. Вместо этого я использую свою переменную, содержащую абсолютный путь к моему рабочему каталогу (никакого заметного влияния на выполнение моего кода). Вы можете передать «..» (включая двойные кавычки) вместо «workDir.toString()», если вам просто нужен относительный путь.
новый SimpleFileVisitor: экземпляр простого посетителя файлов.
Первый аргумент указывает инструкции preVisitDirectory; здесь я указываю своей программе не выполнять поиск в папке «Повторно обработать». Есть много других инструкций, которые можно вставить сюда, если это необходимо.
Второй аргумент указывает, что моя программа должна делать, когда находит совпадение — в этом случае совпадение добавляется в список «файлов» ArrayList.
Третий аргумент предписывает программе не беспокоиться, если совпадение не найдено.
Конец.
Ну, не совсем, но этот код выполняет рекурсивный поиск файлов с использованием регулярных выражений и выводит совпадения с переменной (в данном случае ArrayList).
Получение имен файлов обратно в работоспособном формате также оказывается сложной задачей. Я уверен, что это потому, что я определил ArrayList как строку вместо файла (или другую ошибку нуба, или три...).
Удачи! Не стесняйтесь обращаться ко мне (если это так), если у вас есть какие-либо вопросы, комментарии или проблемы.
Джейк
ОБНОВЛЕНИЕ: мне удалось выяснить, как передавать имена файлов в FileInputStream. Изменения, ниже:
java.util.List<String> files = new ArrayList<>();
стал:
java.util.List<File> files = new ArrayList<>();
аргумент visitFule в функции Files.walkFileTree стал таким:
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
if (matcher.matches(file))
{
files.add(file.toFile());
//'files.add(file.toString());' changed
}//end if(matcher)
Следующий код был добавлен для преобразования ArrayList файлов в fileArray, Array, затем преобразования каждого элемента в Array в строку во время выполнения и передачи этой строки в FileInputStream (обернутый в BufferedReader).
int fLength = files.size();
File[] fileArray = files.toArray(new File[fLength]);
for(int f=0; f<files.size(); f++)
{
//log file Reader init:
String corrFile = fileArray[f].toString();
BufferedReader corrReader = new BufferedReader(new InputStreamReader(new FileInputStream(corrFile),"UTF-16LE"));
//NOTE: PFO differential correction log files are encoded in UTF-16 LE
...аааааааааааааааааааааааааааааааааааааааааа! Этот аспект моего проекта завершен и работает правильно.
17.07.2018