从文本中提取某些值

vay*_*015 4 text-processing

我有一个文本文件:

[31/May/2016:11:58:29-0500]/segment?city=london&language=en&x=12345&y=6789&z=1
[31/May/2016:11:59:15-0500]/segment?language=en&city=madrid&x=4589.4583&y=4865.5465&z=3
[31/May/2016:12:05:13-0500]/segment?city=london&language=en&x=12345&y=6789&z=1
[31/May/2016:12:15:13-0500]/segment?city=london&language=en&x=12345&y=6789&z=1
[31/May/2016:12:26:53-0500]/segment?language=en&city=newyork&x=45724.75575&y=424424.77474&z=3
Run Code Online (Sandbox Code Playgroud)

我需要按顺序提取某些值:日期、城市名称、语言、x、y、z。请注意,在某些行中有不同的顺序,并且在未来的文件顺序中也可能会有所不同。

输出应如下所示:

31/May/2016:11:58:29-0500 london en 12345 6789 1
31/May/2016:11:59:15-0500 madrid en 589.4583 4865.5465 3
31/May/2016:12:05:13-0500 london en 12345 6789 1
31/May/2016:12:15:13-0500 london en 12345 6789 1
31/May/2016:12:26:53-0500 newyork en 45724.75575 424424.77474 3
Run Code Online (Sandbox Code Playgroud)

如果可以编辑逗号甚至更好,因为某个 csv 标准输出看起来像这样:

31/May/2016:11:58:29-0500,london,en,12345,6789,1
31/May/2016:11:59:15-0500,madrid,en,589.4583,4865.5465,3
31/May/2016:12:05:13-0500,london,en,12345,6789,1
31/May/2016:12:15:13-0500,london,en,12345,6789,1
31/May/2016:12:26:53-0500,newyork,en,45724.75575,424424.77474,3
Run Code Online (Sandbox Code Playgroud)

ter*_*don 7

由于顺序可以更改,因此需要编写一些脚本。这是一个 Perl 版本:

#!/usr/bin/perl -nl

my $time = $1 if /\[(.+?)\]/; 
my $city = $1 if /city=(.*?)(&|$)/;
my $lang = $1 if /language=(.*?)(&|$)/;
my $x = $1 if /\bx=(.*?)(&|$)/; 
my $y = $1 if /\by=(.*?)(&|$)/; 
my $z = $1 if /\bz=(.*?)(&|$)/;
print join ",", ($time, $city, $lang, $x, $y, $z)
Run Code Online (Sandbox Code Playgroud)

将其另存为foo.pl,使其可执行 ( chmod +x foo.pl) 并像这样运行它:

./foo.pl file.txt
Run Code Online (Sandbox Code Playgroud)

你也可以把它挤成一个“单线”:

perl -lne '$t=$1if/\[(.+?)\]/;$c=$1if/city=(.*?)(&|$)/;$l=$1if/language=(.*?)(&|$)/;$x=$1if/\bx=(.*?)(&|$)/;$y=$1if/\by=(.*?)(&|$)/;$z=$1if/\bz=(.*?)(&|$)/;print join",",($t,$c,$l,$x,$y,$z)' file
Run Code Online (Sandbox Code Playgroud)

解释

-n意味着“逐行读取输入文件并将脚本应用于每一行。在-l每个print调用中添加一个换行符并从每个输入行中去除换行符。

在每种情况下,我们都使用正则表达式来查找目标字符串,如果找到匹配项,则将其分配给一个变量。第一个正则表达式\[(.+?)\]匹配 a[和第一个之间的任何内容]。周围的括号.+捕获组,让我们将捕获的内容称为$1. 因此,$time将是[ ].

其他正则表达式遵循相同的想法。的\b意思是一个“非文字字符”,并确保y=将不匹配city(&|$)装置或者一个& 该线的端部($),并需要用于在该行的最末端捕获模式。

最后,我们join用逗号将它们打印出来。


ste*_*ver 7

由于这些似乎本质上是作为 URL 查询结构化的,因此您可能需要考虑使用专用的查询解析器 - 例如来自 pythonurlparse模块的解析器。例如

#!/usr/bin/python2

import sys,re
from urlparse import urlparse,parse_qs

keys = ['city', 'language', 'x', 'y', 'z']

with open(sys.argv[1],'r') as f:
        for line in f:
                u = urlparse(line.strip('\n'))
                q = parse_qs(u.query)

                # extract the strings we want from the dict-of-lists
                values = ','.join(['-'.join(q[key]) for key in keys])

                # extract the timestamp portion of the path (between `[` and `]`)
                m = re.search('(?<=\[).*?(?=\])', u.path)
                ts = m.group(0)

                # print as a comma-separated list
                print '{},{}'.format(ts, values)
Run Code Online (Sandbox Code Playgroud)

然后

$ ./queryparse.py queries.txt
31/May/2016:11:58:29-0500,london,en,12345,6789,1
31/May/2016:11:59:15-0500,madrid,en,4589.4583,4865.5465,3
31/May/2016:12:05:13-0500,london,en,12345,6789,1
31/May/2016:12:15:13-0500,london,en,12345,6789,1
31/May/2016:12:26:53-0500,newyork,en,45724.75575,424424.77474,3
Run Code Online (Sandbox Code Playgroud)

注意:该parse_qs方法返回一个列表字典,即它允许每个查询键有多个值:'-'.join(q[key])理论上将每个值列表转换为一个连字符分隔的字符串,但是在这种情况下,我们期望每个键只有一个值。