首页 > 解决方案 > 正则表达式从输入字符串中提取表达式

问题描述

我正在尝试使用正则表达式从输入字符串中提取“操作数运算符操作数”格式的表达式。操作数可以是任何单个单词或引用的短语,操作数之间的运算符将是波浪号后跟数字。该表达式可能在输入字符串中出现 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;
    }
}

标签: javaregex

解决方案


由于您的代码包含一个不错的单元测试,因此建议进行以下修复/更改:

  • 如果为 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 这样的语法解析器库。在那里,您可以轻松转到嵌套表达式并获取抽象语法树 (AST)


推荐阅读