使用任何有效的方法(PERL、SED、AWK)将文件行中的信息提取到列中

Lui*_*res 2 perl awk sed

也许我对 perl/awk/sed 来说太老了,太年轻了不能停止编程。这是我需要解决的问题:

我在 TXT 文件中有这样的信息:

Name:
Name 1
Phone:
1111111
Email:
some@email1
DoentMatterInfo1:
whatever1
=
Name:
Name 2
Phone:
22222222
DoentMatterInfo2:
whatever2
Email:
some@email2
=
Name:
Name 3
DoentMatterInfo3:
whatever2
Email:
some@email3
=
Run Code Online (Sandbox Code Playgroud)

请注意,所需的信息在下一行,有一个记录分隔符 (=) 并且非常重要,有些记录没有所有信息,但可能包含我们不想要的信息。

因此,挑战是在输出中提取所需的信息(如果存在),例如:

Name 1 ; 111111 ; some@email1
Name 2 ; 222222 ; some@email2
Name 3 ; ; some@email3
Run Code Online (Sandbox Code Playgroud)

我尝试过的有点奏效,但剧照不是我想要的。

1. 使用 PERL

使用 Perl 我得到了重要的字段:

while (<>) {

    if ($_ =~ /Name/) {
        print "=\n". scalar <>;

    }    
    if ($_ =~ /Email/) {
        print "; ". scalar <>;

    } 
    if ($_ =~ /Phone/) {
        print "; ". scalar <>;

    } 

}
Run Code Online (Sandbox Code Playgroud)

我得到了一个文件,如:

Name 1
; 1111111
; some@email1
=
Name 2
; 22222222
; some@email2
=
Name:
Name 3
; some@email3
=
Run Code Online (Sandbox Code Playgroud)

现在使用 sed 我将每条记录放在一行中:

SED 使用 SED,此命令替换换行符,在一行中获取信息: sed ':a;N;$!ba;s/\n//g' input.txt > out1.txt

并返回换行符:

sed 's/|=|/\n/g' out1.txt > out2.txt

所以我得到了一个包含每一行信息的文件:

Name 1 ; 1111111 ; some@email1
Name 2 ; 22222222 ; some@email2
Name 3 ; some@email3
Run Code Online (Sandbox Code Playgroud)

仍然不是我想从编码中得到的。我想要更好的东西,比如能够用空间填充丢失的电话,所以第二列可以始终是电话列。你明白了吗?

如您所见,无论是使用 Perl、AWk 还是 SED,关键在于找到解决方案。我正在尝试 perl 哈希...

提前致谢!!

zdi*_*dim 5

这是一个 Perl 解决方案,要求并尝试

use warnings;
use strict;
use feature 'say';

my @fields = qw(Name Phone Email);  # fields to process

my $re_fields = join '|', map { quotemeta } @fields;

my %record;

while (<>) { 
    if (/^\s*($re_fields):/) { 
        chomp($record{$1} = <>);
    }
    elsif (/^\s*=/) { 
        say join ';', map { $record{$_} // '' } @fields;
        %record = (); 
    }   
}
Run Code Online (Sandbox Code Playgroud)

输入准备在数组中@fields;这是唯一拼出这些名称的地方,因此如果需要添加更多字段进行处理,只需在此处添加它们。还准备了用于匹配这些字段中的任何一个的正则表达式模式,在$re_fields.

然后我们使用<>操作符逐行读取命令行上提交的所有文件。

if如果存在,条件将捕获预期的关键字。在正文中,我们读取下一行的值并将其与作为捕获关键字的键一起存储(不需要知道是哪个)。

在以=记录开头的行上打印(正确使用给定的示例文件)。我没有为缺少的字段(没有空格)添加任何内容,也没有在;. 根据需要调整输出格式。


为了在整个过程中收集记录并在以后进一步处理(或只是打印),请将它们添加到合适的数据结构中而不是打印。选择什么样的存储取决于预想的处理类型。最简单的方法是将每个输出记录的字符串添加到数组中

my (@records, %record);

while (<>) {
    ...
    elsif (/^\s*=/) { 
        push @records, join ';', map { $record{$_} // '' } @fields;
        %record = (); 
    }   
}
Run Code Online (Sandbox Code Playgroud)

现在@records为所有记录准备好了字符串,可以简单地打印为

say for @records;
Run Code Online (Sandbox Code Playgroud)

但是,如果可能需要更多涉及的处理,那么最好将其%record作为散列引用存储在数组副本中,以便以后可以更轻松地操作各个组件

my (@records, %record);

while (<>) {
    ...
    elsif (/^\s*=/) { 
        # Add a key to the hash for any fields that are missing
        $record{$_} //= ''  for @fields;
        push @records, { %record };
        %record = (); 
    }   
}
Run Code Online (Sandbox Code Playgroud)

我为可能缺少的字段添加一个键,以便 hashrefs 具有所有预期的键,并为其分配一个空字符串。另一种选择是分配undef.

现在您可以访问每条记录中的各个字段作为

foreach my $rec (@records) { 
    foreach my $fld (sort keys %$rec) {
        say "$fld -> $rec->{$fld}"
    }
}
Run Code Online (Sandbox Code Playgroud)

或者当然只是使用Data::Dumper等等来打印整个事情。