2009-09-29 7 views
2

J'écris un script Perl pour exécuter à travers et saisir divers éléments de données tels que:Comment puis-je extraire des colonnes à partir d'un format à largeur fixe en Perl?

1253592000 
1253678400     86400     6183.000000 
1253764800     86400     4486.000000 
1253851200 36.000000  86400    10669.000000 
1253937600 0.000000  86400     9126.000000 
1254024000 0.000000  86400     2930.000000 
1254110400 0.000000  86400     2895.000000 
1254196800 0.000000        8828.000000 

Je peux saisir chaque ligne de ce fichier texte sans problème. Je travaille sur regex pour saisir chacun de ces champs. Une fois que j'ai la ligne dans une variable, c'est-à-dire $ line - comment puis-je saisir chacun de ces champs et les placer dans leurs propres variables même s'ils ont des délimiteurs différents?

+1

Pourriez-vous être un peu plus précis sur ce que sont vos délimiteurs? –

+0

@Paul: C'est le problème. Cette sortie est générée par une application (splunk).Je ne peux pas définir les délimiteurs, et ils semblent être juste deux caractères d'espacement entre chaque colonne - mais il y a des espaces qui dépassent cela. – Greg

+3

@Greg: Cela ne ressemble vraiment pas du tout à un fichier "délimité". Ce sont des colonnes à largeur fixe. La solution de FM ci-dessous est vraiment la façon la plus propre d'analyser cela. –

Répondre

-1

Vous pouvez diviser la ligne. Il semble que votre délimiteur est juste un espace? Vous pouvez faire quelque chose de l'ordre de:

@line = split(" ", $line); 

Ceci correspondra à tous les espaces. Vous pouvez ensuite vérifier les bornes et accéder à chaque champ via $ line [0], $ line [1], etc.

Split peut également prendre une expression régulière plutôt qu'une chaîne comme délimiteur.

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

Ceci pourrait faire la même chose.

+5

Je pense qu'il parle de codage à largeur fixe. –

+0

utilisé cette méthode - fonctionne très bien, sortie: Heure: 1253592000 Livereporter: Période: Bcreporter: Heure: 1253678400 Livereporter: 86400 Span: 6183,000000 Bcreporter: Heure: 1253764800 Livereporter: 86400 Span: 4486,000000 Bcreporter: Heure: 1253851200 Livereporter: 36,000000 Span: 86400 Bcreporter: 10669,000000 Heure: 1253937600 Livereporter: 0,000000 Span: 86400 Bcreporter: 9126,000000 Heure: 1254024000 Livereporter: 0,000000 Span: 86400 Bcreporter: 2930,000000 Heure: 1254110400 Livereporter: 0,000000 Span: 86400 Bcreporter: 2895,000000 Heure: 1254196800 Livereporter: 0,000000 Span: 8828,000000 – Greg

+1

Vous ne peut pas se diviser sur les espaces car certains champs sont vides. Vous perdez l'ordre des colonnes lorsque vous faites cela. –

-2

délimitant la largeur fixe peut être fait comme ceci:

my @cols; 
my %header; 
$header{field1} = 0; // char position of first char in field 
$header{field2} = 12; 
$header{field3} = 15; 

while(<IN>) { 

    print chomp(substr $_, $header{field2}, $header{field3}); // value of field2 


} 

Mon Perl est très rouillée Je suis sûr qu'il ya des erreurs de syntaxe là. mais c'est l'essentiel.

+1

Pourquoi êtes-vous chomping comme ça? Et que pensez-vous que cela imprime? Voir la documentation pour chomp pour un indice. Pour ne pas être trop méchant à ce sujet, mais si vous devinez et montrez quelque chose que vous n'avez jamais essayé ou même courez, vous devriez attendre qu'une personne plus expérimentée réponde. –

-1

Si tous les champs ont la même largeur fixe et sont mis en forme avec des espaces, vous pouvez utiliser les éléments suivants split:

@array = split/{1,N}/, $line; 

