【问题标题】:Modify regex to return all matched pairs修改正则表达式以返回所有匹配的对
【发布时间】:2021-08-08 09:11:55
【问题描述】:

我正在尝试匹配子字符串和键值对。例如,匹配字符串: "\"a,b,c\",,\"$a = test1, $1 = test2, $2 = test2\",3\n"一个

应该返回

a test1
1 test2
2 test2
a
b
c

在哪里

a test1
2 test2

是地图,a,b,cList 的项目。

以下代码:

import javafx.util.Pair;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class KeyValuesExtract {

    private Pair<List<String>, Map<String, String>> getKeyValues(final String line) {

        final Pattern quotesPattern = Pattern.compile("\"(.*?)\"");
        final Matcher quotesMatcher = quotesPattern.matcher(line);
        quotesMatcher.find();

        final List<String> vids = Arrays.asList(quotesMatcher.group(0).split(",")).stream().map(x ->
                x.replace("\"", "").trim()).collect(Collectors.toList());

        final Map<String, String> enumKeyValuePairs = new HashMap<>();

        final Pattern keyValuePattern = Pattern.compile("\"\\$([A-Za-z0-9]+)\\s=\\s(\\w+)(?:,\\s\\$([A-Za-z0-9]+)\\s=\\s(\\w+))*\"");
        final Matcher keyValueMatcher = keyValuePattern.matcher(line);
        while (keyValueMatcher.find()) {
            for (int i = 1; i <= keyValueMatcher.groupCount(); i++) {
                enumKeyValuePairs.put(keyValueMatcher.group(i), keyValueMatcher.group(++i));
            }
        }

        return new Pair(vids, enumKeyValuePairs);
    }

    public static void main(String args[]) {

        final String str = "\"a,b,c\",,\"$a = test1, $1 = test2, $2 = test2\",3\n";

        final KeyValuesExtract testCode = new KeyValuesExtract();
        final Pair<List<String>, Map<String, String>> pair = testCode.getKeyValues(str);

        pair.getValue().entrySet().forEach(entry -> {
            System.out.println(entry.getKey() + " " + entry.getValue());
        });

        pair.getKey().forEach(entry -> {
            System.out.println(entry);
        });

    }

}

打印:

a test1
2 test2
a
b
c

从一个较早的问题:Extracting key value pair from substring within string我已经更新了正则表达式

"\"\\$(\\d+)\\s=\\s(\\w+)(?:,\\s\\$(\\d+)\\s=\\s(\\w+))*\""

同时匹配数字和字符:

"\"\\$([A-Za-z0-9]+)\\s=\\s(\\w+)(?:,\\s\\$([A-Za-z0-9]+)\\s=\\s(\\w+))*\""

如何匹配所有 Map 值? :

a test1
1 test2
2 test2

【问题讨论】:

    标签: java regex


    【解决方案1】:

    您不能使用单个正则表达式来捕获多个组(请参阅Java regex: Repeating capturing groups)。

    真的,当您的输入变得更复杂时,您应该使用一些Lexer and Parser

    无论如何,你的问题可以通过迭代字符串两次来解决:

    @Getter
    @Setter
    @AllArgsConstructor
    @ToString
    static class Result {
        private List<String> items;
        private Map<String, String> map;
    }
    
    static Result parse(String str) {
        final Result result = new Result(new ArrayList<>(), new HashMap<>());
    
        final Pattern find1 = Pattern.compile("(\" *\\p{Alnum}+ *(?:, *\\p{Alnum}+ *)*\")");
        final Pattern extract1 = Pattern.compile("\\p{Alnum}+");
        final Pattern find2 = Pattern.compile("(\" *\\$\\p{Alnum}+ *= *\\p{Alnum}+ *(?:, *\\$\\p{Alnum}+ *= *\\p{Alnum}+ *)*\")");
        final Pattern extract2 = Pattern.compile("\\$(\\p{Alnum}+) *= *(\\p{Alnum}+)");
    
        final Matcher matcher1 = find1.matcher(str);
        while (matcher1.find()) {
            final Matcher extractor1 = extract1.matcher(matcher1.group(0));
            while(extractor1.find())
                result.items.add(extractor1.group(0));
        }
    
        final Matcher matcher2 = find2.matcher(str);
        while (matcher2.find()) {
            final Matcher extractor2 = extract2.matcher(matcher2.group(0));
            while(extractor2.find())
                result.map.put(extractor2.group(1), extractor2.group(2));
        }
    
        return result;
    }
    
    public static void main(String... args) {
    
        // your example
        System.out.println(parse("\"a,b,c\",,\"$a = test1, $1 = test2, $2 = test2\",3\n"));
    
        // more complex case
        System.out.println(parse("\"a,b,c\",,\"$a = test1, $1 = test2, $2 = test2\",3,\"foo\",bar,\" $er33=33re  \"\n"));
    
    }
    

    有输出

    Result(items=[a, b, c], map={a=test1, 1=test2, 2=test2})
    Result(items=[a, b, c, foo], map={a=test1, 1=test2, 2=test2, er33=33re})
    

    或者,如果您需要单独的列表和地图,您可以这样做

    private List<List<String>> items;
    private List<Map<String, String>> map;
    ...
    final Matcher matcher1 = find1.matcher(str);
    while (matcher1.find()) {
        final Matcher extractor1 = extract1.matcher(matcher1.group(0));
        final List<String> l = new ArrayList<>();
        while(extractor1.find())
            l.add(extractor1.group(0));
        result.items.add(l);
    }
    
    final Matcher matcher2 = find2.matcher(str);
    while (matcher2.find()) {
        final Matcher extractor2 = extract2.matcher(matcher2.group(0));
        final Map<String, String> m = new HashMap<>();
        while(extractor2.find())
            m.put(extractor2.group(1), extractor2.group(2));
        result.map.add(m);
    }
    

    有输出

    Result(items=[[a, b, c]], map=[{a=test1, 1=test2, 2=test2}])
    Result(items=[[a, b, c], [foo]], map=[{a=test1, 1=test2, 2=test2}, {er33=33re}])
    

    此外,如果您需要保留顺序,您也可以使用正则表达式(find1|find2),然后将其应用于extract1extract2

    【讨论】:

    • 感谢,如果 的值包含 '_' ,例如 'test2_5' : System.out.println(parse("\"a,b,c\",,\ "$a = test1, $1 = test2, $2 = test2_5\",3,\"foo\",bar,\" $er33=33re \"\n"));那么模式不匹配。我可以在解析之前替换所有 _,可以更新正则表达式来处理这个吗?
    • 当然,您可以将\\p{Alnum} 替换为您想匹配的任何表达式。例如。 [_\\p{Alnum}].
    【解决方案2】:

    你可以使用这个正则表达式:

    \$[a-zA-Z0-9]+\s*=\s*[a-zA-Z0-9]+[,]*
    

    我在https://regex101.com/r/xDX5v7/1 中尝试了您的示例:

    "\"a,b,c\",,\"$a = test1, $1 = test2, $2 = test2\",3\n"
    

    它返回了:

    $a = test1,
    $1 = test2,
    $2 = test2
    

    匹配正则表达式模式后,您可以使用Matcher 类中的.groupCount 方法获取计数,然后使用空格作为字符串分隔符来拆分匹配的字符串并将它们放入映射中。

    【讨论】:

      猜你喜欢
      • 2021-10-12
      • 2020-12-02
      • 2011-12-22
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 2012-12-22
      • 2011-09-20
      • 2022-01-14
      相关资源
      最近更新 更多