You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中从日志字符串提取KEY=VALUE键值对的正则方案求助

从带转义引号的日志字符串中提取KEY=VALUE键值对到Map

问题背景

有如下格式的日志字符串(注意其中的双引号是转义的""形式):

String s0 = "DC696,\"/xi/ajax/remoting/call/plaincall/adhocReportBuilderControllerProxy.getRortList.dwr\",\"2222-11-10 08:32:22,351               PLV=REQ CIP=9.9.9.7 CMID=syairp CMN=\"\"Dub Airport Corporation Limited\"\" SN=sfv4_APM180885. DPN=dbPool66HFT01 UID=3862D04108 UN=91F6025D47F01D IUID=1931 LOC=en_GB EID=\"\"EVENT-UNKNOWN-UNKNOWN-ob55abe0118-201110083217-396080\"\" AGN=\"\"[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35]\"\" RID=REQ-[7274545]  MTD=POST URL=\"\"/xi/ajax/remoting/call/plaincall/adhocRrtBuilderCoollerProxy.getRtList.dwr\"\" RQT=2835 MID=ADIN PID=ADMIN PQ=ADIN_PAGE SUB=0 MEM=2331036 CPU=2410 UCPU=2300 SCPU=110 FRE=10 FWR=0 NRE=2281 NWR=218 SQLC=43 SQLT=142 RPS=200 SID=60826A3FAB005A8A9B930177C5******.pc6bc1029 GID=e262dde6d0e040070b58afd4c8 HSID=ddc665538db779508d3213c0bb63bcb1c49fe8236d5f0884ae975915728e61 CSL=CRITICAL CCON=0 CSUP=0 CLOC=0 CEXT=0 CREM=0 STK={\"\"n\"\":\"\"/xi/ajax/remoting/call/plaincall/adhocReportBuilderControllerProxy.getrtList.dwr\"\",\"\"i\"\":1,\"\"t\"\":2835,\"\"slft\"\":2679,\"\"sub\"\":[{\"\"n\"\":\"\"SQL:select * from sfv4_HOUA180885.REPORT_DEF WHERE REPORT_DEF_ID IN (SELECT REPORT_DEF_ID FROM sfv4_HA80885.REPORT_DTASET WHERE REPORT_ID=?) AND DELETED=? ORDER BY REPORT_DEF_ID asc NULLS LAST\"\",\"\"i\"\":17,\"\"t\"\":40,\"\"slft\"\":40,\"\"st\"\":337,\"\"m\"\":220958,\"\"nr\"\":154,\"\"rt\"\":0,\"\"rn\"\":22,\"\"fs\"\":0}]}   \"},\"2022-11-09T21:32:22.351+0000\",p66cf1029,\"dc606_ss_application\",1,\"/app/tomcat/logs/pef.log\",\"perf_log_yxx\",swsskix13";

需要从中提取PLV=REQ、CIP=9.9.9.7、CMN="Dub Airport Corporation Limited"、STK={...}这类KEY=VALUE键值对,转换为Map<String,String>对象。之前尝试用split方法无法正常工作:

String[] str1= str.split("\\s(?=(([^\\"]*\\"))*[^\\"]*$)\\s*");
System.out.println("Value of split string is "+ Arrays.toString(str1));

问题分析

原方案用split的问题在于:

  • 日志中的值可能包含空格(比如AGN对应的浏览器UA字符串),且存在转义双引号""和嵌套大括号(比如STK的JSON结构),单纯按空格分割会破坏完整的键值对。
  • 正则表达式的逻辑没有覆盖大括号嵌套的场景,无法正确识别STK这类包含复杂结构的值边界。

解决方案

推荐使用正则匹配的方式,直接定位所有符合KEY=VALUE格式的片段,其中VALUE可能是:

  1. 普通无空格字符串
  2. 被转义双引号""包裹的内容
  3. 被大括号{}包裹的JSON结构(支持嵌套)

实现代码

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