N est le avec du terrain. Cela donnera un espace pour chaque champ vide.

+0

Je ne pense pas que cela fasse ce que vous pensez. Il y a deux erreurs majeures dans cette ligne: une en logique et une en syntaxe. –

+0

@brian d foy: merci, corrigé. Désolé pour une réponse de mauvaise qualité. Quoi qu'il en soit, la solution 'unpack' est bien meilleure. –

13

Cet exemple illustre comment analyser la ligne avec des espaces comme délimiteur (split) ou avec une disposition de colonne fixe (unpack). Avec unpack si vous utilisez des majuscules (A10 etc), les espaces seront supprimés pour vous. Note: comme le souligne brian d foy, l'approche split ne fonctionne pas bien pour une situation avec des champs manquants (par exemple, la deuxième ligne de données), car les informations de position du champ seront perdues; unpack est le chemin à parcourir ici, à moins que nous ne comprenions mal vos données.

use strict; 
use warnings; 

while (my $line = <DATA>){ 
    chomp $line; 
    my @fields_whitespace = split m'\s+', $line; 
    my @fields_fixed = unpack('a10 a10 a12 a28', $line); 
} 

__DATA__ 
1253592000             
1253678400     86400     6183.000000 
1253764800     86400     4486.000000 
1253851200 36.000000  86400    10669.000000 
1253937600 0.000000  86400     9126.000000 
1254024000 0.000000  86400     2930.000000 
1254110400 0.000000  86400     2895.000000 
1254196800 0.000000        8828.000000 
+0

+1 pour décompresser, compte tenu de la disposition des données de l'échantillon – Hobo

+3

Tout le monde oublie que Perl a pack, mais c'est vraiment pratique et je devrais l'utiliser plus moi-même. Je venais d'éditer ce chapitre pour la prochaine édition de Effective Perl Programming. :) –

+1

'split m '\ s +'' serait mieux mis en évidence. –

0

Je ne suis pas sûr des noms de colonnes et la mise en forme, mais vous devriez être en mesure d'ajuster cette recette à votre goût en utilisant Text::FixedWidth

use strict; 
use warnings; 
use Text::FixedWidth; 

my $fw = Text::FixedWidth->new; 
$fw->set_attributes(
    qw(
     timestamp undef %10s 
     field2 undef %10s 
     period undef %12s 
     field4 undef %28s 
     ) 
); 

while (<DATA>) { 
    $fw->parse(string => $_); 
    print $fw->get_timestamp . "\n"; 
} 

__DATA__ 
1253592000 
1253678400     86400     6183.000000 
1253764800     86400     4486.000000 
1253851200 36.000000  86400    10669.000000 
1253937600 0.000000  86400     9126.000000 
1254024000 0.000000  86400     2930.000000 
1254110400 0.000000  86400     2895.000000 
1254196800 0.000000        8828.000000 
3

Utilisez my module DataExtract::FixedWidth. C'est le plus complet, et bien testé, pour travailler avec des colonnes Fixed Width en Perl. Si ce n'est pas assez rapide, vous pouvez passer un unpack_string et éliminer le besoin de détection heuristique des limites.

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

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

say join ('|', @{$de->parse($_)}) for @rows; 

    --alternatively if you want header info-- 

my @rows = <DATA>; 
my $de = DataExtract::FixedWidth->new({ 
    heuristic => \@rows 
    , header_row => undef 
    , cols => [qw/timestamp field2 period field4/] 
}); 

use Data::Dumper; 
warn Dumper $de->parse_hash($_) for @rows; 

__DATA__ 
1253592000 
1253678400     86400     6183.000000 
1253764800     86400     4486.000000 
1253851200 36.000000  86400    10669.000000 
1253937600 0.000000  86400     9126.000000 
1254024000 0.000000  86400     2930.000000 
1254110400 0.000000  86400     2895.000000 
1254196800 0.000000        8828.000000 
+0

J'ai utilisé ce module dans le passé et la détection de colonne est lisse. – Demosthenex

Questions connexes