2016-11-09 58 views
3

我有文件的格式如下提取字母数字值

m.dat -c16 -S32m 1.3768702014349401 s, rate: 3.2434134115834929 GB/s. 
m.dat -c16 -S64m 1.0852226612623781 s, rate: 4.115062684139847 GB/s. 
m.dat -c20 -S1m 3.8889309875667095 s, rate: 1.1483256688332133 GB/s. 
m.dat -c20 -S2m 16.622251618420705 s, rate: 0.26866151348562284 GB/s. 
m.dat -c20 -S4m 4.5505061785224825 s, rate: 0.98137637927430543 GB/s. 
m.dat -c20 -S8m 2.4563963813707232 s, rate: 1.8180124800752873 GB/s. 

,我想从中提取不同数字值。特别是,我得到类似此之后:

m.dat 20 4 4.5505061785224825 0.98137637927430543 

也就是说,我想提取数字没有字符文件中的每一行的第一个字段。

我可以很容易地得到各行的不同字段awk,但这些值也将包括-c-S,这是不感兴趣的。

awk '{print $1, $2, $3, $4}' file 

回答

3

这里的Perl的一个棘手位:

$ perl -lane '@fields=(@F[0], /(\d+(?:\.\d*)?|\d*\.\d+)/g); print "@fields"' file 
m.dat 16 32 1.3768702014349401 3.2434134115834929 
m.dat 16 64 1.0852226612623781 4.115062684139847 
m.dat 20 1 3.8889309875667095 1.1483256688332133 
m.dat 20 2 16.622251618420705 0.26866151348562284 
m.dat 20 4 4.5505061785224825 0.98137637927430543 
m.dat 20 8 2.4563963813707232 1.8180124800752873 

总得来看,应要求解释。

+0

管道输出到'column -t'使其变得很漂亮 –

+0

请允许我解释:'-a'将每个输入行以空格分隔为一个字段数组,这使得第一个字段的访问权限为@F [0] 。然后,将第1个字段与行中的数字标记数组结合起来,使用正则表达式构造输出数组“@ fields”。通过将双引号内的'@ fields'传递给'print',数组元素被转换为元素之间具有单个空格的字符串。正则表达式'/ .../g'作为一个整体隐式应用于输入行,并将所有('g')匹配返回为一个数组。至少在样本输入中,更简单的'/ \ d +(?:\。\ d +)?/ g'也可以工作。 – mklement0

+1

谢谢@ mklement0。更复杂的正则表达式捕获没有整数部分的分数。如果这些没有出现在输入中,请简化。 –

1
awk '{print $1,substr($2,3),substr(substr($3,3),1,length(substr($3,3))-1),$4,$7}' file 

输出:

 
m.dat 16 32 1.3768702014349401 3.2434134115834929 
m.dat 16 64 1.0852226612623781 4.115062684139847 
m.dat 20 1 3.8889309875667095 1.1483256688332133 
m.dat 20 2 16.622251618420705 0.26866151348562284 
m.dat 20 4 4.5505061785224825 0.98137637927430543 
m.dat 20 8 2.4563963813707232 1.8180124800752873 
0

我建议分拆所有非数值(仍然需要改进处理.):

$ awk '{ for (i=2;i<=NF;i++) { gsub("[^0-9.]*","",$i); } gsub("\\s+", " "); $NF=""; print ; }' data.dat 
m.dat 16 32 1.3768702014349401 3.2434134115834929 
m.dat 16 64 1.0852226612623781 4.115062684139847 
m.dat 20 1 3.8889309875667095 1.1483256688332133 
m.dat 20 2 16.622251618420705 0.26866151348562284 
m.dat 20 4 4.5505061785224825 0.98137637927430543 
m.dat 20 8 2.4563963813707232 1.8180124800752873 

编辑:我想通我有额外的一点(当包括.在正则表达式中)是由于第一个字段被替换。我相应地修改了答案。

+2

好主意剥离非数字,但你只需要做第2列和第3列(这避免了'.'问题)。你也可以简化打印:'awk'{for(i = 2; i <= 3; i ++)gsub(“[^ 0-9]”,“”,$ i);打印$ 1,$ 2,$ 3,$ 4,$ 7}'' – jas

