Java Regex String#replaceAll Alternative

后端 未结 2 1146
时光说笑
时光说笑 2020-12-07 03:18

I\'ve been trying to devise a method of replacing multiple String#replaceAll calls with a Pattern/Matcher instance in the hopes that it would be faster than my current metho

相关标签:
2条回答
  • 2020-12-07 03:39

    This is a more dynamic version of previous answer to another similar question.

    Here is a helper method for searching for any @keyword@ you want. They don't have to be 3 characters long.

    private static String replace(String input, Map<String, String> replacement) {
        StringJoiner regex = new StringJoiner("|", "@(", ")@");
        for (String keyword : replacement.keySet())
            regex.add(Pattern.quote(keyword));
        StringBuffer output = new StringBuffer();
        Matcher m = Pattern.compile(regex.toString()).matcher(input);
        while (m.find())
            m.appendReplacement(output, Matcher.quoteReplacement(replacement.get(m.group(1))));
        return m.appendTail(output).toString();
    }
    

    The above runs on Java 8+. In Java 9+, this can be done with lambda expression. The following also fixes the potential issue of a short keyword being a substring of a longer one, by sorting the keywords descending by length.

    private static String replace(String input, Map<String, String> replacement) {
        String regex = replacement.keySet().stream()
                .sorted(Comparator.comparingInt(String::length).reversed())
                .map(Pattern::quote).collect(Collectors.joining("|", "@(", ")@"));
        return Pattern.compile(regex).matcher(input)
                .replaceAll(m -> Matcher.quoteReplacement(replacement.get(m.group(1))));
    }
    

    Test

    Map<String,String> replacement = new HashMap<>();
    replacement.put("bla", "hello,");
    replacement.put("red", "world!");
    replacement.put("Hold", "wait");
    replacement.put("Better", "more");
    replacement.put("a?b*c", "special regex characters");
    replacement.put("foo @ bar", "with spaces and the @ boundary character work");
    
    System.out.println(replace("@bla@This is a @red@line @bla@of text", replacement));
    System.out.println(replace("But @Hold@, this can do @Better@!", replacement));
    System.out.println(replace("It can even handle @a?b*c@ without dying", replacement));
    System.out.println(replace("Keyword @foo @ bar@ too", replacement));
    

    Output

    hello,This is a world!line hello,of text
    But wait, this can do more!
    It can even handle special regex characters without dying
    Keyword with spaces and the @ boundary character work too
    
    0 讨论(0)
  • 2020-12-07 03:41

    This is a relatively straightforward case for appendReplacement:

    // Prepare map of replacements
    Map<String,String> replacement = new HashMap<>();
    replacement.put("bla", "hello,");
    replacement.put("red", "world!");
    // Use a pattern that matches three non-@s between two @s
    Pattern p = Pattern.compile("@([^@]{3})@");
    Matcher m = p.matcher("@bla@This is a @red@line @bla@of text");
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
        // Group 1 captures what's between the @s
        String tag = m.group(1);
        String repString = replacement.get(tag);
        if (repString == null) {
            System.err.println("Tag @"+tag+"@ is unexpected.");
            continue;
        }
        // Replacement could have special characters, e.g. '\'
        // Matcher.quoteReplacement() will deal with them correctly:
        m.appendReplacement(sb, Matcher.quoteReplacement(repString));
    }
    m.appendTail(sb);
    String result = sb.toString();
    

    Demo.

    0 讨论(0)
提交回复
热议问题