Como ajustar campos numéricos em um arquivo de texto

5

Suponha que eu tenha o seguinte arquivo de texto separado <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

Este arquivo consiste em várias "tabelas", cada uma inicia com uma linha de cabeçalho e, em seguida, contém algumas linhas de dados. Algumas colunas são numéricas, mas cada tabela pode ter um número diferente, bem como diferentes tipos de colunas. Os tipos de colunas não são conhecidos antecipadamente e não podem ser determinados de acordo com o cabeçalho da tabela.

Os valores numéricos na tabela usam vários formatos - pode haver números inteiros, números decimais de ponto flutuante ou números em notação científica.

A minha pergunta é como converter todos os campos numéricos nesta tabela no mesmo formato. Por exemplo, talvez eu queira ter cada campo numérico formatado com um especificador de formato "%.2f" printf. Naturalmente, os outros campos não numéricos devem permanecer inalterados.

Além disso, gostaria de poder ajustar arbitrariamente (por exemplo, adicionar 42 e multiplicar por 7) todos os campos numéricos contidos neste arquivo.

A solução que estou procurando deve ser baseada em campo. Ele deve verificar o arquivo inteiro e, para cada campo, deve determinar se é numérico ou não. Se for numérico, deverá imprimir seu valor ajustado e formatado. Caso contrário, deve imprimir apenas o original.

Eu sei que algo assim pode ser feito com awk . Mas, se bem me lembro, awk usa double para representação interna de números e, portanto, pode ter problemas com precisão e valores maiores. Então, idealmente, eu gostaria de usar outra coisa, algo que deveria manipular corretamente pelo menos 64 bits inteiros.

Existe alguma maneira simples de conseguir isso?

    
por Peter Bašista 27.07.2012 / 02:47

1 resposta

1

perl tem um módulo chamado Scalar::Util (incluído no perl desde v5.8) que tem uma função útil chamada looks_like_number() , que pode ser usada para detectar se um campo é um número ou não.

looks_like_number não é perfeito, mas é muito bom.

O esboço simples de um programa perl simples para fazer o que você quer pode ser algo como isto:

#! /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";
}

Se você receber dados de amostra acima como entrada, imprime isto:

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

Aqui está outra versão do script que usa o Math :: BigFloat para todos os cálculos, arredondando decimais para 2 dígitos.

#! /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";
}

exemplo de entrada:

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

saída:

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
    
por 27.07.2012 / 12:07