2009-12-12 15 views
9

La programmazione è così nuova per me che mi scuso per non sapere come esprimere la domanda.Come posso dividere colonne a larghezza fissa in Perl?

Ho uno script Perl che ottiene una variabile da uno strumento interno. Questo non è sempre quello che sembra, ma sarà sempre seguire questo modello:

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, qual è il modo più semplice per ottenere ciascuna di queste linee in una variabile di per sé? A seconda di cosa sputa lo strumento interno ogni riga sarà sempre diversa e ci possono essere più di cinque righe. La lettera maiuscola in ogni riga è ciò che finirà per essere ordinata per (tutti Come, tutti i C, tutti gli Es, ecc.). Dovrei guardare espressioni regolari?

+0

dove sono questi dati/linee? Il tuo strumento interno le mette in una singola variabile? O sono questi dati di testo in un file che devi leggere? –

+0

Lo strumento li mette in una singola variabile. – scraft3613

+0

Ci sono novizi Perl !!! 1 – nes1983

risposta

17

Mi piace usare unpack per questo genere di cose. È veloce, flessibile e reversibile.

Hai solo bisogno di conoscere le posizioni per ogni colonna, e unpack può tagliare automaticamente gli spazi bianchi extra da ogni colonna.

Se si cambia qualcosa in una delle colonne, è facile andare pacchetto al formato originale reimballaggio con lo stesso 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 

Ora, c'è una considerazione ulteriore. Sembra che tu possa avere questo grosso pezzo di testo multilinea in una singola variabile. Gestirlo come se fosse un file aprendo un filehandle su un riferimento allo scalare. La roba filehandle si prende cura di tutto il resto:

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

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

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

Anche un formato di "A23 A8 A7 A *" funzionerebbe. –

+3

Un bel esempio di Perl leggibile ... (anche a un utente di una volta ogni due anni) – Rook

+0

Non sono sicuro del formato che hai visto perché ho commesso un errore nella prima che ho postato, ma alla fine allo stesso formato. –

3
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 
0

Per ogni riga di testo o meno così:

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

utilizzare una matrice per la sentenza in quanto non è chiaro se la frase alla fine avrà spazi o meno. è quindi possibile unire l'array @text in una nuova stringa, se necessario. Se le frasi alla fine non avranno spazi, puoi convertire @text in $ text.

+0

Se in questo caso utilizzerai la divisione, utilizza il terzo argomento per limitare il numero di elementi restituiti. Se quest'ultima colonna ha uno spazio bianco significativo, perderai parte dei dati. –

2

Supponendo che il testo viene messo in un unico $ info variable, allora si può dividere in righe separate utilizzando la funzione di perl scissione intrinseca:

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

dove @linee è un array delle linee. "\ N" è la regex per una nuova riga. È possibile scorrere ogni riga come segue:

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

È quindi possibile dividere ogni riga su spazi bianchi (regex \ s +, dove il \ s è un carattere di spazio bianco, e il + significa 1 o più volte):

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

e si può quindi accedere a ogni campo direttamente tramite il suo indice di matrice: $ campo [0], $ campo [1] ecc

o, si può fare:

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

che metterà i campi in ogni riga in variabili con nome separate.

ora - se si vuole sorta le linee dal carattere nella terza colonna, si potrebbe fare questo:

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

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

Ora avete un "array di array". Questo può essere facilmente risolto come segue:

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

che ordinare @arr dal terzo elemento (indice 2) del @Campi.

Edit 2 Per mettere le linee con la stessa terza colonna nelle proprie variabili, fare questo:

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 
} 

ora avete un hash digitato con la terza personaggi delle colonne, con il valore di ogni essere chiave le linee che contengono quella chiave. È quindi possibile scorrere l'hash e stampare o utilizzare in altro modo i valori hash.

+0

Se si intende utilizzare split in questo caso, utilizzare il terzo argomento per limitare il numero di elementi restituiti. Se quest'ultima colonna ha uno spazio bianco significativo, perderai parte dei dati. –

+0

Grazie Richard: ogni riga deve essere raggruppata in lettere maiuscole. A seconda dell'output di quella query potrei avere un massimo di 20 righe o un minimo di 2 righe. Le linee con "C" devono entrare in una variabile, le righe con "B" devono andare nella loro variabile, ecc. Funzionerà? – scraft3613

+0

utilizzando la funzione di ordinamento nella mia risposta sopra, l'array verrà ordinato alfanumerico. quindi appariranno "A" prima, "B" e così via. Se vuoi mettere tutte le linee "A" in una singola variabile, c'è (come ogni problema di programmazione) un numero di possibilità. Puoi usare un hash/mappa con chiave, con i caratteri "A" ecc. Come chiave, con il valore sia a) una serie di linee o b) un singolo singolo a cui aggiungi le linee successive come li trovi. Vedi here per un tutorial sull'uso degli hash. –

-1

Usare CPAN, e il mio modulo 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 

Non ottiene molto più semplice di quello.

Problemi correlati