当某些字段包含空格时,如何拆分线?

时间:2010-10-21 10:18:34

标签: regex perl

我有一个从PDF文件中提取的文本文件。它以表格形式排列;这是其中的一部分:

 DATE SESS PROF1 PROF2 COURSE SEC GRADE COUNT 

 2007/09 1 RODRIGUEZ TANIA DACSB 06500 001 A 3 

 2007/09 1 RODRIGUEZ TANIA DACSB 06500 001 A- 2 

 2007/09 1 RODRIGUEZ TANIA DACSB 06500 001 B 4 

 2007/09 1 RODRIGUEZ TANIA DACSB 06500 001 B+ 2 

 2007/09 1 RODRIGUEZ TANIA DACSB 06500 001 B- 1 

 2007/09 1 RODRIGUEZ TANIA DACSB 06500 001 WU 1 

 2007/09 1 NOOB ADRIENNE JOSH ROGER DBIOM 10000 125 C+ 1 

 2007/09 1 NOOB ADRIENNE JOSH ROGER DBIOM 10000 125 C+ 1 

 2007/09 1 FUENTES TANIA DACSB 06500 002 A 3 

 2007/09 1 FUENTES TANIA DACSB 06500 002 A- 8 

 2007/09 1 FUENTES ALEXA DACSB 06500 002 B 5 

 2007/09 1 FUENTES ALEXA DACSB 06500 002 B+ 3 

 2007/09 1 FUENTES ALEXA DACSB 06500 002 B- 1 

 2007/09 1 FUENTES ALEXA DACSB 06500 002 C 1 

 2007/09 1 FUENTES ALEXA DACSB 06500 002 C+ 1 

 2007/09 1 LIGGINS FREDER DACSB 06500 003 A 1

第一行是列名,其余行是数据。 我想要获得8列,起初看起来非常容易通过我为每行读取split(/\s+/, ...),但后来我注意到在某些行中还有其他空格,例如:

2007/09 1 NOOB ADRIENNE JOSH ROGER DBIOM 10000 125 C+ 1

有时某个列的数据是可选的,因为您可以看到它。

5 个答案:

答案 0 :(得分:2)

相信它含糊不清:

如果PROF1可以包含空格,您如何知道它的结束位置以及PROF2的开始位置?如果PROF2还包含空格怎么办?或3个空间..

你甚至可能无法告诉自己,如果可以的话,因为你可以区分出一个姓氏和一个姓氏。

如果您使用的是Linux / Unix,请尝试在pdf上运行text2pdf ..可能会给您带来更好的效果。

答案 1 :(得分:2)

问题很复杂,但并非无法解决。在我看来,课程将始终包含alpha代码和数字代码之间的空格,并且教授名称也将始终包含空格。但是如果有人像“VAN DYKE”那样有一个由两部分组成的名字,你就会变得非常紧张。

正则表达式会描述此记录:

my $record_exp
    = qr{ ^ \s*
          (\d{4}/\d{2}) # yyyy/mm date
          \s+
          (\d+)         # any number of digits
          \s+
          (\S+ \s \S+) # non-space cluster, single space, non-space cluster
          \s+
          # sames as last, possibly not there, separating spaces are included
          # in the conditional, because we have to make sure it will start
          # right at the next rule.
          (?:(\S+ \s \S+)\s+)?  
          # a cluster of alpha, single space, cluster of digits
          (\p{Alpha}+ \s \d+)   
          \s+    # any number of spaces           
          (\S+)  # any number of non-space
          \s+    # ditto..  
          (\S+)  
          \s+    
          (\S+)  
        }x;

这使得循环更容易:

while ( <$input> ) { 
    my @fields = m{$record_exp};
    # ... list of semantic actions here...
}

但是你也可以将它存储到结构中,因为知道数据的唯一可变部分是教授:

use strict;
use warnings;
my @records;
<$input>; # bleed the first line
while ( <$input> ) { 
    my @fields         = split; # split on white-space
    my $record         = { date => shift @fields };
    $record->{session} = shift @fields;
    $record->{profs}   = [ join( ' ', splice( @fields, 0, 2 )) ];
    while ( @fields > 5 ) { 
        push @{ $record->{profs} }, join( ' ', splice( @fields, 0, 2 ));
    }
    $record->{course} = splice( @fields, 0, 2 );
    @$record{ qw<sec grade count> } = @fields;
    push @records, $record;
}

答案 2 :(得分:1)

在我看来,前四列和最后5列始终存在,第5和第6列(prof2)是可选的

因此,在尝试时拆分线,从结果数组中拉出前四个和最后五个元素,然后剩下的就是第5列和第6列

但是如果prof1或prof2条目可能丢失,那么你就会被卡住 - 你的文件格式不明确

答案 3 :(得分:1)

没有任何内容表明您必须只使用一个正则表达式。如果这样可以更容易处理奇怪的部分,你可以用块来修剪你的线条。

答案 4 :(得分:1)

我可能仍会使用split(),但随后会访问数据:

my @values = split '\s+', $string;
my $date = $values[0];
my $sess = $values[1];
my $count = $values[-1];
my $grade = $values[-2];
my $sec = $values[-3];
my $course = $values[-4];
my @profs = @values[2..($#values-5)];

使用此构造,您不必担心您拥有多少教授。即使你没有,其他值都可以正常工作(你的教授会得到一个空数组)。