Regex для извлечения выражений из входной строки - PullRequest
0 голосов
/ 19 февраля 2020

Я пытаюсь использовать регулярное выражение для извлечения выражений в формате «операнд оператора-операнда» из входной строки. Операндами могут быть любые отдельные слова или фразы в кавычках, а оператор между операндами будет знаком тильды, за которым следует цифра. Выражение может встречаться n раз в строке ввода. Что не так в моем регулярном выражении?

package test;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ExpressionExtractor {

    private static final String operator = "\\s+\\~{1}\\d+\\s+";
    private static final String quotedWords = "[\"|'|“][A-Za-zÀ-ü0-9\\\\.\\/\\-,\\*\\s]+[\"|'|“]";
    private static final String singleWords = "[A-Za-zÀ-ü0-9\\\\.\\/\\-,\\*]+";
    private static final String operand = quotedWords+"|"+singleWords;
    private static final Pattern expressionPattern = Pattern.compile("("+operand + operator +operand+")");
    private static final Pattern operatorPattern = Pattern.compile(operator);

    public static Matcher evaluateExpression(String input) {
        return expressionPattern.matcher(input);
    }
}

Тестовый пример с ожидаемыми результатами:

package test;

import static org.junit.Assert.assertEquals;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;

import org.junit.Test;

public class ExpressionExtractorUnitTest {

    @Test
    public void testExpressionExtractor() {
        assertEquals(Arrays.asList("firstWord ~20 secondWord"), find("any word firstWord ~20 secondWord one more word"));
        assertEquals(Arrays.asList("mother-in-law ~8 long-Word"), find("start of sentence mother-in-law ~8 long-Word one sentence"));
        assertEquals(Arrays.asList("firstWord ~7 secondWord", "word ~8 \"complex expression\""), 
                find("more complex expression firstWord ~7 secondWord with another word ~8 \"complex expression\" continued"));
    }

    private List<String> find(String expression) {
        Matcher matcher = ExpressionExtractor.evaluateExpression(expression);
        List<String> tokens = new ArrayList<>();
        while (matcher.find()) {
            tokens.add(matcher.group());
        }
        return tokens;
    }
}

1 Ответ

0 голосов
/ 20 февраля 2020

Поскольку ваш код включает в себя хороший модульный тест, здесь предлагаются исправления / изменения:

  • Вам не нужно указывать номер символа, если он равен 1, поэтому «~ {1}» может стать "~". Вам не нужно экранировать символ тильды, поэтому "\ s + \ ~ {1} \ d + \ s +" может стать "\ s + ~ \ d + \ s +". Там, если вам не нравится «забор», вы можете использовать другой символ, например, такой:
String operator = "!s+~!d+!s+".replace('!','\\').
  • Я нахожу выражение для цитируемых слов слишком сложно следовать. Вы можете попробовать группу, начинающуюся с символа кавычки, за которым следует 0 или более символов без кавычек, а затем кавычка. Кроме того, вам не нужна труба в квадратных скобках
String quotedWords = "[\"|'|“][^\"|'|“]*[\"|'|“]";
  • Чтобы соответствовать множеству альтернативных шаблонов, необходимо добавить дополнительные скобки для каждой группы
expressionPattern = Pattern.compile("(" + operand + ")(" +operator + ")(" +operand + ")");

Тесты пройдут успешно. Однако вы можете использовать библиотеку грамматического анализатора, такую ​​как ANTLR. Там вы можете легко go для вложенных выражений и получить абстрактное синтаксическое дерево (AST)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...