Можно ли разобрать этот кошмар с помощью Perl? - PullRequest
1 голос
/ 25 июня 2009

Я работаю над файлом doc, который при копировании и вставке в текстовый файл дает мне следующий пример вывода:

ARTA215   ADVANCED LIFE DRAWING (3 Cr) (2:2)  + Studio 1 hr.
This advanced study in drawing with the life ....
Prerequisite: ARTA150
Lab Fee Required

ARTA220   CERAMICS II  (3 Cr) (2:2)  + Studio 1 hr.
This course affords the student the opportunity to ex...
Lab Fee Required

ARTA250   SPECIAL TOPICS IN ART 
  This course focuses on selected topic....

ARTA260   PORTFOLIO DEVELOPMENT   (3 Cr) (3:0)
The purpose of this course is to pre....
BIOS010   INTRODUCTION TO BIOLOGICAL CONCEPTS (3IC) (2:2) 
This course is a preparatory course designed to familiarize the begi....

BIOS101   GENERAL BIOLOGY (4 Cr) (3:3)
This course introduces the student to the principles of mo...
Lab Fee Required

BIOS102   INTRODUCTION TO HUMAN BIOLOGY  (4 Cr)  (3:3)
This course is an introd....
Lab Fee Required

Я хочу иметь возможность проанализировать его так, чтобы сгенерировалось 3 поля, и я мог бы вывести значения в файл .csv.

Разрывы строк, интервалы и т. Д. - вот как это может быть в любой точке этого файла.

Лучше всего для регулярного выражения найти 4 заглавные буквы, за которыми следуют 3 числа, а затем выяснить, заглавные ли следующие 2 буквы. (это учитывает курс №, но также исключает возможность отключения во время, когда он может сказать «предварительное условие», как в первой записи). После этого регулярное выражение находит первый разрыв строки и получает все после него, пока не найдет следующий курс #. 3 поля - это номер курса, название курса и описание курса. Номер и название курса всегда находятся в одной строке, а описание - все ниже.

Пример конечного результата будет содержать 3 поля, которые, как я предполагаю, могут быть сохранены в 3 массива:

"ARTA215","ADVANCED LIFE DRAWING (3 Cr) (2:2)  + Studio 1 hr.","This advanced study in drawing with the life .... Prerequisite: ARTA150 Lab Fee Required"


Как я уже сказал, это настоящий кошмар, но я хочу автоматизировать это, а не убирать за кем-то каждый раз, когда файл генерируется.

Ответы [ 5 ]

11 голосов
/ 25 июня 2009

Рассмотрим следующий пример, который зависит от того, какие блоки описания курса полностью содержатся в том, что Perl считает абзацами:

#! /usr/bin/perl

$/ = "";
my $record_start = qr/
  ^            # starting with a newline
  \s*          # allow optional leading whitespace
  ([A-Z]+\d+)  # capture course tag, e.g., ARTA215
  \s+          # separating whitespace
  (.+?)        # course title on rest of line
  \s*\n        # consume trailing whitespace
/mx;

while (<>) {
  my($course,$title);
  if (s/\A$record_start//) {  # fix Stack Overflow highlighting /
    ($course,$title) = ($1,$2);
  }
  elsif (s/(?s:^.+?)(?=$record_start)//) {  # ditto /
    redo;
  }
  else {
    next;
  }

  my $desc;
  die unless s/^(.+?)(?=$record_start|\s*$)//s;
  (my $desc = $1) =~ s/\s*\n\s*/ /g;
  for ($course, $title, $desc) {
    s/^\s+//; s/\s+$//; s/\s+/ /g;
  }
  print join("," => map qq{"$_"} => $course, $title, $desc), "\n";
  redo if $_;
}

Когда подается ваш ввод данных, он выводит

"ARTA215","ADVANCED LIFE DRAWING (3 Cr) (2:2) + Studio 1 hr.","This advanced study in drawing with the life .... Prerequisite: ARTA150 Lab Fee Required"
"ARTA220","CERAMICS II (3 Cr) (2:2) + Studio 1 hr.","This course affords the student the opportunity to ex... Lab Fee Required"
"ARTA250","SPECIAL TOPICS IN ART","This course focuses on selected topic...."
"ARTA260","PORTFOLIO DEVELOPMENT (3 Cr) (3:0)","The purpose of this course is to pre...."
"BIOS010","INTRODUCTION TO BIOLOGICAL CONCEPTS (3IC) (2:2)","This course is a preparatory course designed to familiarize the begi...."
"BIOS101","GENERAL BIOLOGY (4 Cr) (3:3)","This course introduces the student to the principles of mo... Lab Fee Required"
"BIOS102","INTRODUCTION TO HUMAN BIOLOGY (4 Cr) (3:3)","This course is an introd.... Lab Fee Required"
7 голосов
/ 25 июня 2009

Попробуйте:

my $course;
my @courses;
while ( my $line = <$input_handle> ) {
    if ( $line =~ /^([A-Z]{4}\d+)\s+([A-Z]{2}.*)/ ) {
        $course = [ "$1", "$2" ];
        push @courses, $course;
    }
    elsif ($course) {
        $course->[2] .= $line
    }
    else {
        # garbage before first course in file
        next
    }
}

Это создает массив массивов, насколько я понимаю, вы хотите. Для меня было бы более разумно иметь массив хэшей или даже хэш хэшей.

4 голосов
/ 25 июня 2009

У меня была примерно та же идея , что и у Gbacon , чтобы использовать режим абзаца, так как это аккуратно разделит файл на записи для вас. Он печатал быстрее, но я написал один, так что вот мой треск:

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

local $/ = "";

my @items;
while (<>) {
  my( $course, $description ) = (split /\n/, $_)[0, 1];
  my( $course_id, $name ) = ($course =~ m/^(\w+)\s+(.*)$/);
  push @items, [ $course_id, $name, $description ];
}

for my $record (@items) {
  print "Course id: ", $record->[0], "\n";
  print "Name and credits: ", $record->[1], "\n";
  print "Description: ", $record->[2], "\n";
}

Как отмечает Ysth в комментарии к ответу Gbacon, режим абзаца здесь может не работать. Если нет, не берите в голову.

0 голосов
/ 29 января 2010
#!/usr/bin/perl
$/ = "\n\n";
$FS = "\n";
$, = ',';
while (<>) {
    chomp;
    @F = split($FS, $_);
    print join($,,@F) ."\n";
}
0 голосов
/ 25 июня 2009

регулярное выражение может быть излишним для этого, так как шаблон выглядит просто:

[course]
[description]
{Prerequisites}
{Lab Fee Required}

, где [курс] состоит из

[course#] [course title] {# Cr} [etc/don't care]

и курс # - это только первые 7 символов.

, чтобы вы могли сканировать файл с помощью простого конечного автомата, например:

//NOTE: THIS IS PSEUDOCODE
s = 'parseCourse'
f = openFile(blah)
l = readLine(f)
while (l) {
    if (s=='parseCourse') {
        if (l.StartsWith('Prerequisite:')) {
            extractPrerequisite(l)
        }
        else if (l.StartsWith('Lab Fee Required')) {
            extractLabFeeRequired(l)
        }
        else {
            extractCourseInfo(l)
            s='parseDescription'
        }
    }
    else if (s=='parseDescription') {
        extractDescription(l)
        s='parseCourse'
    }
    l = readLine(f)
}
close(f)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...