2017-06-21 69 views
0

我想通過使用番石榴分離器來解析日誌文件。日誌文件看起來是這樣的:番石榴分配器與包含在字符串中的分割符字符的鍵值映射

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); 

我的問題是位置=「=/V1/TXXXX」字段具有「=」裏面的字符串和withKeyValueSeperator當前不能解析它。你能幫我解釋一下,我應該如何改變模式以正確地獲得所有的字段?

+0

如果你能完成這項工作,我會感到有些吃驚。你可能需要一些定製的,足夠聰明的來處理這個問題。 –

+0

@LouisWasserman,不需要使用花哨的東西...與常規的正則表達式是可行的 –

回答

0

異常java.lang.IllegalArgumentException: Chunk [location="= /v1/tXXXX"] is not a valid entry從您的代碼中拋出,因爲keyValueSeparator在塊中出現多次。您可以調整keyValueSeparator,以便只匹配與您的值模式相同的符號。例如: -

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

請注意,如果您有行內像key="key=value"這是不行的。

1

不知道番石榴分路器是如何工作的,但如果使用普通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工作演示h ERE:

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"` 
+0

番石榴的'Splitter.onPattern'是一個內部編譯'Pattern'的分離器。考慮一下改進後的'String.split(String)'。所以最初的問題類似於「我怎樣才能正確使用'String.split(String)'?正則表達式將是相同的。 –

+0

@OlivierGrégoire,很高興知道。那麼,我的答案不關注分裂模式,而是捕獲鍵/值模式。我猜OP想要捕獲這些值 –

+0

的確。更好的解決方案,不像OP所想象的那樣,沒有什麼;) –

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的麻煩是分裂的真正的目的只是爲了找到分隔符。這與普通的正則表達式的用法不同,在這種情況下你可以使用組來選擇你想要的東西;當你分裂時,你試圖匹配不要想要的東西,這會變得非常混亂。