我有一个从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
有时某个列的数据是可选的,因为您可以看到它。
答案 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)];
使用此构造,您不必担心您拥有多少教授。即使你没有,其他值都可以正常工作(你的教授会得到一个空数组)。