-2

对于我的无知,我深表歉意,但我对 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

4 回答 4

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><>使其使用文件参数。

这会稍微降低效率,因为文件被读入内存,并且会消耗内存,这对于大文件来说可能是昂贵的。另一种方法是简单地运行文件并计算记录,然后重新打开文件进行打印。例如:

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模块。例如:

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;

请注意,这样做的影响是不可逆的,因此请在尝试之前进行备份。即使对于大文件,它也被认为是有效的,并且不会将文件读入内存。

于 2012-12-11T19:33:43.433 回答
0

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

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

然后,您可以通过简单地删除文件来管理您的数据。要遍历所有数据,您可以使用 perl 的 globbing 功能:

@ARGV = glob("data-*");
while (<>) {
  ...
}
于 2012-12-11T19:34:23.180 回答
0

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

于 2012-12-11T19:07:50.423 回答
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 之类的东西,并在输出数据之前删除行数组的前半部分。您可能需要考虑保留第一行,因为它可能包含标题数据......没有看到您的输入,很难说什么是最好的。

于 2012-12-11T19:36:39.240 回答