Сканирование файла и сбор полного слова, соответствующего шаблону
Я работаю над проектом, в котором мне нужно отсканировать папку и отсканировать каждый файл на предмет конкретного слова (скажем, "@MyPattern").
Я с нетерпением ждал лучшего подхода к разработке такого сценария. Для начала я работал, как показано ниже:
//Read File
List<String> lines = new ArrayList<>();
try (Stream<String> stream = Files.lines(Paths.get(fileName))) {
stream.forEach(line-> lines.add(line));
} catch (IOException e) {
e.printStackTrace();
}
//Create a pattern to find for
Predicate<String> patternFilter = Pattern
.compile("@MyPattern^(.+)")
.asPredicate();
//Apply predicate filter
List<String> desiredWordsMatchingPattern = lines
.stream()
.filter(patternFilter)
.collect(Collectors.<String>toList());
//Perform desired operation
desiredWordsMatchingPattern.forEach(System.out::println);
Я не уверен, почему это не работает, хотя в файле есть несколько слов, соответствующих '@MyPattern'.
2 ответа
Как вы используете ^(.+)
не имеет смысла в регулярном выражении. ^
соответствует началу строки (строки), но начало строки не может следовать за шаблоном (только если шаблон будет соответствовать пустой строке, чего здесь нет). Таким образом, ваш шаблон никогда не может соответствовать ни одной линии.
Просто используйте:
Predicate<String> patternFilter = Pattern
.compile("@MyPattern")
.asPredicate();
Если вам требуется, чтобы после шаблона не было символов (даже пробелов), $
соответствует концу строки:
Predicate<String> patternFilter = Pattern
.compile("@MyPattern$")
.asPredicate();
Вот мое решение:
// can extract annotation and text-inside-parentheses
private static final String REGEX = "@(\\w+)\\((.+)\\)";
//Read File
List<String> lines = Files.readAllLines(Paths.get(filename));
//Create a pattern to find for
Pattern pattern = Pattern.compile(REGEX);
// extractor function uses pattern's second group (text-within-parentheses)
Function<String, String> extractOnlyTextWithinParentheses = s -> {
Matcher m = pattern.matcher(s);
m.find();
return m.group(2);
};
// all lines are filtered and text will be extracted using extractor-fn
Stream<String> streamOfExtracted = lines.stream()
.filter(pattern.asPredicate())
.map(extractOnlyTextWithinParentheses);
//Perform desired operation
streamOfExtracted.forEach(System.out::println);
Объяснение:
Давайте сначала выясним, для чего используется регулярное выражение @(\\w+)\\((.+)\\)
следует сделать:
Предполагая: вы фильтруете текст для Java-подобной аннотации, например
@MyPattern
сопоставление определенных строк с использованием регулярного выражения
@\\w+
соответствует символу at, за которым следует слово (\\w
имеет особое значение и обозначает слово, то есть буквенные буквы и подчеркивания). Так что это будет соответствовать любой аннотации (например,@Trace
,@User
и так далее).\\(.+\\)
соответствует тексту в скобках (например,("10869")
где скобки тоже должны быть экранированы\\(
а также\\)
а также.+
для любого непустого текста внутри
Примечание: неэкранированные скобки имеют специальное значение внутри любого регулярного выражения, то есть группировки и захвата
Соответствующие скобки и извлечение их содержимого см. В ответе " Шаблон" для извлечения текста между скобками.
извлечение текста с использованием групп захвата внутри регулярного выражения
Просто используйте круглые скобки (без экранирования), чтобы сформировать группу и запомнить их номер заказа.(grouped)(Regex)
будет соответствовать тексту groupedRegex
и может извлечь две группы:
- группа 1:
grouped
- группа № 2:
Regex
Чтобы получить эти группы используйтеmatcher.find()
а потомmatcher.group()
или его перегруженные методы.
возможность проверить регулярное выражение и извлечение
Находясь внутри IntelliJ, вы можете использовать действие Проверить RegExp в IntelliJ: ALT+Enter для выбранного регулярного выражения, чтобы проверить и адаптировать его. Похоже есть довольно много сайтов для проверки регулярных выражений. Например, http://www.regexplanet.com/ также поддерживает синтаксис Java-RegEx, и вы можете проверить извлеченные группы онлайн. Смотрите пример на RegexPlanet.
Примечание: кроме начала есть одно особое значение каретки, как Оле ответил выше:
[^)]+
означает совпадение с чем угодно (не менее 1 символа), кроме закрывающих скобок
сделать его расширяемым с помощью функции экстрактора
Если вы замените функцию извлечения, используемую в качестве аргумента .map(..)
выше, следуя, вы также можете напечатать аннотацию-имя и текст внутри скобок (разделенные табуляцией):
Function<String, String> extractAnnotationAndTextWithinParentheses = s -> {
Matcher m = pattern.matcher(s);
m.find();
StringBuilder sb = new StringBuilder();
int lastGroup = m.groupCount();
for (int i = 1; i <= lastGroup; i++) {
sb.append(m.group(i));
if (i < lastGroup) sb.append("\t");
}
return sb.toString();
};
Резюме:
Ваша трансляция была эффективной. Ваше регулярное выражение имело ошибку:
- это почти соответствует постоянной аннотации, а именно
@MyPattern
- Вы пытались получить исправление с помощью скобок
- была ошибка синтаксиса или опечатка внутри вашего регулярного выражения, каретки
^
- не используя экранированные скобки
\\(
а также\\)
Вы бы получили не только текст внутри, но и скобки, как экстракт