Linux - 拆分记录并生成所需的格式

时间:2015-07-13 10:11:40

标签: bash perl shell awk scripting

我在文本文件中有成千上万条记录。

2015-05-16      testing112 alpha1        {}      {}      {beta1}
2015-05-16      testing124  gamma1   {xbgtd1} {}      {hjhjje;g76gr}
2015-05-16      testing124  asdasdg   {xbgtd1;dfdfgg} {}      {hjhjje;g76gr}
  1. 该文件有6列。
  2. 空格(一个或多个)是字段之间的分隔符。
  3. 第一,第二和第二第三个字段永远不会是空的。
  4. 4th,5th& {}之间包含第6个字段。如果没有价值,只有2个大括号,如{}。如果这些字段具有多个值,则花括号内的值将以分号(例如{a;b})分隔。
  5. 我想为每行中的每个字段执行以下操作

    循环浏览文件中的每一行&生成以下

    1) <some sentence>field1, field2,field3;
    2) <some sentence>field1, field2, field4;
    3) <some sentence>field1, field2,field5;
    4) <some sentence>field1, field2,field6;
    

    在(2),(3)&amp; (4)上面,如果花括号内的字段有多个值,它们用分号分隔,并且喜欢为每个字段生成相同的语句,如下所示:

    1) <some sentence>field1, field2, field4_first;
    2) <some sentence>field1, field2, field4_second;
    3) <some sentence>field1, field2, field5_first;
    4) <some sentence>field1, field2, field5_second;
    5) <some sentence>field1, field2, field6_first;
    6) <some sentence>field1, field2, field6_second;
    

    我尝试使用perl来实现这一目标。但是,字符串的分割并不正确。我在split(s/ {1,}//,$_)的行上使用了一些东西,因为字段之间可以有任意数量的空格。这不起作用。我也尝试了其他一些选项似乎不起作用。有人可以帮我吗?

    我是在CentOS上运行的。我可以用任何语言来达到结果。

    下面的代码是在进一步写入文件之前用于解析和打印以查看值的代码:

    #!/usr/bin/perl -w
    
    my $i_file      =   'input.txt';
    my $o_file      =   'output.txt';
    my $text_cont   =   "";
    our $ins_1  =   "";
    our $ins_2  =   "";
    our $ins_3  =   "";
    our $ins_4  =   "";
    
    open (FILE, $i_file) or die "Could not read from $i_file, program halting.";
        while(<FILE>) {
            (my $map_date,my $nam,my $ins_name_1, my $ins_name_2, my $ins_name_3, my $ins_name_4) = split(s/ \{1,\}//,$_);
    
            my $name1_refined   =   $ins_name_1 =~ s/\{|\}//;
            my $name2_refined   =   $ins_name_2 =~ s/\{|\}//;
            my $name3_refined   =   $ins_name_3 =~ s/\{|\}//;
            my $name4_refined   =   $ins_name_4 =~ s/\{|\}//;
    
            my @nam1_values =   split(';', $name1_refined);
            my @nam2_values =   split(';', $name2_refined);
            my @nam3_values =   split(';', $name3_refined);
            my @aod_values  =   split(';', $name4_refined);
    
            print "$name1_refined\n";
            print "$name2_refined\n";
            print "$name3_refined\n";
            print "$name4_refined\n";
        }
    
    close FILE;
    

4 个答案:

答案 0 :(得分:3)

对于你的第一部分 - 我建议你过度思考它。

split没有参数在空格上分割。

所以拿你的输入数据:

#!/usr/local/bin/perl
use strict;
use warnings;

use Data::Dumper;

while ( <DATA> ) {
   my @stuff = split;
   print Dumper \@stuff;
}
__DATA__
2015-05-16      testing112 alpha1        {}      {}      {beta1}
2015-05-16      testing124  gamma1   {xbgtd1} {}      {hjhjje;g76gr}
2015-05-16      testing124  asdasdg   {xbgtd1;dfdfgg} {}      {hjhjje;g76gr}

你得到一个数组:

$VAR1 = [
          '2015-05-16',
          'testing124',
          'asdasdg',
          '{xbgtd1;dfdfgg}',
          '{}',
          '{hjhjje;g76gr}'
        ];

然后,您可以在子字段上再次应用清理/拆分。

 my @subfields = map {  s/^{|}$//g; split( /;/ ) } @stuff[ 3 .. 5 ];
print Dumper \@subfields;

这将 - 使用map - 拆分字段3-5中的每个元素(记住perl从零开始),并删除外部的波浪形括号。

map是一个非常聪明的高阶函数,它是foreach循环的 sort ,因为它可以转换&#39;列表中的每个元素并通过依次将代码块应用于列表中的每个项目来返回新列表(并且&#39;隐式地返回最后一次调用的结果,例如{{{{ 1}}功能)。

给予(最后一行):

split

那么你可以:

$VAR1 = [
          'xbgtd1',
          'dfdfgg',
          'hjhjje',
          'g76gr'
        ];

注意 - 在最后一行中,它跳过了空字段&#39; 5&#39;。如果你需要它,这不是很难保持。首先想到这样做是为了改变地图:

foreach my $field ( @subfields ) {
    print "some_sentence $stuff[0] $stuff[1] $field\n";
}

现在就是你的地图:

  • 迭代元素3 - 5.

  • 应用&#39;删除括号&#39;变换。

  • 测试是否还有其他内容

  • 要么返回my @subfields = map { s/^{|}$//g; m/./ ? split( /;/ ) : '' } @stuff[ 3 .. 5 ]; 字符串,要么返回空字符串。

此外 - 您的代码中有几个要点 - 您最好还是选择:

  • 将打开更改为使用词法文件句柄打开的3个arg。例如。 split;

  • 您可以改为open ( my $input_fh, "<", $i_file ) or die $!

  • 当您为类似的变量编号时,经常表示您希望使用列表。

因此,使代码看起来更像:

my ( $var1, $var2 ) = split;

(您可以删除#!/usr/local/bin/perl use strict; use warnings; use Data::Dumper; my $i_file = 'input.txt'; open( my $input_fh, "<", $i_file ) or die "Could not read from $i_file, program halting : $!"; while (<$input_fh>) { my ( $map_date, $nam, @ins_name ) = split; print Dumper \@ins_name; my @subfields = map { s/^{|}$//g; m/./ ? split(/;/) : '' } @ins_name; print Dumper \@subfields; foreach my $field (@subfields) { print "some_sentence $map_date $nam $field\n"; } } close($input_fh); - 它用于打印诊断)。

答案 1 :(得分:1)

使用foreach循环的另一种方法就像我在代码本身中做的解释

$_foreach循环中当前元素的隐式变量:

#!/usr/bin/perl
use strict;
use warnings;

my @splits;
open my $fh, '<', 'file' or die "unable to open file :$! \n";

while ( <$fh> ) {

    chomp;
    @splits = split( /\s+/ );
    my ( $field1, $field2, $field3 ) = ( shift @splits, shift @splits, shift @splits );    # get first second and third fields and remove from array

    print "<some text> $field1 $field2 $field3 \n";    #print them

    foreach ( @splits ) {    #@splits contain only 4th 5th and 6th field now

        if ( $_ =~ /\{(.*)\}/ ) {

            my $match = $1;

            if ( $match =~ /;/ ) {    #check if any of fields contain ;
                my @fields = split( /;/, $match );    #split with ;

                print "<some text> $field1 $field2 $_ \n" foreach ( @fields ); # print for each field

            }
            else {
                print "<some text> $field1 $field2 $match\n";    # print if field does not contain ;
            }
        }
    }
}

close( $fh );

答案 2 :(得分:1)

这个Perl程序将执行我认为您要求的

use strict;
use warnings;

my $sentence = '<some sentence>';

while ( <DATA> ) {
  my @fields = /[^\s{};]+/g;
  print $sentence, join(', ', @fields[0,1,$_]), "\n" for 2 .. $#fields;
}

__END__
2015-05-16      testing112 alpha1        {}      {}      {beta1}
2015-05-16      testing124  gamma1   {xbgtd1} {}      {hjhjje;g76gr}
2015-05-16      testing124  asdasdg   {xbgtd1;dfdfgg} {}      {hjhjje;g76gr}

输出

<some sentence>2015-05-16, testing112, alpha1
<some sentence>2015-05-16, testing112, beta1
<some sentence>2015-05-16, testing124, gamma1
<some sentence>2015-05-16, testing124, xbgtd1
<some sentence>2015-05-16, testing124, hjhjje
<some sentence>2015-05-16, testing124, g76gr
<some sentence>2015-05-16, testing124, asdasdg
<some sentence>2015-05-16, testing124, xbgtd1
<some sentence>2015-05-16, testing124, dfdfgg
<some sentence>2015-05-16, testing124, hjhjje
<some sentence>2015-05-16, testing124, g76gr

答案 3 :(得分:0)

这是awk版本/一个班轮。 text是包含数据的文件名。

awk '{for(i=4;i<=NF;i++){if(length($i) > 2) printf $1","$2","$3","substr($i,2,length($i)-2)"\n"}}' text | awk -F',' '{split($4,arr,";");for (i in arr) { printf "<some sentence>"$1","$2","$3","arr[i]";\n"}}'