1

我正在尝试使用 Java 的 Pattern 和 Matcher 执行多个字符串替换,其中正则表达式模式可能包含元字符(例如 \b、() 等)。例如,对于输入 string fit i am,我想应用替换:

\bi\b --> EYE
i     --> I

然后我遵循了两个问题的编码模式(Java Replacing multiple different substrings in a stringReplacing multiple substrings in Java when replacement text overlays search text)。在两者中,他们创建了一个 or'ed 搜索模式(例如 foo|bar)和一个(模式,替换)的映射,并且在matcher.find()循环内部,他们查找并应用替换。

我遇到的问题是该matcher.group()函数不包含有关匹配元字符的信息,因此我无法区分i\bi\b。请看下面的代码。我能做些什么来解决这个问题?

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

public class ReplacementExample
{
    public static void main(String argv[])
    {
        Map<String, String> replacements = new HashMap<String, String>();
        replacements.put("\\bi\\b", "EYE");
        replacements.put("i", "I");

        String input = "fit i am";

        String result = doit(input, replacements);

        System.out.printf("%s\n", result);
    }


    public static String doit(String input, Map<String, String> replacements)
    {
        String patternString = join(replacements.keySet(), "|");
        Pattern pattern = Pattern.compile(patternString);
        Matcher matcher = pattern.matcher(input);
        StringBuffer resultStringBuffer = new StringBuffer();

        while (matcher.find())
        {
            System.out.printf("match found: %s at start: %d, end: %d\n",
                matcher.group(), matcher.start(), matcher.end());

            String matchedPattern = matcher.group();
            String replaceWith = replacements.get(matchedPattern);

            // Do the replacement here.
            matcher.appendReplacement(resultStringBuffer, replaceWith);
        }

        matcher.appendTail(resultStringBuffer);

        return resultStringBuffer.toString();
    }

    private static String join(Set<String> set, String delimiter)
    {
        StringBuilder sb = new StringBuilder();
        int numElements = set.size();
        int i = 0;

        for (String s : set)
        {
            sb.append(Pattern.quote(s));
            if (i++ < numElements-1) { sb.append(delimiter); }
        }

        return sb.toString();
    }
}

这打印出来:

match found: i at start: 1, end: 2
match found: i at start: 4, end: 5
fIt I am

理想情况下,它应该是fIt EYE am

4

2 回答 2

0

您输入错误的正则表达式之一:

    replacements.put("\\bi\\", "EYE"); //Should be \\bi\\b
    replacements.put("i", "I");

您可能还想让您的正则表达式独一无二。map.getKeySet() 不能保证顺序,因此它可能只是在检查之前将 i 替换为 I \\bi\\b

于 2012-05-15T18:31:45.907 回答
0

您可以使用捕获组,而不会偏离您现有的设计太远。因此,您无需使用匹配的模式作为键,而是根据列表中的顺序进行查找。

您需要更改join方法以在每个模式周围加上括号,如下所示:

private static String join(Set<String> set, String delimiter) {
    StringBuilder sb = new StringBuilder();
    sb.append("(");
    int numElements = set.size();
    int i = 0;
    for (String s : set) {
        sb.append(s);
        if (i++ < numElements - 1) {
            sb.append(")");
            sb.append(delimiter);
            sb.append("(");         }
    }
    sb.append(")");
    return sb.toString();
}

附带说明一下,在原始代码清单中使用 Pattern.quote 会导致匹配在存在这些元字符的地方失败。

完成此操作后,您现在需要确定哪个捕获组负责匹配。为简单起见,我将假设所有匹配模式本身都不会包含捕获组,在这种情况下,在匹配器 while 循环中,这样的事情会起作用:

        int index = -1;
        for (int j=1;j<=replacements.size();j++){
            if (matcher.group(j) != null) {
                index = j;
                break;
            }

        }
        if (index >= 0) {
            System.out.printf("Match on index %d = %s %d %d\n", index, matcher.group(index), matcher.start(index), matcher.end(index));
        }

接下来,我们想使用生成的索引值直接索引到替换中。原代码使用了HashMap,不适合这个;您将不得不对其进行重构,以便以某种形式使用一对列表,一个包含匹配模式列表,另一个包含相应的替换字符串列表。我不会在这里这样做,但我希望能提供足够的细节来创建一个可行的解决方案。

于 2012-05-15T22:55:49.397 回答