Pregunta ¿Cómo puedo separar las columnas de ancho fijo en Perl?


La programación es tan nueva para mí que me disculpo por no saber cómo formular la pregunta.

Tengo un script de Perl que obtiene una variable de una herramienta interna. Esto no siempre es lo que parece, pero siempre seguirá este patrón:

darren.local           1987    A      Sentence1
darren.local           1996    C      Sentence2
darren.local           1991    E      Sentence3
darren.local           1954    G      Sentence4
darren.local           1998    H      Sentence5

Con Perl, ¿cuál es la forma más fácil de obtener cada una de estas líneas en una variable en sí misma? Dependiendo de lo que escupe la herramienta interna, cada línea siempre será diferente y puede haber más de cinco líneas. La letra en mayúscula de cada línea es por lo que terminará ordenada (todas las As, todas las C, todas las Es, etc.). ¿Debería estar mirando expresiones regulares?


9
2017-12-12 15:57


origen


Respuestas:


Me gusta usar deshacer para este tipo de cosas Es rápido, flexible y reversible.

Solo necesita saber las posiciones para cada columna, y unpack puede recortar automáticamente el espacio en blanco adicional de cada columna.

Si cambia algo en una de las columnas, es fácil empacar el paquete al formato original reempacando con el mismo formato:

my $format = 'A23 A8 A7 A*';

while( <DATA> ) {
    chomp( my $line = $_ );

    my( $machine, $year, $letter, $sentence ) =
        unpack( $format, $_ );

    # save the original line too, which might be useful later
    push @grades, [ $machine, $year, $letter, $sentence, $_ ];
    }

my @sorted = sort { $a->[2] cmp $b->[2] } @grades;

foreach my $tuple ( @sorted ) {
    print $tuple->[-1];
    }

# go the other way, especially if you changed things
foreach my $tuple ( @sorted ) {
    print pack( $format, @$tuple[0..3] ), "\n";
    }

__END__
darren.local           1987    A      Sentence1
darren.local           1996    C      Sentence2
darren.local           1991    E      Sentence3
darren.local           1954    G      Sentence4
darren.local           1998    H      Sentence5

Ahora, hay una consideración adicional. Parece que podría tener este gran trozo de texto de varias líneas en una sola variable. Maneje esto como lo haría con un archivo abriendo un identificador de archivo en una referencia al escalar. La herramienta Filehandle se ocupa del resto:

 my $lines = '...multiline string...';

 open my($fh), '<', \ $lines;

 while( <$fh> ) {
      ... same as before ...
      }

17
2017-12-12 17:22



use strict;
use warnings;

# this puts each line in the array @lines
my @lines = <DATA>; # <DATA> is a special filehandle that treats
                    # everything after __END__ as if it was a file
                    # It's handy for testing things

# Iterate over the array of lines and for each iteration
# put that line into the variable $line
foreach my $line (@lines) {
   # Use split to 'split' each $line with the regular expression /s+/
   # /s+/ means match one or more white spaces.
   # the 4 means that all whitespaces after the 4:th will be ignored
   # as a separator and be included in $col4
   my ($col1, $col2, $col3, $col4) = split(/\s+/, $line, 4);

   # here you can do whatever you need to with the data
   # in the columns. I just print them out
   print "$col1, $col2, $col3, $col4 \n";
}


__END__
darren.local           1987    A      Sentece1
darren.local           1996    C      Sentece2
darren.local           1991    E      Sentece3
darren.local           1954    G      Sentece4
darren.local           1998    H      Sentece5

3
2017-12-12 16:23



Suponiendo que el texto se coloque en una sola variable $ info, entonces puede dividirlo en líneas separadas usando la función de división perl intrínseca:

my @lines = split("\n", $info); 

donde @lines es una matriz de tus líneas. El "\ n" es la expresión regular para una nueva línea. Puede recorrer cada línea de la siguiente manera:

foreach (@lines) {
   $line = $_;
   # do something with $line....  
}

A continuación, puede dividir cada línea en espacios en blanco (regex \ s +, donde el \ s es un carácter de espacio en blanco, y el + significa 1 o más veces):

@fields = split("\s+", $line);

y luego puede acceder a cada campo directamente a través de su índice de matriz: $ campo [0], $ campo [1] etc.

o, puedes hacer:

($var1, $var2, $var3, $var4) = split("\s+", $line);

que colocará los campos en cada línea en variables nombradas separadas.

Ahora, si quieres ordenar tus líneas por el personaje en la tercera columna, podrías hacer esto:

my @lines = split("\n", $info); 
my @arr = ();    # declare new array

foreach (@lines) {
   my @fields = split("\s+", $_);
   push(@arr, \@fields)    # add @fields REFERENCE to @arr 
}

Ahora tienes una "matriz de matrices". Esto se puede ordenar fácilmente de la siguiente manera:

@sorted = sort { $a->[2] <=> $b->[2] } @arr;

que ordenará @arr con el tercer elemento (índice 2) de @fields.

Editar 2 Para poner líneas con la misma tercera columna en sus propias variables, haga esto:

my %hash = ();             # declare new hash

foreach $line (@arr) {     # loop through lines
  my @fields = @$line;     # deference the field array

  my $el = $fields[2];     # get our key - the character in the third column

  my $val = "";
  if (exists $hash { $el }) {         # check if key already in hash
     my $val = $hash{ $el };        # get the current value for key
     $val = $val . "\n" . $line;    # append new line to hash value         
  } else {
     $val = $line;
  }
  $hash{ $el } = $val;         # put the new value (back) into the hash
}

Ahora tiene un hash en clave con los caracteres de la tercera columna, con el valor de cada tecla como las líneas que contienen esa clave. A continuación, puede recorrer el hash e imprimir o utilizar los valores hash.


2
2017-12-12 16:24



Para cada línea de texto, algo como esto:

my ($domain, $year, $grade, @text) = split /\s+/, $line;

Utilizo una matriz para la oración, ya que no está claro si la oración al final tendrá espacios o no. luego puede unir la matriz @text a una nueva cadena si es necesario. Si las oraciones al final no van a tener espacios, entonces puede convertir @text en $ text.


0
2017-12-12 16:23



Use CPAN y mi módulo DataExtract :: FixedWidth

#!/usr/bin/env perl
use strict;
use warnings;
use DataExtract::FixedWidth;

my @rows = <DATA>;

my $defw = DataExtract::FixedWidth->new({ heuristic => \@rows, header_row => undef });

use Data::Dumper;

print Dumper $defw->parse( $_ ) for @rows;

__DATA__
darren.local           1987    A      Sentence1
darren.local           1996    C      Sentence2
darren.local           1991    E      Sentence3
darren.local           1954    G      Sentence4
darren.local           1998    H      Sentence5

No es mucho más simple que eso.


-1
2017-09-28 18:29