使用Perl和非结构化数据进行标记

时间:2011-02-13 09:52:12

标签: perl parsing tokenize text-parsing

我有以下数据(来自文本文件),我想分割/获取每个元素,甚至那些空白的元素(你可以看到的某些等级没有列出,这意味着它们是0,所以我也希望得到他们)

CRN SUB      CRSE   SECT   COURSE TITLE         INSTRUCTOR        A   A- B+ B     B- C+ C     C- D+ D     D- F    I   CR NC W     WN INV TOTAL
----- --     ----   ----   -----------------   ----------------- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- -----
33450 XX     9950   AIP    OVERSEAS-AIP SPAI   NOT FOUND                                                               1   1                2
33092 XX     9950   ALB    ddddddd, SPN. vi   NOT FOUND                                                               1                    1
33494 XX     9950   W16    OVERSEAS Univ.Wes   NOT FOUND                                                               1                    1

                           INSTRUCTOR TOTALS NOT FOUND             2                                                1   18   1    2          24
                           PERCENTAGE DISTRI NOT FOUND             8                                                4   75   4    8       ******

33271 PE 3600 001          Global Geography    sfnfbg,dsdassaas        2    2    1    1    2    3    6    5    3    3   1                        29

                           INSTRUCTOR TOTALS snakdi,plid          2    2    1    1    2    3    6    5    3    3   1                        29
                           PERCENTAGE DISTRI krapsta,lalalal          7    7    3    3    7   10   21   17   10   10   3                     ***

问题如你所见,我没有特定的分隔符,因为有些等级缺失,如果不是,我可以从行开始直到一年级('A')获得所有数据)然后所有等级并用/ \ s + /分割它们,但事实并非如此。 任何建议(如果有的话......)都会很棒。

感谢,

2 个答案:

答案 0 :(得分:3)

这看起来最好是编写或查找基于列的文本解析器?我在CPAN上找到DataExtract-FixedWidth,但没有个人经验。格式看起来很混乱,尤其是列边框上的数字。无论如何,你必须做某种预处理或启发式方法......

答案 1 :(得分:3)

某些列中的地方存在不规则性(请注意,第一个总值18和75部分位于下一列),但如果您不需要它们,可以尝试这样的事情:

my @data;

# skip header
my $hdr = <DATA>;
my $sep = <DATA>;

while(<DATA>) {
    chomp;

    # skip empty and total lines
    next if /^\s*$/ || /^[ ]{5}/;

    push @data, [
        map { s/^\s+//; s/\s+$//; $_ }      # trim each column
        unpack 'A6A7A7A7 A18A20 A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4A4 A10', $_
    ];
}

use Data::Dump;
dd \@data;

__DATA__
CRN SUB      CRSE ...
----- --     ---- ...

您可能需要在解包模板中调整实际数据的列边界,但这应该可以帮助您入门。