+0

@jas:解决了问题!我不明白为什么当我的正则表达式是'[^ 0-9。'''时,我又多了一个'.';我现在有! – Aif

2

另一个perl溶液

$ perl -lne 'print join "\t", /^\s*\K\S+|\d+\.\d+|\d+/g' file 
m.dat 16 32 1.3768702014349401 3.2434134115834929 
m.dat 16 64 1.0852226612623781 4.115062684139847 
m.dat 20 1 3.8889309875667095 1.1483256688332133 
m.dat 20 2 16.622251618420705 0.26866151348562284 
m.dat 20 4 4.5505061785224825 0.98137637927430543 
m.dat 20 8 2.4563963813707232 1.8180124800752873 
  • join "\t"使用标签作为输出分隔符,如果需要的话
  • /^\s*\K\S+|\d+\.\d+|\d+/g正则表达式定义的文本以从开头提取
    • ^\s*\K\S+它更改为任何其他字符串序列行,不包括可选空间,得到非空格字符 - 得到我们行标签m.dat在这种情况下与至少一个数字前/后的.
    • \d+顺序是重要的,提取分数第一,然后得到的数字非小数序列
    • \d+\.\d+提取小数
1

我建议的pastecutawktr务实的组合:

$ paste -d' ' <(cut -d' ' -f1 file) <(awk '{print $2, $3, $4, $7}' file | tr -dC '0-9. \n') 

m.dat 16 32 1.3768702014349401 3.2434134115834929 
m.dat 16 64 1.0852226612623781 4.115062684139847 
m.dat 20 1 3.8889309875667095 1.1483256688332133 
m.dat 20 2 16.622251618420705 0.26866151348562284 
m.dat 20 4 4.5505061785224825 0.98137637927430543 
m.dat 20 8 2.4563963813707232 1.8180124800752873 

这不是最快的方法,但它很容易理解:

  • cut -d' ' -f1 file从文件file输出第一空间分隔的字段。

  • awk '{print $2, $3, $4, $7 }'输出file的空格分隔的字段2,3,4和7,用输出上的单个空格分隔。

    • tr -dC '0-9 \n'去除(-d)除(-C)数字,空格,和从awk的输出换行的所有字符。
  • paste -d' ' <(...) <(...)合并来自cut命令的输出和awk管道,由单个空格分隔相应的线,使用两个process substitutions

1

用户定义的函数awk中不使用的时候,但在这种情况下,它们允许一种简单的,可扩展的解决方案:

$ awk ' 
function strip(val) { gsub("[^0-9.]", "", val); return val } # keep only digits and "." 
{ print $1, strip($2), strip($3), $4, $7 } 
' file 

m.dat 16 32 1.3768702014349401 3.2434134115834929 
m.dat 16 64 1.0852226612623781 4.115062684139847 
m.dat 20 1 3.8889309875667095 1.1483256688332133 
m.dat 20 2 16.622251618420705 0.26866151348562284 
m.dat 20 4 4.5505061785224825 0.98137637927430543 
m.dat 20 8 2.4563963813707232 1.8180124800752873 

或者,GNUawkgensub()功能允许一个相对简洁的解决方案:

awk '{ print $1, gensub("[^0-9]+", "", "g", $2), gensub("[^0-9]+", "", "g", $3), $4, $7 }' 
1

我想获得最简单的想法和最少的代码。如果你想要的是数字数据,使用非数值作为您的分隔符:

$ awk -F '[^0-9.-]+' '{split($0, a,/+/); print a[2], $4, $6, $7, $8}' dat 
m.dat 16 32 1.3768702014349401 3.2434134115834929 
m.dat 16 64 1.0852226612623781 4.115062684139847 
m.dat 20 1 3.8889309875667095 1.1483256688332133 
m.dat 20 2 16.622251618420705 0.26866151348562284 
m.dat 20 4 4.5505061785224825 0.98137637927430543 
m.dat 20 8 2.4563963813707232 1.8180124800752873 

要钉在第一场,拆分记录两个方面。

+1

@_James K. Lowden,如果我也想要第一个领域? – Manolete