使用Perl,修剪文件前半部分的最佳方法是什么?

时间:2012-12-11 19:00:27

标签: perl

我为自己的无知道歉,但我对Perl非常陌生,需要立即帮助....

我有一个包含“历史”数据的文件,我知道这些记录是逗号分隔的。当然,随着历史的发展,这个文件不断增长和发展。所以使用Perl,我想修剪数据文件,因为它是最老的。这不需要是一门精确的科学。我想我会做类似以下的事情:

  • 计算文件中逗号的数量并除以2(找到中间点)
  • 因此,例如,如果有100条记录(因此,99个逗号分隔符),则逗号计数为99
  • 然后我除以2得到近似的中间点(向上舍入),在这个例子中为46
  • 然后删除第46个逗号之前的所有记录(包括逗号,以便该文件不以逗号开头)。
  • 然后保存我新修剪的历史数据文件。

以下是使用逗号分隔符的非常小的示例文件布局:

20121130092403000Server1::RedHat   1.2.3.4(1234),20121130092503000Server2::RedHat   5.6.7.8(1234),20121130092603000Server3::SUSE   9.8.7.6(9876),20121130092703000Server4::WindowsXP   5.6.7.8(6543)

我希望这是有道理的。

谢谢!

4 个答案:

答案 0 :(得分:1)

“最快,最有效的方式”可能是一个不同的问题。这是做这样的事情的典型方式:

use strict;
use warnings;

local $/ = ",";
my @file = <DATA>;
say "Number of records: " . @file;
my $half = int((@file/2)+0.5);
say "Last half of records ($half):";
say @file[$half .. $#file];

__DATA__
20121130092403000Server1::RedHat   1.2.3.4(1234),20121130092503000Server2::RedHat   5.6.7.8(1234),20121130092603000Server3::SUSE   9.8.7.6(9876),20121130092703000Server4::WindowsXP   5.6.7.8(6543),

请注意,此处使用DATA文件句柄进行演示。您只需将<DATA>更改为<>即可使其使用文件参数。

这样效率会有所降低,因为文件被读入内存,并且会占用内存,这对于大文件来说可能是昂贵的。另一种方法是简单地浏览文件并计算记录,然后重新打开文件进行打印。 E.g:

my $file = shift;
local $/ = ",";
open my $fh, "<", $file or die $!;
my $count;
while (<$fh>) { $count++ }
$count = int(($count/2)+0.5);
open $fh, "<", $file or die $!;
while ($count-- > 0) { <$fh> };
while (<$fh>) { print }

当然,这些输出需要重定向,例如像这样:

perl script.pl oldfile > newfile

您可能也喜欢Tie::File模块。 E.g:

use strict;
use warnings;
use Tie::File;

my $file = shift;
tie @array, 'Tie::File', $file or die $!;
my $half = int((@array/2)+0.5);
splice @array, 0, $half;
untie @array;

请注意,这种效果是不可逆转的,因此请在尝试之前进行备份。据推测,即使对于大文件也是如此,并且不会将文件读入内存。

答案 1 :(得分:0)

无法从文件的开头删除。它只来自文件的末尾。要从文件的开头有效删除,必须重写整个文件(例如,通过创建一个包含要保留的部分的新文件,然后在旧文件上重命名新文件)。

答案 2 :(得分:0)

很大程度上取决于数据添加到文件的方式和时间。数据是否每天添加一次?一小时一次?持续?在手动的基础上?在重新构建数据文件时,是否可以阻止将新数据添加到文件中?文件是否通过写入过程持续保持打开状态,或者每次添加新数据时是否重新打开文件?

更好的方法是将新数据写入新文件。例如,如果要基于每天管理数据,请让编写器进程根据当前日期将新数据写入文件。例如。写入2012-12-11的数据将写入文件data-2012-12-11

然后,您只需删除文件即可管理数据。要迭代所有数据,您可以使用perl的通配功能:

@ARGV = glob("data-*");
while (<>) {
  ...
}

答案 3 :(得分:0)

这取决于所有记录是在一行中(以便您的.csv类似于列表)还是多行(以便您的.csv类似于表)。

如果是前者,您概述的方法可以正常工作。这就是诀窍:

use strict;
use POSIX;

my $filename = "somecsvfilename.csv";
open (IN, "<", $filename);
my $fulltext;
while (<IN>) {
    chomp;
    $fulltext .= $_;
}
close IN;

my @data_segments = split(",", $fulltext);
my $num_commas = @data_segments;
my $num_to_delete = floor($num_commas/2);

open (OUT, ">", $filename);
my $i = $num_to_delete;
while ($i < $num_commas) {
    print OUT $data_segments[$i];
    if ($i != ($num_commas - 1)) {print OUT ","}
    $i++;
}
close OUT;

如果您的数据实际上是一个表,您将需要使用Text :: CSV之类的东西,并在输出数据之前删除行数组的前半部分。您可能需要考虑保留第一行,因为它可能包含标题数据...没有看到您的输入,很难说什么是最好的。