假设我有以下<Tab>
分隔的文本文件:
file name size owner
file1.txt 12.345 root
file2.txt 0.172222 user1
file3.txt 2.46e2 user2
file4.txt 12345 root
file5.txt 21 user3
file6.txt 246.0 user1
file name owner last modified last accessed
text4.txt root 12.73 13.53
text5.txt user3 15.3333 34
file1.txt root 23 31.0032
该文件由几个“表”组成,每个表都以标题行开头,然后包含一些数据行。有些列是数字,但每个表可以有不同的数字以及不同类型的列。列的类型事先未知,无法根据表头确定。
表中的数值使用各种格式 - 可能有整数、浮点十进制数或科学记数法的数字。
我的问题是如何将此表中的所有数字字段转换为相同的格式。例如,我可能希望使用"%.2f"
printf 格式说明符格式化每个数字字段。当然,其他非数字字段必须保持不变。
另外,我希望能够任意调整(例如添加 42,然后乘以 7)此文件中包含的每个数字字段。
我正在寻找的解决方案应该是基于现场的。它应该扫描整个文件,并且对于每个字段,它应该确定它是否是数字。如果它是数字,它应该打印其调整和格式化的值。否则,应该只打印原件。
我知道可以用 来完成类似的事情awk
。但如果我没记错的话,awk
用于double
数字的内部表示,因此它可能会出现精度和较大值的问题。因此,理想情况下,我想使用其他东西,应该正确处理至少 64 位整数的东西。
有什么简单的方法可以实现这一目标吗?
答案1
Scalar::Util
perl 有一个名为(自 v5.8 起包含在 perl 中)的模块,该模块有一个名为 的有用函数looks_like_number()
,可用于检测字段是否为数字。
looks_like_number
并不完美,但已经相当不错了。
一个简单的 Perl 程序来完成你想要的事情的简单轮廓可能看起来像这样:
#! /usr/bin/perl
use Scalar::Util qw(looks_like_number);
while(<>) {
chomp;
my @fields=split("\t");
foreach my $f (0..scalar @fields-1) {
if (looks_like_number($fields[$f])) {
$fields[$f] += 42;
$fields[$f] *= 7;
$fields[$f] = sprintf("%.2f",$fields[$f]);
}
}
print join("\t",@fields),"\n";
}
如果将上面的示例数据作为输入,它会打印以下内容:
file name size owner
file1.txt 380.41 root
file2.txt 295.21 user1
file3.txt 2016.00 user2
file4.txt 86709.00 root
file5.txt 441.00 user3
file6.txt 2016.00 user1
file name owner last modified last accessed
text4.txt root 383.11 388.71
text5.txt user3 401.33 532.00
file1.txt root 455.00 511.02
这是该脚本的另一个版本,它使用 Math::BigFloat 进行所有计算,将小数四舍五入为 2 位数字。
#! /usr/bin/perl
use Scalar::Util qw(looks_like_number);
use Math::BigFloat;
while(<>) {
chomp;
my @fields=split("\t");
foreach my $f (0..scalar @fields-1) {
if (looks_like_number($fields[$f])) {
my $BF = Math::BigFloat->new($fields[$f]);
$BF->badd(42);
$BF->bmul(7);
$BF->ffround(-2);
$fields[$f] = $BF->bstr();
}
}
print join("\t",@fields),"\n";
}
输入示例:
file name owner last modified last accessed
text4.txt root 12.73 13.53
text5.txt user3 15.3333 34
file6.txt root 903709792518875002.42857142857142857142 903709792518875002
file7.txt root 6659166111488656281486807152009765625 539422123247359763587428687890625
输出:
file name owner last modified last accessed
text4.txt root 383.11 388.71
text5.txt user3 401.33 532.00
file6.txt root 6325968547632125311.00 6325968547632125308.00
file7.txt root 46614162780420593970407650064068359669.00 3775954862731518345112000815234669.00