我有一个文本文件:
[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
我需要提取某些值:日期、城市名称、语言、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
或者如果可以编辑逗号就更好了,因为某个 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
答案1
由于这些似乎本质上是 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)
然后
$ ./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
注意:该parse_qs
方法返回一个列表字典,即它允许每个查询键有多个值:'-'.join(q[key])
名义上将每个值列表变成连字符分隔的字符串,但是在这种情况下我们期望每个键只有一个值。
答案2
由于顺序可能会改变,因此需要编写一些脚本。以下是 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)
将其另存为foo.pl
,使其可执行(chmod +x foo.pl
)并像这样运行它:
./foo.pl file.txt
您还可以将其压缩成“一行”:
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
解释
意思-n
是“逐行读取输入文件并将脚本应用到每一行。-l
在每个调用中添加一个换行符print
并从每个输入行中删除换行符。
在每种情况下,我们都使用正则表达式来查找目标字符串,并在找到匹配项时将其分配给变量。第一个正则表达式\[(.+?)\]
匹配 a[
和第一个之间的任何内容]
。 周围的括号.+
是捕获组我们将捕获的内容称为$1
。因此,$time
将是 里面的内容[ ]
。
其他正则表达式遵循相同的思想。\b
表示“非单词字符”,并确保y=
不会匹配city
等。(&|$)
表示任何一个A&
或者行尾($
),用于捕获行末的模式。
最后,我们join
用逗号分隔这些内容并打印出来。
答案3
由于顺序可能会改变,这稍微困难一些,但sed
可以处理:
s/\[(.*)\](\/segment\?)(.*)/\3,\1/ #Match text between [], append to end of line and remove /segmennt?
s/city=([^&,]*)[&,](.*)/\2,\1/ #Match city= followed by any character
s/language=([^&,]*)[&,](.*)/\2,\1/ #except & and , which are the separators and append to end of line
s/x=([^&,]*)[&,](.*)/\2,\1/
s/\by=([^&,]*)[&,](.*)/\2,\1/ #Avoid matching city again by making sure y is at a word boundary
s/z=([^&,]*)[&,](.*)/\2,\1/
运行方式:sed -rnf scriptfile inputfile