Guava Splitter到键值映射,包含字符串中的splitter字符

时间:2017-06-21 18:28:19

标签: java regex guava

我正在尝试使用Guava拆分器解析日志文件。日志文件如下所示:

appName=XXX clientIp=X.X.X timestamp="2017-06-05T13:22:12-07:00" request="POST /forward HTTP/1.1" statusCode=204 bytesOut=1167 totalTime=0.062 bytesIn=1289 sourceHost=XXXX connId=49936598 connReqs=9 upInstance=XXX:104:XXX-XXX:8664:17F34 upConnectSec=0.052 upAddr="XX.XX.XX:123" upHost="vcv08it-cvcv2801:8464" upHdrTimeSec=0.058 upRespTimeSec=0.058 pid=32561  upStatusCode=204 message="Access Log" corrKey=GMIFCDIKRZR2T4VZQXJA2IT6 upCached=- length=0 partition=XXX location="= /v1/tXXXX" xff="XX.XX.XX.XX" referer="-" user-agent="Apache-HttpAsyncClient/4.1.1 (Java/1.8.0_131)\" rateLimitCurrentValues="--" rateLimitTimeMs=\"-:-"

我用这段代码来解析它:

Map<String, String> parserMap;
parserMap = Splitter.onPattern("\\s(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)")
.omitEmptyStrings()
.withKeyValueSeparator(Splitter.onPattern("="))
.split(line);

我的问题是location =“= / v1 / tXXXX”字段,字符串中有'=',当前withKeyValueSeperator无法解析它。你能帮我解决一下如何更改模式以正确获取所有字段吗?

4 个答案:

答案 0 :(得分:1)

不确定Guava分割器的工作原理,但如果您使用常规的PatternMatcher类,则可以使用以下正则表达式来捕获您的键和值:

([\w-]+?)=(".*?"|\S+)

<强> Regex demo

Java代码

String text = "your string";
Pattern pattern = Pattern.compile("([\\w-]+?)=(\".*?\"|\\S+)");
Matcher m = pattern.matcher(text);
Map<String, String> parserMap = new HashMap<>();

while (m.find()) {
    String key = m.group(1);
    String value = m.group(2);
    parserMap.put(key, value);
}

在这里准备了一个IdeOne java工作演示:

https://ideone.com/y8b8di

您可以在下面看到匹配信息的样本

Match 1
    Group 1.    0-7     `appName`
    Group 2.    8-11    `XXX`

Match 2
    Group 1.    12-20   `clientIp`
    Group 2.    21-26   `X.X.X`

Match 3
    Group 1.    27-36   `timestamp`
    Group 2.    37-64   `"2017-06-05T13:22:12-07:00"`

Match 4
    Group 1.    65-72   `request`
    Group 2.    73-97   `"POST /forward HTTP/1.1"`

答案 1 :(得分:0)

我不确定单个正则表达式可以解决问题,但可以相对轻松地制作出可行的解决方案:

parserMap = Splitter.onPattern("\\s(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)")
    .omitEmptyStrings()
    .splitToList(line)
    .stream()
    .collect(Collectors.toMap(
        s -> s.split("=", 2)[0],  // the first part of split gets the key
        s -> s.split("=", 2)[1]   // everything else is the value
    )
);

尝试对split使用正则表达式的麻烦在于,split的固有目标只是找到分隔符。这与正常的正则表达式使用不同,您可以使用组来选择所需的内容;当你分手时,你正试图匹配你想要的东西,这真的很混乱。

答案 2 :(得分:0)

从代码中抛出异常java.lang.IllegalArgumentException: Chunk [location="= /v1/tXXXX"] is not a valid entry,因为keyValueSeparator在块中不止一次出现。您可以调整keyValueSeparator,以便只匹配您的值模式后面的等号。 e.g:

final String keyPattern = "\\S+";
final String valuePattern = "(\\S+|\"[^\"]*\")";
parserMap = Splitter.onPattern("\\s(?=" + keyPattern + "=" + valuePattern + ")")
        .omitEmptyStrings()
        .withKeyValueSeparator(Splitter.onPattern("=(?=" + valuePattern + ")"))
        .split(line);

请注意,如果您的行中有key="key=value"之类的内容,则无法使用此功能。

答案 3 :(得分:0)

limit拆分器上使用withKeyValueSeparator

Splitter.onPattern("\\s(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)")
    .omitEmptyStrings()
    .withKeyValueSeparator(Splitter.on("=").limit(2).trimResults())
    .split(line);

请参阅GitHub问题:https://github.com/google/guava/issues/1900