【问题标题】:Split my output into multiple files将我的输出拆分为多个文件
【发布时间】:2018-07-05 14:39:21
【问题描述】:

我在 CSV 文件中有以下列表,我的目标是根据每行中的日期将此列表拆分为名为 YYYY-Month 的目录。

NAME99;2018/06/13;12:27:30
NAME01;2018/06/13;13:03:59
NAME00;2018/06/15;11:33:01
NAME98;2018/06/15;12:22:00
NAME34;2018/06/15;16:58:45
NAME17;2018/06/18;15:51:10
NAME72;2018/06/19;10:06:37
NAME70;2018/06/19;12:44:03
NAME77;2018/06/19;16:36:55
NAME25;2018/06/11;16:32:57
NAME24;2018/06/11;16:32:57
NAME23;2018/06/11;16:37:15
NAME01;2018/06/11;16:37:15
NAME02;2018/06/11;16:37:15
NAME01;2018/06/11;16:37:18
NAME02;2018/06/05;09:51:17
NAME00;2018/06/13;15:04:29
NAME07;2018/06/19;10:02:26
NAME08;2018/06/26;16:03:57
NAME09;2018/06/26;16:03:57
NAME02;2018/06/27;16:58:12
NAME03;2018/07/03;07:47:21
NAME21;2018/07/03;10:53:00
NAMEXX;2018/07/05;03:13:01
NAME21;2018/07/05;15:39:00
NAME01;2018/07/05;16:00:14
NAME00;2018/07/08;11:50:10
NAME07;2018/07/09;14:46:00

什么是最聪明的方法来实现这个结果而不必创建一个静态路由列表来执行附加?

目前我的程序仅根据localtime 将此列表写入名为YYYY-Month 的目录,但不会对每一行执行任何操作。

Perl

#!/usr/bin/perl

use strict;
use warnings 'all';
use feature qw(say);

use File::Path qw<mkpath>;
use File::Spec;
use File::Copy;
use POSIX qw<strftime>;

my $OUTPUT_FILE = 'output.csv';
my $OUTFILE     = 'splitted_output.csv';

# Output to file
open( GL_INPUT, $OUTPUT_FILE ) or die $!;
$/ = "\n\n";    # input record separator

while ( <GL_INPUT> ) {

    chomp;
    my @lines = split /\n/;

    my $i = 0;

    foreach my $lines ( @lines ) {

        # Encapsulate Date/Time
        my ( $name, $y, $m, $d, $time ) =
                $lines[$i] =~ /\A(\w+);(\d+)\/(\d+)\/(\d+);(\d+:\d+:\d+)/;    

        # Generate Directory YYYY-Month - #2009-January
        my $dir = File::Spec->catfile( $BASE_LOG_DIRECTORY, "$y-$m" ) ;
        unless ( -e $dir ) {
            mkpath $dir;
        }

        my $log_file_path = File::Spec->catfile( $dir, $OUTFILE );
        open( OUTPUT, '>>', $log_file_path ) or die $!;

        # Here I append value into files
        print OUTPUT join ';', "$y/$m/$d", $time, "$name\n";    
        
        $i++;
    }
}

close( GL_INPUT );
close( OUTPUT );

【问题讨论】:

  • 你能澄清一下你想做什么吗?我知道您想创建带有基于年份和月份的子集的较小文件。所以输入文件的第一行应该进入目录2018-06/ 中的一个文件,最后一行应该进入目录2018-07/ 中的另一个文件。对吗?
  • 您期望有多少个输出文件? (如果你需要很多——比如几千个——文件,解决方案会变得更加复杂)
  • @simbabque 是的,没错 :)
  • 你确实意识到你的代码做了一些完全不同的事情?
  • 你不需要关心今天的日期。你关心每一行数据中的一些值,它恰好代表一个日期。

标签: perl date split


【解决方案1】:

这里根本没有理由关心实际日期或使用日期函数。您希望根据数据中某一列的部分值拆分数据。那恰好是日期。

NAME08;2018/06/26;16:03:57   # This goes to 2018-06/
NAME09;2018/06/26;16:03:57   #
NAME02;2018/06/27;16:58:12   #
NAME03;2018/07/03;07:47:21      # This goes to 2018-07/
NAME21;2018/07/03;10:53:00      #
NAMEXX;2018/07/05;03:13:01      #
NAME21;2018/07/05;15:39:00      #

执行此操作的最简单方法是迭代您的输入数据,然后将其粘贴到带有每个年月组合键的散列中。但是你说的是日志文件,它们可能很大,所以效率很低。

我们应该使用不同的文件句柄。

use strict;
use warnings;

my %months = ( 6 => 'June', 7 => 'July' );

my %handles;
while (my $row = <DATA>) {

    # no chomp, we don't actually care about reading the whole row
    my (undef, $dir) = split /;/, $row; # discard name and everything after date

    # create the YYYY-MM key
    $dir =~ s[^(....)/(..)][$1-$months{$2}];

    # open a new handle for this year/month if we don't have it yet
    unless (exists $handles{$dir}) {
        # create the directory (skipped here) ...
        open my $fh, '>', "$dir/filename.csv" or die $!;
        $handles{$dir} = $fh;
    }

    # write out the line to the correct directory
    print { $handles{$dir} } $row;
}

__DATA__
NAME08;2018/06/26;16:03:57
NAME09;2018/06/26;16:03:57
NAME02;2018/06/27;16:58:12
NAME03;2018/07/03;07:47:21
NAME21;2018/07/03;10:53:00
NAMEXX;2018/07/05;03:13:01
NAME21;2018/07/05;15:39:00

我已经跳过了有关创建目录的部分,因为您已经知道如何执行此操作。

如果您的数据行不连续,此代码也可以使用。这不是最有效的,因为您拥有的数据越多,句柄的数量就会增加,但只要您没有同时拥有 100 个句柄,这并不重要。

注意事项:

  • 您不需要chomp,因为您不关心使用最后一个字段。
  • 您不需要分配split 之后的所有值,因为您不关心它们。
  • 您可以通过将值分配给 undef 来丢弃它们。
  • 始终使用三参数open词法 文件句柄。
  • {} 中的print { ... } $row 需要告诉 Perl 这也是我们正在打印的句柄。见http://perldoc.perl.org/functions/print.html

【讨论】:

  • 哦。您想要月份名称,而不是 MM 数字。在那种情况下,我的正则表达式没有多大意义。但你可以使用查找哈希。
  • 在我最后一次编辑之后,我的代码可以工作♥。你怎么看待这件事 ?现在我研究您的代码,以了解您的解决方案...
  • @Perler 我很高兴您可以自己使用它!那太棒了!我的回答中的一些观点已经解决了代码审查问题。您的解决方案非常复杂。您一直在重新打开文件句柄,而不是明确关闭它们。您使用了并不真正需要的额外模块。恐怕在 $work 代码审查中我会拒绝它,因为它过于复杂和低效。随时将其发布到Code Review 以进行深入审查。 :)
猜你喜欢
  • 1970-01-01
  • 2021-12-22
  • 2021-11-29
  • 1970-01-01
  • 2014-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多