1

我目前正在阅读包含以下行的文本文件:

0 24000 97200
1 52200 95400
2 0 0
3 37800 180000
4 0 0
5 48000 95400
6 0 0

第一个值表示日期(0 = sunday, 1 = monday, ...) 。数值,例如 24000,表示总秒数。

最终我想得到这样的东西:

每周计划的图形表示

这是到目前为止的代码:

open(SCHEDULE, $schedule) or die print "Failed to open $schedule";
@lines = <SCHEDULE>;
@secondsfrom  = (0,0,0,0,0,0,0);
@secondsto    = (0,0,0,0,0,0,0);
@secondsextra = (0,0,0,0,0,0,0);
@days = ("sunday","monday","tuesday","wedsneday","thursday","friday","saturday");
foreach (@lines) {
    ($day, $fromtotalseconds, $tototalseconds) = split(/ /,$_,3);
    @secondsfrom[$day] += $fromtotalseconds;
    @secondsto[$day]   += $tototalseconds;
}
for (my $i=0; $i<=6;$i++) {
    print "\n@days[$i]  @secondsfrom[$i] to @secondsto[$i]";
}

这一刻我卡住了!我一直在寻找如何将这些值转换为类似的东西,如:

周日从 24000 到 86400 (MAX)
星期二从 0 到 48000 和从 52200 到 86400
…

这就是它为我产生的:

周日 24000 至 97200
周二 52200 至 95400
…
4

4 回答 4

2

这是一个文本实现。我并没有真正检查它的正确性,所以它可能潜伏着错误,但它应该说明如何一次处理一行输入:

#!/usr/bin/env perl

use strict; use warnings;

use constant ONE_MINUTE => 60;
use constant ONE_HOUR   => 60 * ONE_MINUTE;
use constant ONE_DAY    => 24 * ONE_HOUR;
use constant BLOCK_SIZE => ONE_MINUTE * 20;
use constant DAY_LENGTH => ONE_DAY/BLOCK_SIZE;

my @days = qw(Sun Mon Tue Wed Thu Fri Sat);

my $remainder = 0;

while (my $line = <DATA>) {
    next unless $line =~ m{
        \A
        ( [0-6]  ) \s+
        ( [0-9]+ ) \s+
        ( [0-9]+ ) \s+
        \z
    }x;
    my ($daynum, $start, $duration) = ($1, $2, $3);
    my $start_block = seconds_to_blocks($start);
    my $duration_block = seconds_to_blocks($duration);

    my ($dayrow, $hang) = make_dayrow(
        $remainder,
        $start_block,
        $duration_block,
    );

    printf "%3s: %s\n", $days[$daynum], $dayrow;
    $remainder = $hang;
}

sub seconds_to_blocks {
    my ($seconds) = @_;
    return int($seconds / BLOCK_SIZE);
}

sub make_dayrow {
    my ($remainder, $start, $duration) = @_;

    if ($remainder > DAY_LENGTH) {
        my $hang = $remainder - DAY_LENGTH;
        return ('#' x DAY_LENGTH, $hang);
    }

    my $hang = $start + $duration > DAY_LENGTH
             ? $duration - (DAY_LENGTH - $start)
             : 0
             ;

    my $dayrow = '#' x $remainder;
    $dayrow   .= ' ' x ($start - $remainder);
    $dayrow   .= '#' x ($duration - $hang);
    $dayrow   .= ' ' x (DAY_LENGTH - length $dayrow);

    return ($dayrow, $hang);
}


__DATA__
0 24000 97200
1 52200 95400
2 0 0
3 37800 180000
4 0 0
5 48000 95400
6 0 0

输出:

太阳: ################################################ ####
周一:############################################### ##########
星期二:############################################### ##
星期三: #########################################
周四:############################################### ########################
周五:################################################ #####################
周六:##############################################

更新

如果你只想要百分比,那也很简单:

#!/usr/bin/env perl

use strict; use warnings;
use YAML;

use constant ONE_MINUTE => 60;
use constant ONE_HOUR   => 60 * ONE_MINUTE;
use constant ONE_DAY    => 24 * ONE_HOUR;

my @days = qw(Sun Mon Tue Wed Thu Fri Sat);

my $remainder = 0;
my @rows;

while (my $line = <DATA>) {
    next unless $line =~ m{
        \A
        ( [0-6]  ) \s+
        ( [0-9]+ ) \s+
        ( [0-9]+ ) \s+
        \z
    }x;
    my ($daynum, $start, $duration) = ($1, $2, $3);

    my $dayrow = make_dayrow($remainder, $start, $duration);

    push @rows, $dayrow->[0];
    $remainder = $dayrow->[1];
}

for my $row (@rows) {
    print join("\t", map sprintf('%.0f%%', $_ * 100), @$row), "\n";
}

