【问题标题】:Check String against list of characters and replace it dynamically - Regex根据字符列表检查字符串并动态替换它 - 正则表达式
【发布时间】:2022-01-10 05:12:00
【问题描述】:

我正在尝试找到解决此问题的方法。 我有需要用原始字符映射的特定字符替换的字符列表。

例如:我有字符映射,其中包含字符及其替换值。 字符映射:

Map<String, String> characterMap = new HashMap<String, String>();
characterMap.put("&", "\x26");
characterMap.put("^", "\x5e");

需要替换的字符串:String hello = "Hello&amp; World^"; 我想用地图中的值替换 hello 字符串。此地图是根据属性文件创建的,并且是动态的。

我可以通过正则表达式来实现吗? 我可以在不迭代字符映射的情况下实现这一点吗?

【问题讨论】:

  • Pattern.compile(".").matcher(hello).replaceAll(m -&gt; characterMap.getOrDefault(m.group(), m.group()))

标签: java regex spring-boot


【解决方案1】:

您可以使用此代码:

Map<String, String> characterMap = new HashMap<>();
characterMap.put("&", "\\x26");
characterMap.put("^", "\\x5e");

String hello = "Hello& World^"; 

Pattern.compile("\\W").matcher(hello).replaceAll(
   m -> characterMap.getOrDefault(m.group(), m.group())
        .replaceAll("\\\\", "$0$0"));

输出:

"Hello\\x26 World\\x5e"

详情:

  • 在主正则表达式中,我们匹配\\W,它将匹配任何非单词
  • 我们从characterMap 中提取每个匹配的非单词字符的值,或者如果找不到该键,我们会返回相同的字符。
  • 我们在提取的值上调用.replaceAll("\\\\", "$0$0") 以获得正确的转义(假设值仅使用单个转义)。 $0 是我们在正则表达式中匹配的完整字符串 \\\\ 并使用 $0$0 我们将其设为 \\\\\\\\

Code Demo


另一种优化的方法是使用地图的键构造正则表达式,如下所示:

Pattern p = Pattern.compile(characterMap.keySet().stream()
   .map(s -> Pattern.quote(s)).collect(Collectors.joining("|")));

// then use it with . getOrDefault
p.matcher(hello).replaceAll(m -> 
   characterMap.get(m.group()).replaceAll("\\\\", "$0$0"));
// => "Hello\\x26 World\\x5e"

【讨论】:

  • 您能告诉我为什么我们有 $0$0 部分吗?
  • 我在答案中添加了解释,也检查代码演示。
【解决方案2】:

反斜杠必须转义,导致两个反斜杠\\;在 java 字符串文字和 .properties 文件中。

    Map<String, String> characterMap = new LinkedHashMap<>(); // Linked = keep order of inserting.
    characterMap.put("&", "\\x26");
    characterMap.put("^", "\\x5e");

但你提到源是一个文件。

    ResourceBundle bundle = ResourceBundle.getBundle("charmap");

    String hello = "Hello& World^";

    System.out.println("Replaced: " + mapReplace(hello, bundle));

charmap.properties:

\\ = xx
& = \\x26
^ = \\x5e

为了不被替换,最好按字符替换。

private String mapReplace(String s, ResourceBundle characterMap) {
    Pattern p = Pattern.compile(".");
    Matcher m = p.matcher(s);
    return m.replaceAll(mr -> {
        String c = mr.group();
        if (characterMap.containsKey(c)) {
            c = m.quoteReplacement(characterMap.getString(c));
        }
        return c;
    });
}

Matcher#replaceAll 期望替换为正则表达式样式,反斜杠再次转义,美元 $ 也是如此。 为此可以使用Matcher#quoteReplacement

最后但并非最不重要的一点,如果您只想替换 \x..

private String mapReplace(String s, String keys) {
    return Pattern.compile("[" + keys.replaceAll(".", "\\\\$0") + "]").matcher(s)
            .replaceAll(mr -> String.format("\\\\x%02x", (int)mr.group().charAt(0)));
}

hello = mapReplace(hello, "^&");

keys.replaceAll(".", "\\\\$0") 需要有正则表达式 [\^\&amp;] aka "[\\^\\&amp;]"-^ 和其他在正则表达式中有特殊含义。

通常建议使用\uXXXX 而不是\xXX


看到这两个字符,这个任务看起来就像一个 ASCII 到 EBCDIC 的转换。这可以采取不同的方式。

您还可以使用 URL 编码 %26/%5e,其中存在 URLEncoder。

【讨论】:

    猜你喜欢
    • 2016-05-14
    • 2019-09-06
    • 1970-01-01
    • 2017-02-04
    • 1970-01-01
    • 1970-01-01
    • 2018-07-13
    相关资源
    最近更新 更多