public class LogParser {
    public static void main(String[] args) {
        String s0 = "DC696,\"/xi/ajax/remoting/call/plaincall/adhocReportBuilderControllerProxy.getRortList.dwr\",\"2222-11-10 08:32:22,351               PLV=REQ CIP=9.9.9.7 CMID=syairp CMN=\"\"Dub Airport Corporation Limited\"\" SN=sfv4_APM180885. DPN=dbPool66HFT01 UID=3862D04108 UN=91F6025D47F01D IUID=1931 LOC=en_GB EID=\"\"EVENT-UNKNOWN-UNKNOWN-ob55abe0118-201110083217-396080\"\" AGN=\"\"[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edg/107.0.1418.35]\"\" RID=REQ-[7274545]  MTD=POST URL=\"\"/xi/ajax/remoting/call/plaincall/adhocRrtBuilderCoollerProxy.getRtList.dwr\"\" RQT=2835 MID=ADIN PID=ADMIN PQ=ADIN_PAGE SUB=0 MEM=2331036 CPU=2410 UCPU=2300 SCPU=110 FRE=10 FWR=0 NRE=2281 NWR=218 SQLC=43 SQLT=142 RPS=200 SID=60826A3FAB005A8A9B930177C5******.pc6bc1029 GID=e262dde6d0e040070b58afd4c8 HSID=ddc665538db779508d3213c0bb63bcb1c49fe8236d5f0884ae975915728e61 CSL=CRITICAL CCON=0 CSUP=0 CLOC=0 CEXT=0 CREM=0 STK={\"\"n\"\":\"\"/xi/ajax/remoting/call/plaincall/adhocReportBuilderControllerProxy.getrtList.dwr\"\",\"\"i\"\":1,\"\"t\"\":2835,\"\"slft\"\":2679,\"\"sub\"\":[{\"\"n\"\":\"\"SQL:select * from sfv4_HOUA180885.REPORT_DEF WHERE REPORT_DEF_ID IN (SELECT REPORT_DEF_ID FROM sfv4_HA80885.REPORT_DTASET WHERE REPORT_ID=?) AND DELETED=? ORDER BY REPORT_DEF_ID asc NULLS LAST\"\",\"\"i\"\":17,\"\"t\"\":40,\"\"slft\"\":40,\"\"st\"\":337,\"\"m\"\":220958,\"\"nr\"\":154,\"\"rt\"\":0,\"\"rn\"\":22,\"\"fs\"\":0}]}   \"},\"2022-11-09T21:32:22.351+0000\",p66cf1029,\"dc606_ss_application\",1,\"/app/tomcat/logs/pef.log\",\"perf_log_yxx\",swsskix13";
        
        // 提取日志中包含键值对的核心部分(第三个引号内的内容)
        String coreLog = s0.split("\",\"")[2];
        Map<String, String> keyValueMap = new HashMap<>();
        
        // 正则表达式:匹配KEY=VALUE,覆盖三种VALUE类型
        String regex = "(\\w+)=((?:""[^""]*"")|(?:\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\})|(?:[^\\s]+))";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(coreLog);
        
        while (matcher.find()) {
            String key = matcher.group(1);
            String value = matcher.group(2);
            // 处理转义双引号:把""替换为"
            value = value.replace("\"\"", "\"");
            // 去掉包裹内容的首尾引号(如果存在)
            if (value.startsWith("\"") && value.endsWith("\"")) {
                value = value.substring(1, value.length() - 1);
            }
            keyValueMap.put(key, value);
        }
        
        // 输出测试
        keyValueMap.forEach((k, v) -> System.out.println(k + " = " + v));
    }
}

正则表达式说明

(\\w+)=((?:""[^""]*"")|(?:\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\})|(?:[^\\s]+))

  • (\\w+):匹配键名,由字母数字下划线组成
  • =:匹配等号分隔符
  • 括号内的三个分支分别匹配三种值类型:
    1. (?:""[^""]*""):匹配被转义双引号""包裹的内容
    2. (?:\\{[^{}]*(?:\\{[^{}]*\\}[^{}]*)*\\}):匹配被大括号包裹的嵌套结构(支持一层嵌套)
    3. (?:[^\\s]+):匹配无空格的普通字符串

额外处理说明

  1. 首先通过split("\",\"")提取日志中包含键值对的核心片段,避免处理前后无关的字段。
  2. 把转义的""替换为普通双引号",并去掉包裹内容的首尾引号,让值更符合正常格式。
  3. 对于STK这类JSON结构,保留原始大括号格式,后续可以根据需要进一步解析为JSON对象。

内容的提问来源于stack exchange,提问作者Alferd Nobel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 12:55:14