现在有一行行的文本数据( DNS 日志),是非结构化的,但是也有结构,比如
ad=0 cd=0 rcode=ok qdcount=1 ancount=0 nscount=0 arcount=1 src=232.190.252.222 dst=45.80.170.1 这种
如何提取出指定字段的行呢? 比如过滤出 rcode=ok 的字段行,或者如何将这些数据变成结构化的,比如对应到一个 表格里,如果有的话 就为 “=” 后面的值,如果没有就为空
![]() | 1 matsuz 2019-04-07 16:29:50 +08:00 正则表达式 |
2 lastpass 2019-04-07 16:30:57 +08:00 via Android py 不知道。 antlr 倒是非常简单。 |
![]() | 3 anonymoustian OP @matsuz 谢谢老哥,除了正则还有什么方便的库么 |
![]() | 4 zjsxwc 2019-04-07 16:35:11 +08:00 C 语言可以 sscanf Python 可以 正则 |
![]() | 5 recall704 2019-04-07 16:37:10 +08:00 如果只是这种,不是可以按空格分割吗? |
![]() | 6 anonymoustian OP @recall704 是可以按照空格分割,但是比如说我想提 src=232.190.252.222 后面的 ip 地址,我在想有没有这种方法可以直接 getline('src') 这种 好像也不麻烦哈~ 而且字段不一定存在 |
![]() | 7 matsuz 2019-04-07 16:44:34 +08:00 ![]() @lastpass 你这种结构的最合适的就是正则 ```python import re match = re.match( r'ad=([^ ]+)\W+cd=([^ ]+)\W+rcode=([^ ]+)\W+qdcount=([^ ]+)\W+ancount=([^ ]+)\W+nscount=([^ ]+)\W+arcount=([^ ]+)\W+src=([^ ]+)\W+dst=([^ ]+)', 'ad=0 cd=0 rcode=ok qdcount=1 ancount=0 nscount=0 arcount=1 src=232.190.252.222 dst=45.80.170.1', ) for i in range(1, 10): print(match.group(i)) ``` |
![]() | 8 e2c 2019-04-07 16:47:10 +08:00 这么简单的文本,可以自己解析了 |
![]() | 9 CEBBCAT 2019-04-07 16:58:17 +08:00 via Android ![]() 为什么我想到的是两个 spilt ?我思维太初级了? |
![]() | 10 Kilerd 2019-04-07 17:07:15 +08:00 via iPhone 这么简单的处理都写不出来吗? |
![]() | 11 Kylin30 2019-04-07 17:17:28 +08:00 要我这外行来搞的话,那就直接先 split 空格然后 split 等号,2 个循环就变成了一个 dict,之后处理就很随便了。每行格式固定的话,不计较什么性能代码好看的还是非常简单的。 希望有大佬来说说,看看有什么高级搞法学习一下。 |
![]() | 12 westoy 2019-04-07 17:19:07 +08:00 from collections import defaultdict import re ini = defaultdict(lambda: None, re.findall('(?:^|\s+)([^\=]+)\=([\S]+)','''ad=0 cd=0 rcode=ok qdcount=1 ancount=0 nscount=0 arcount=1 src=232.190.252.222 dst=45.80.170.1''')) print(ini['helloworld'], ini['src']) 如果结构复杂一点, 比如 xx=xx xx="yy" xx='yy' xx='zz abc'这种, 用 pyparsing 更方便一点 |
13 Levox 2019-04-07 18:24:06 +08:00 ![]() ``` python >>> s = 'ad=0 cd=0 rcode=ok qdcount=1 ancount=0 nscount=0 arcount=1 src=232.190.252.222 dst=45.80.170.1' >>> >>> from urllib import parse >>> d = parse.parse_qs(s.replace(' ', '&')) >>> >>> d['src'] ['232.190.252.222'] >>> ``` |
![]() | 15 911speedstar 2019-04-07 22:19:54 +08:00 非结构化用正则,结构化用 xpath。。。 |
16 mintist 2019-04-07 22:43:37 +08:00 先 split,然后用个 json 来结构化存储 |
17 lastpas 2019-04-08 00:34:47 +08:00 via Android @matsuz 不不不,正则并没有 antlr 强大。而且正则那又臭又长辣眼睛的代码无论是写还是维护都是灾难。所以,我目前项目里处理这种字符串都是使用的 antlr。 |
18 jiaqiangbandongg 2019-04-08 04:04:34 +08:00 s = "ad=0 cd=0 rcode=ok qdcount=1 ancount=0 nscount=0 arcount=1 src=232.190.252.222 dst=45.80.170.1" data = dict(map(lambda x:x.split("="),str.split())) print(data["rcode"]) |
19 jiaqiangbandongg 2019-04-08 04:05:23 +08:00 一行处理就够了 |
![]() | 20 dartabe 2019-04-08 06:03:45 +08:00 split 之后存个字典 再查找键值就行了 求大佬评价一下。。。 |
![]() | 21 LokiSharp 2019-04-08 09:09:56 +08:00 用 pyparsing 定义语法之后解析 |
22 yuhr123 2019-04-08 09:14:12 +08:00 via iPhone split 生成以空格分割的列表,迭代列表查找=号所在位置,用这个位置坐切片取封号前面的字符串,和你需要的字符串做 if 判断。 |
![]() | 23 niknik 2019-04-08 09:56:16 +08:00 以空格 split 啊,然后转 json |
![]() | 24 shuax 2019-04-08 10:09:33 +08:00 直接两次 split 多简单啊 |
25 capric 2019-04-08 10:21:08 +08:00 src = 'ad=0 cd=0 rcode=ok qdcount=1 ancount=0 nscount=0 arcount=1 src=232.190.252.222 dst=45.80.170.1' dst = dict(parts.split('=') for parts in src.split()) print(dst.get('ad')) |
![]() | 26 thautwarm 2019-04-08 11:00:18 +08:00 via Android 除开正则还有各种文法库。 比如我写的一个库的话, import rbnf.zero as ze ze_exp = ze.compile(""" import std.common.[Number Name Space] Pair ::= keys=Name '::=' values=(~Space)* -> (k.value, "".join(x.value for x in v)) Row ::= pairs=Pair+ -> dict(pairs) Logs ::= (rows<<Row '\n'+)+ -> rows """ ze_exp.match("你的日志字符串").result |
![]() | 27 www5070504 2019-04-08 11:24:11 +08:00 两个 split 加上 字典推导式 用默认字典可以避免键可能不存在的情况 |
28 btv2bt 2019-04-08 11:49:52 +08:00 应该可以直接转 dict 吧 |
![]() | 29 z67nnciQnb7r8bLf 2019-04-08 13:28:29 +08:00 @lastpass 最近在搞 antlr,有啥好的资料看看吗 |
![]() | 30 TheBestSivir 2019-04-08 15:39:20 +08:00 看你的场景咯。你对性能有需求么?有的话还是不要正则了。另外,这种简单场景正则真的有利于维护么??? |
31 hakono 2019-04-08 16:11:46 +08:00 via Android 噗,其实说真的,如果要搜索的字符串格式固定的话,首先想到的不应该是转为 dict,而是 for line in log_lines: if "rcode=ok" in line: xxxxxxxxxxxxxxxxxxx |
![]() | 32 caryqy 2019-04-08 16:15:38 +08:00 cat dns.log | awk -F ' ' '{print $3}' >> res.log |
![]() | 33 caryqy 2019-04-08 16:19:59 +08:00 cat dns.log | awk -F ' ' '{if ($3=="rcode=ok") {print $3} else {print "rcode=error"}}' |
![]() | 34 omph 2019-04-08 16:25:38 +08:00 http://pypi.python.org/pypi/parse 类似于 C 语言的 sscanf 字符串扫描 |
![]() | 35 bumz 2019-04-08 17:20:15 +08:00 用 BNF 写一下文法?然后用 BNF Parser Generator 生成一下相应的解析器 (其实这种正则就行,逃 (再复杂直接用递归下降写就好 |
![]() | 36 metamask 2019-04-08 17:48:43 +08:00 cOntent= "ad=0 cd=0 rcode=ok qdcount=1 ancount=0 nscount=0 arcount=1 src=232.190.252.222 dst=45.80.170.1 test test2" 1 直接字符串判断 rocde_is_ok = "rcode=ok" rocde_is_ok in content 2 pairs = content.split(' ') content_dict = {} for name_value in pairs: nv = name_value.split('=', 1) if len(nv) != 2: nv.append("") key, value = nv content_dict[key] = value |
37 saulshao 2019-04-08 23:46:08 +08:00 上来就先 Split,然后写个函数分析 split 之后的结果,决定要不要将每个=加入最终结果。 我不建议用正则,你这个规律性太强了...... |
38 lastpass 2019-04-09 00:54:40 +08:00 via Android @C0dEr 我看的就是 The Definitive ANTLR 4 Reference 这本书。简单易懂\(////)\。 |
39 wonderay 2019-04-09 13:27:17 +08:00 |
![]() | 40 shawndev 2019-04-11 16:52:54 +08:00 空格换成换行就是 ini 格式了。 |
![]() | 41 shawndev 2019-04-11 16:53:43 +08:00 空格换成&,前面随便添加一个 baseUrl,用现成的 url 解析库解析。 |