В Perl, как мне обрабатывать несколько строк - PullRequest
3 голосов
/ 10 июля 2010

Скажем, у меня есть файл, который имеет следующие строки с "TIMESTAMP" "NAME":

10: 00: 00 Боб
11:00:00 Том
11:00: 20 Фред
11:00:40 Джордж
12:00:00 Билл

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

10: 00: 00 Боб
11:00:00 Том, Фред, Джордж
12:00:00 Билл

Ответы [ 4 ]

2 голосов
/ 11 июля 2010

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

Мое решение, тогда:

#!/usr/bin/env perl

use strict;
use warnings;

my $current_hour = -1;
my @names;

while (my $line = <DATA>) {
  my ($hour, $name) = $line =~ /(\d{2}):\d{2}:\d{2} (.*)/;
  next unless $hour;

  if ($hour != $current_hour) {
    print_hour($current_hour, @names);
    @names = ();
    $current_hour = $hour;
  }

  push @names, $name;
}

print_hour($current_hour, @names);

exit;

sub print_hour {
  my ($hour, @names) = @_;
  return unless @names;

  print $hour, ':00:00 ', (join ', ', @names), "\n";
}

__DATA__
10:00:00 Bob
11:00:00 Tom
11:00:20 Fred
11:00:40 George
12:00:00 Bill
2 голосов
/ 10 июля 2010

В grouped_by_hour ниже, для каждой строки из дескриптора файла, если у него есть временная метка и имя, мы push будем указывать это имя в массиве, связанном с часом временной метки, используя sprintf для нормализации часа в случаеодна временная метка равна 03:04:05, а другая - 3:9:18.

sub grouped_by_hour {
  my($fh) = @_;

  local $_;
  my %hour_names;

  while (<$fh>) {
    push @{ $hour_names{sprintf "%02d", $1} } => $2
      if /^(\d+):\d+:\d+\s+(.+?)\s*$/;
  }

  wantarray ? %hour_names : \%hour_names;
}

Нормализованные часы также позволяют нам выполнять сравнение по умолчанию.Приведенный ниже код помещает ввод в специальный файловый дескриптор DATA, добавляя его после токена __DATA__, но в реальном коде вы можете вызвать grouped_by_hour $fh.

my %hour_names = grouped_by_hour \*DATA;
foreach my $hour (sort keys %hour_names) {
  print "$hour:00:00 ", join(", " => @{ $hour_names{$hour} }), "\n";
}

__DATA__
10:00:00 Bob
11:00:00 Tom
11:00:20 Fred
11:00:40 George
12:00:00 Bill

Выход:

10:00:00 Bob
11:00:00 Tom, Fred, George
12:00:00 Bill
2 голосов
/ 10 июля 2010

Читайте файл построчно в блоке следующим образом:

while(<>) {
    # ... do something with the line in $_
    # specifically, collect the hour and name
    # ignoring malformed lines
    if (/(\d\d):\d\d:\d\d\s+(\w+)/) {
        my $hour = $1;
        my $name = $2;
    }
}

и создайте хэш с первым битом, вставив следующее во внутренний if блок

$people{$hour} = $people{$hour} . ", " . $name 

Наконец, за пределами цикла выведите хеш:

while ( my ($time, $names) = each(%people) ) {
    print $time . ":00:00 " . $names ."\n";
}

(Это не проверено, но это базовый подход, который я бы выбрал.)

0 голосов
/ 10 июля 2010

Вот полное решение, как это сделать.

my @readings = (
    "10:00:00 Bob",
    "11:00:00 Tom",
    "11:00:20 Fred",
    "11:00:40 George",
    "12:00:00 Bill",
);

my %hours;

for my $line (@readings) {
    $line =~ /^(\d{2}).*?([a-zA-Z]+)/;
    push(@{$hours{$1}}, $2);
}

for my $hour (sort keys %hours) {
    print "$hour:00:00 ";
    print join ", ", @{$hours{$hour}};
    print "\n";
}

В результате:

10:00:00 Bob
11:00:00 Tom, Fred, George
12:00:00 Bill
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...