sub make_dayrow {
    my ($remainder, $start, $duration) = @_;

    return [[1, 0, 0], $remainder - ONE_DAY] if $remainder > ONE_DAY;

    my $hang = $start + $duration > ONE_DAY
             ? $duration - (ONE_DAY - $start)
             : 0
             ;

    return [
        [
            $remainder / ONE_DAY,
            $start / ONE_DAY,
            ($duration - $hang) / ONE_DAY
        ],
        $hang
    ];
}


__DATA__
0 24000 97200
1 52200 95400
2 0 0
3 37800 180000
4 0 0
5 48000 95400
6 0 0

输出:

0% 28% 72%
40% 60% 40%
71% 0% 0%
0% 44% 56%
100% 0% 0%
52% 56% 44%
66% 0% 0%
于 2012-05-03T11:58:32.920 回答
0

对您的代码进行此更改怎么样?

use strict;
use warnings;
my @lines = (
'0 24000 97200',
'1 52200 95400',
'2 0 0',
'3 37800 180000',
'4 0 0',
'5 48000 95400',
'6 0 0',
);
my @days = qw(sunday monday tuesday wedsneday thursday friday saturday);
for (my $i=0; $i<=$#lines;$i++) {
    my ($day, $fromtotalseconds, $tototalseconds) = split(/ /,$lines[$i],3);
    next if ($fromtotalseconds == 0 && $tototalseconds == 0);
    if ($tototalseconds > 86400) {
      my $remainingSeconds = $tototalseconds - (86400 - $fromtotalseconds);
      $tototalseconds = 86400;
      splice @lines, $i+1, $#lines - $i, (sprintf('%d %d %d', $day + 1, 0, $remainingSeconds), @lines[$i+1 .. $#lines]);
    }
    print "\n$days[$day] from $fromtotalseconds to $tototalseconds";
}

我删除了数组并将打印放在每次循环迭代的末尾。我还从 foreach 切换到三参数 for 因为这样我们可以轻松地将更多内容放入@lines. 每次我们发现 to-value$tototalseconds大于一天时,我们都会@lines在当前行之后添加一个新行。这样它将在下一次迭代中使用。

它没有说

Tuesday from 0 to 48000 and from 52200 to 86400

但它说

sunday from 24000 to 86400
monday from 0 to 10800
monday from 52200 to 86400
tuesday from 0 to 9000
wedsneday from 37800 to 86400
thursday from 0 to 86400
friday from 0 to 7200
friday from 48000 to 86400
saturday from 0 to 9000

我发现还不错。你将能够以你需要你的价值观的方式改变它。

顺便说一句,总是use strictuse warnings。您的代码中有几处引发了一些警告。例如,如果 perl 中数组元素的值是标量,则必须$在其前面放置 a,而不是@: $secondsfrom[$day] += $fromtotalseconds

编辑:第二个例子。

my @lines = (
'0 9600 91200',
'1 40800 186000', 
'2 0 0', 
'3 57000 87000',
'4 63600 173400', 
'5 0 0', 
'6 0 0',);

产生:

sunday from 9600 to 86400
monday from 0 to 14400
monday from 40800 to 86400
tuesday from 0 to 86400
wedsneday from 0 to 54000
wedsneday from 57000 to 86400
thursday from 0 to 57600
thursday from 63600 to 86400
friday from 0 to 86400
saturday from 0 to 64200
于 2012-05-03T12:17:37.203 回答
0
  open SH, "<", $schedule or die "Oh, $schedule die :\ \n";
      %hash = map{$_,0}(0..6); #not best but work, create a hash of days
      my $day;
      my $first;
      my $second;
  for(<SH>){
     ($day,$first,$second)=split(/ /,$_,3); #work? yeah
     $hash{$day}=$second-$first;
  }

  map{print "key is ".$_." value is ".$hash{$_}."\n";}sort keys %hash;
于 2012-05-03T11:18:10.383 回答
0

这几乎正​​是您正在寻找的:

use strict;
use warnings;

use constant MAX_DAY => 86_400;

my @week;
while ( <DATA> ) { 
    my ( $dow, $start, $end ) = split ' ';
    next unless $start + $end;
    while ( $end > 0 ) { 
        my $cont = $end > MAX_DAY;
        push @{ $week [ ( $dow++ ) % 7 ] }, [ $start, $cont ? MAX_DAY : $end, $cont ];
        $start  = 0;
        $end   -= MAX_DAY;
    }
}

my @dow_names = qw(Sun Mon Tue Wed Thu Fri Sat);
foreach my $day ( @week ) { 
    print shift( @dow_names ) . ': ';
    if ( ref $day ) { 
        print join( ' and '
                  , map { "from $_->[0] to $_->[1]" . ( $_->[2] and ' (MAX)' ) } @$day 
                  );
    }
    print "\n";
}
__DATA__ 
0 24000 97200
1 52200 95400
2 0 0
3 37800 180000
4 0 0
5 48000 95400
6 0 0
于 2012-05-03T12:55:48.167 回答