如果行包含超过特定数量的非数字值,请删除该行

时间:2016-09-15 01:03:26

标签: bash awk sed preprocessor gawk

我有一个大的(2GB)逗号分隔文本文件,其中包含来自传感器的一些数据。有时传感器关闭,没有数据。如果每行中有超过指定数量的No DataOffany non-numeric值,我想删除行;不包括标题。我只对从第3栏开始算起来感兴趣。例如:我的数据如下:

Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
1871XYZR/KB.RAT,Data from process value,Off,No Data, No Data
1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80
1871XKZR/KB.VAT,Data from process value,No Data,Off,2

这里的第一行是标题,我希望保持原样。但我希望从第3列开始删除任何列/字段中包含2个或2个以上No DataOff或任何non numeric字段的行。换句话说,行中有4个或五个文本字段。在该示例中,第3行和第6行有2个或多于2个No DataOff字段,我想删除它们。因此,我的首选输出是

Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80  

我可以针对特定情况使用循环执行此操作:

awk -F, '{ non_numeric=0;
  for(i=1;i<=NF;i++){
    if($i ~ // ) non_numeric++
  }
  if(non_numeric<2) print $0
}' testfile.txt

在此,我只考虑No DataOff。如何计算所有非数字字符串。如果我将if语句更改为

if($i ~ /[^0-9]/ ) non_numeric++

它不起作用并且没有输出。此外,因为我使用循环,我认为它会变慢。我们能以某种方式加快速度吗?任何Commandline解决方案都可以。

7 个答案:

答案 0 :(得分:2)

awk -F, '
    {   nonnum = 0;
        for (i = 3; i <= NF; i++) { 
            if ($i ~ /[^.0-9]/) {
                nonnum++;
                if(nonnum >= 2) { next; }
            }
        }
    } 1' infile > outfile

如果循环从未执行1以跳过当前行的剩余模式,则末尾的next将打印该行。

答案 1 :(得分:1)

使用静态字符串:

$ awk '(a=$0) && gsub(/No Data|Off/,"",a)<2' file

IE中。将当前记录$0复制到临时变量a,如果计数,则使用OffNo Data计算gsubprint的出现次数小于2.输出:

Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80

如果要匹配所有非数字字符串,请使用:

awk 'NR==1 || (a=$0) && gsub(/,[^\.,0-9]+/,"",a)<3' file

它输出第一个记录(NR==1)和少于三个非数字值的记录(第三个是,Data from process value)。

答案 2 :(得分:1)

您可以使用grep执行此操作:

grep -vP '((?<=,|^)(No Data|Off)(?=,|$).*){2,}' input

Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80

说明:(No Data|Off)No DataOff匹配。我们通过(?<=,|^)(?=,|$)将其包围起来;这些是零宽度的lookbehind和lookahead,与字符串的,或开头(或结尾)匹配。这可确保我们仅与整个字段匹配。由于我们希望多次匹配某个字段,因此我们将所有内容都放在量化的(...){2,}内,并添加.*来说明字段之间的内容。

答案 3 :(得分:1)

使用GNU awk,你可以使用这个好东西:

awk 'NF<2' FPAT='No Data' file

FPAT指定一种模式,用于描述文本行中的字段。它是GNU扩展。将其设置为静态字符串No Data,我们只需使用NF<2检查字段数。

答案 4 :(得分:1)

$ perl -F, -ane 'print if $. == 1 || (grep {!/\d/} @F[2..$#F]) < 2' ip.txt 
Tag, Description,2015/01/01,2015/01/01 00:01:00,2015/01/01 00:02:00, 2015/01/01 00:02:00
1827XYZR/KB.SAT,Data from Process Value,2.1,Off,2.7
1962XYMK/KB.GAT,Data from Process Value,No Data,5,3
1867XYST/KB.FAT,Data from process value,1.05,5.87,7.80
  • -F,,
  • 上拆分输入行
  • $. == 1如果行号为1,即打印标题
  • 如果第3列到第3列中的非数字字段数小于2,则
  • (grep {!/\d/} @F[2..$#F]) < 2打印。条件只是检查数字是否不存在

可以根据需要轻松更改要检查的列和检查次数。例如:@F[3..$#F]检查第4列以后,< 3检查少于3的非数字字段数

答案 5 :(得分:0)

懒惰方式:打印iff字段3-5包含至少一个数字字符:

awk -F, '$3$4$5 ~ "[0-9]"' data.csv

lazier方式(适用于您的示例数据):print iff row包含逗号后跟数字字符:

grep ',[0-9]' data.csv

答案 6 :(得分:0)

这可能适合你(GNU sed):

sed -r '/(.*No Data|.*Off){2}/d' file

使用交替删除包含2个或更多指定字符串的行。