Java Map replaceAll 与多个字符串匹配 [英] Java Map replaceAll with multiple String matches

查看:36
本文介绍了Java Map replaceAll 与多个字符串匹配的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下程序,我想用其对应的值替换所有出现的字符串,其中单词作为映射中的键存在.

I have the following program that I would like to replace all occurrences of a string where a word exists as a key in a map with its corresponding value.

我已经实现了 4 种方法.它们中的每一个都执行大致相同的功能,但方式不同.前 3 个的输出不正确,因为下一个替换会覆盖前一个的结果.第四个有效,但只是因为我要替换整个字符串中的单个字符.无论如何,这非常低效,因为我只检查整个字符串的子字符串.

I have implemented 4 methods. Each of them perform roughly the same function but in a different way. The output for the first 3 are incorrect as the next replacement overrides the result of the previous. The fourth works, but only because I am replacing single characters in the whole string. This is very inefficient anyways, because I am only checking a substring of the entire string.

有没有办法安全地替换所有出现的内容而不覆盖之前的替换内容?

Is there a way to safely replace all occurrences without overwriting the previous replacements?

我注意到 Apache 有一个 StringUtils.replaceEach() 方法,但我更喜欢使用地图.

I noticed that Apache has a StringUtils.replaceEach() method, but I would prefer to use a map.

输出:

Apple BApplenApplenApple CApplentApplelope DApplete Apple BApplenApplenApple CApplentApplelope DApplete
Apple BApplenApplenApple CApplentApplelope DApplete Apple BApplenApplenApple CApplentApplelope DApplete
Apple BApplenApplenApple CApplentApplelope DApplete Apple BApplenApplenApple CApplentApplelope DApplete
Apple Banana Cantalope Date Apple Banana Cantalope Date

ReplaceMap.java

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ReplaceMap {
    private static Map<String, String> replacements;

    static {
        replacements = new HashMap<String, String>();
        replacements.put("a", "Apple");
        replacements.put("b", "Banana");
        replacements.put("c", "Cantalope");
        replacements.put("d", "Date");
    }

    public ReplaceMap() {
        String phrase = "a b c d a b c d";

        System.out.println(mapReplaceAll1(phrase, replacements));
        System.out.println(mapReplaceAll2(phrase, replacements));
        System.out.println(mapReplaceAll3(phrase, replacements));
        System.out.println(mapReplaceAll4(phrase, replacements));
    }

    public String mapReplaceAll1(String str, Map<String, String> replacements) {
        for (Map.Entry<String, String> entry : replacements.entrySet()) {
            str = str.replaceAll(entry.getKey(), entry.getValue());
        }

        return str;
    }

    public String mapReplaceAll2(String str, Map<String, String> replacements) {
        for (String key : replacements.keySet()) {
            str = str.replaceAll(Pattern.quote(key),
                    Matcher.quoteReplacement(replacements.get(key)));
        }

        return str;
    }

    public String mapReplaceAll3(String str, Map<String, String> replacements) {        
        String regex = new StringBuilder("(")
            .append(join(replacements.keySet(), "|")).append(")").toString();
        Matcher matcher = Pattern.compile(regex).matcher(str);

        while (matcher.find()) {
            str = str.replaceAll(Pattern.quote(matcher.group(1)),
                    Matcher.quoteReplacement(replacements.get(matcher.group(1))));
        }

        return str;
    }

    public String mapReplaceAll4(String str, Map<String, String> replacements) {        
        StringBuilder buffer = new StringBuilder();
        String regex = new StringBuilder("(")
            .append(join(replacements.keySet(), "|")).append(")").toString();
        Pattern pattern = Pattern.compile(regex);

        for (int i = 0, j = 1; i < str.length(); i++, j++) {
            String s = str.substring(i, j);
            Matcher matcher = pattern.matcher(s);


            if (matcher.find()) {
                buffer.append(s.replaceAll(Pattern.quote(matcher.group(1)),
                            Matcher.quoteReplacement(replacements.get(matcher.group(1)))));
            } else {
                buffer.append(s);
            }
        }


        return buffer.toString();
    }

    public static String join(Collection<String> s, String delimiter) {
        StringBuilder buffer = new StringBuilder();
        Iterator<String> iter = s.iterator();
        while (iter.hasNext()) {
            buffer.append(iter.next());
            if (iter.hasNext()) {
                buffer.append(delimiter);
            }
        }
        return buffer.toString();
    }

    public static void main(String[] args) {
        new ReplaceMap();
    }
}

推荐答案

我会这样做:

replace(str, map)
    if we have the empty string, the result is the empty string.
    if the string starts with one of the keys from the map:
        the result is the replacement associated with that key + replace(str', map)
             where str' is the substring of str after the key
    otherwise the result is the first character of str + replace(str', map)
             where str' is the substring of str without the first character

请注意,虽然以递归方式制定,但它可以(并且应该,由于 Java 臭名昭著的小堆栈空间)实现为循环并将结果的第一部分(即替换字符串或第一个字符)写入 stringbuilder.

Note that, though formulated recursively, it can (and should, due to Javas notorious small stack space) be implemented as a loop and write the first part of the result (i.e. the replacement string or the first character) to a stringbuilder.

如果您在映射中有一个键是某个其他键的前缀(即键"、键"),您可能需要尝试减小长度的键.

If you have a key in the map that is a prefix of some other key (i.e. "key", "keys") you may need to try the keys in decreasing length.

请进一步注意,可以设计使用 Tries 而不是 HasMaps 的更快算法.这也将是对不明确的关键问题的补救.

Note further, that a faster algorithm could be devised that uses Tries instead of HasMaps. This would also be a remedy for the ambiguous key problem.

这是一个大纲(未经测试):

Here is an outline (not tested):

public static String replace(String it, Map<String, String> map) {
    StringBuilder sb = new StringBuilder();
    List<String> keys = map.keySet();      // TODO: sort by decreasing length!!
    next: while (it.length() > 0) {
        for (String k : keys) {
            if (it.startsWith(k)) {
                // we have a match!
                sb.append(map.get(k));
                it = it.substring(k.length(), it.length());
                continue next;
            }
        }
        // no match, advance one character
        sb.append(it.charAt(0));
        it = it.substring(1, it.length());
    }
    return sb.toString();
}

这篇关于Java Map replaceAll 与多个字符串匹配的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