【问题标题】:extract optional field values from lines从行中提取可选字段值
【发布时间】:2012-02-09 13:27:43
【问题描述】:

我有单独行形式的文本,其中每行都有类似 CSV 的格式:

SOME BUNCH OF TEXT, FIELD_A: 12, FIELD_B: 0.2321, FIELD_C: 12:10:08 2011/07/22, FIELD_D: 656

字段的顺序始终相同,但有些字段可能不存在。感兴趣的字段之间可以有其他字段,例如与上面的行相比,我也可以得到以下信息:

SOME BUNCH OF TEXT, FIELD_A: 12, NOT_INTERESTED: 235, FIELD_B: 0.2321, FIELD_C: 12:10:08 2011/07/22, FIELD_D: 656, FIELDS

作为处理此文本的结果,我希望有一个干净的 CSV 文件,其中一个接一个地指定我的字段:

12,0.2321,12:10:08 2011/07/22,656

如果某些字段不存在,那么我想简单地省略值(例如 FIELD_B 不存在):

12,,12:10:08 2011/07/22,656

如何使用 sed、perl 或 awk 等命令执行此操作? 我尝试使用 perl -pe 's/^.*?(FIELD_A: (.*?),)?.*?$/\2/' 提取单个字段并失败 - 正则表达式会忽略我的字段,即使它存在

【问题讨论】:

    标签: regex linux unix text-processing


    【解决方案1】:

    您可以将awk 与关联数组一起使用,如下所示。循环遍历字段并将它们拆分为:。然后将键值对存储到关联数组中。最后打印出你想要的字段。

    awk -F, '{
     split("",arr)
     for(i=1; i<=NF; i++){
       a=index($i, ":")
       if(a != 0){
         # split on first colon to get key-value pair
         key=substr($i,1,a-1)
         val=substr($i,a+1)
    
         # remove leading spaces from key and value
         gsub(/^ */,"",key)
         gsub(/^ */,"",val)
    
         # store in an associative array
         arr[key]=val
       }   
     }
     # print out the desired fields
     print arr["FIELD_A"]","arr["FIELD_B"]","arr["FIELD_C"]","arr["FIELD_D"]
    }' data.txt
    

    【讨论】:

    • 您的解决方案效果很好。事实上,我期待一些正则表达式作为解决方案,但似乎正则表达式无法解决我的问题。所以我们需要使用脚本。
    【解决方案2】:

    这种方式怎么样(假设文件名是已知的):

    #!/usr/bin/perl
    use strict;
    use warnings;
    
    my @f = qw(FIELD_A FIELD_B FIELD_C FIELD_D);
    while(my $line = <DATA>) {
        chomp $line;
        my @r;
        for(@f) {
            if ($line =~ /$_:\s*([^,]+)/) {
                push @r, $1;
            } else {
                push @r,'';
            }
        }
        print join(',',@r), "\n";
    }
    __DATA__
    SOME BUNCH OF TEXT, FIELD_A: 12, FIELD_B: 0.2321, FIELD_C: 12:10:08 2011/07/22, FIELD_D: 656
    SOME BUNCH OF TEXT, FIELD_A: 12, NOT_INTERESTED: 235, FIELD_B: 0.2321, FIELD_C: 12:10:08 2011/07/22, FIELD_D: 656, FIELDS
    SOME BUNCH OF TEXT, FIELD_A: 12, NOT_INTERESTED: 235, FIELD_C: 12:10:08 2011/07/22, FIELD_D: 656, FIELDS
    

    输出:

    12,0.2321,12:10:08 2011/07/22,656
    12,0.2321,12:10:08 2011/07/22,656
    12,,12:10:08 2011/07/22,656
    

    【讨论】:

    • 谢谢,我试过了,但得到错误Can't locate Modern/Perl.pm in @INC (@INC contains: /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.10.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .) at for_perl line 2. BEGIN failed--compilation aborted at for_perl line 2. 似乎我的 perl 版本不是现代的 :) 我相信它对于其他使用最新版本 perl 的人来说也可以。
    • @pavel_kazlou:只需删除行 use Modern::Perl; 并将 say 更改为 print。查看我的编辑。
    • 现在这对我有用,谢谢。我已经对你的答案投了赞成票
    猜你喜欢
    • 1970-01-01
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    • 2019-11-09
    • 2020-04-26
    • 1970-01-01
    • 2011-03-05
    • 1970-01-01
    相关资源
    最近更新 更多