【问题标题】:Replacing variables in Strings and properly allowing escaping替换字符串中的变量并正确允许转义
【发布时间】:2014-10-18 01:41:00
【问题描述】:

我有一个项目,其中有需要注入字符串的变量。假设我已经编写了方法并将其称为 replaceVariables 并将 {varaible} 替换为 foo

replaceVariables("This variable should be replaced {variable}")
    // "This variable should be replaced foo"

replaceVariables("This variable should NOT be replaced \\{variable\\}")
    // "This variable should NOT be replaced {variable}"

replaceVariables("This variable should be replaced \\\\{variable\\\\}")
    // "This variable should be replaced \foo\"

我可以编写一个自定义解析器来检测反斜杠并完成所有这些,但我真的觉得我在这里重新发明了轮子。

在 Apache 或 Spring 库中是否有这样的实现?我从春天就知道有 SPEL,但我觉得这种方式过头了。其他可能性是正则表达式,但无论我如何尝试构建正则表达式,它似乎总是需要在检查转义 \ 之前查看 1,也就是您检测到反斜杠,但可能是反斜杠本身被转义等等(虽然我有点讨厌正则表达式)。

注意:我必须遵循{variableName} 命名约定。

【问题讨论】:

  • 我认为第三个测试没有意义。如果大括号内有文字 \,则大括号内的值不是 variable
  • @bowmore 同意。为什么我不想编写自己的实现的案例和要点:)

标签: java string-matching


【解决方案1】:

我认为使用正则表达式可以安全地完成:

public class VariableReplace {
    private class Substitution {
        private final String variable;
        private final String value;

        private Substitution(String variable, String value) {
            this.variable = variable;
            this.value = value;
        }

        public String getRegex() {
            return "((\\\\)*)\\{\\Q" + variable + "\\E\\}";
        }

        public String getValue() {
            return "$1" + value;
        }
    }

    private final List<Substitution> substitutions = new ArrayList<>();

    public void replace(String variable, String foo) {
        substitutions.add(new Substitution(variable, foo));
    }

    public String replaceVariables(String s) {
        String result = s;
        for (Substitution substitution : substitutions) {
            result = result.replaceAll(substitution.getRegex(), substitution.getValue());
        }
        result = result.replaceAll("\\\\(.)", "$1");
        return result;
    }
}

测试类:

public class VariableReplaceTest {

    private VariableReplace variableReplace = new VariableReplace();

    @Before
    public void setUp() {
        variableReplace.replace("variable", "foo");
    }

    @Test
    public void testNormal() {
        assertEquals("This variable should be replaced foo", variableReplace.replaceVariables("This variable should be replaced {variable}") );
    }

    @Test
    public void testEscaped() {
        assertEquals("This variable should NOT be replaced {variable}", variableReplace.replaceVariables("This variable should NOT be replaced \\{variable\\}") );
    }

    @Test
    public void testDoubleEscaped() {
        assertEquals("This variable should be replaced \\foo", variableReplace.replaceVariables("This variable should be replaced \\\\{variable}") );
    }

    @Test
    public void testVariableContainsRegexChars() {
        variableReplace.replace("var[iable", "foo");
        assertEquals("This variable should be replaced foo", variableReplace.replaceVariables("This variable should be replaced {var[iable}") );
    }

}

警告:可能需要进一步测试

【讨论】:

    【解决方案2】:

    如果您将replaceAll() 与正则表达式一起使用,则您只需要一行,该正则表达式在后面使用否定的外观来断言大括号没有被转义:

    str.replaceAll("(?<![^\\\\]\\\\\\\\)\\{\\Q" + name + "\\E\\}", value));
    

    我故意不考虑允许在后面加上转义的反斜杠的尾括号,因为这很荒谬。

    这是一些测试代码:

    String name = "foo", value = "bar";
    String[] strs = {"abc {foo} def", "abc \\\\{foo} def", "abc \\\\\\\\{foo} def"};
    for (String str : strs) 
        System.out.println(str + " --> " + str.replaceAll("(?<![^\\\\]\\\\\\\\)\\{\\Q" + name + "\\E\\}", value));
    

    输出:

    abc {foo} def --> abc bar def
    abc \\{foo} def --> abc \\{foo} def
    abc \\\\{foo} def --> abc \\\\bar def
    

    【讨论】:

      猜你喜欢
      • 2014-12-11
      • 2020-02-04
      • 1970-01-01
      • 2014-12-31
      • 1970-01-01
      • 2013-02-16
      • 2016-06-13
      • 1970-01-01
      相关资源
      最近更新 更多