テキストファイルの数値フィールドのサイズを変更する方法

テキストファイルの数値フィールドのサイズを変更する方法

<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::Utillooks_like_number()perlには、フィールドが数値であるかどうかを検出するために使用できる便利な関数を持つモジュール(v5.8以降のperlに含まれています)があります。

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

関連情報