2009-12-12 11 views
9

プログラミングはとても新しいので、質問のフレーズの仕方が分からないことをお詫びします。Perlで固定幅の列を分割するにはどうしたらいいですか?

私は内部ツールから変数を取得するPerlスクリプトを持っています。これは、それがどのように見えるかを常にではありませんが、それはいつもこのパターンに従います:Perlで

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 

、それ自体で変数にこれらの行のそれぞれを取得する最も簡単な方法は何ですか?内部ツールが吐き出すものによって、各ラインは常に異なり、5ライン以上が存在します。各行の大文字の文字は、すべてのAs、すべてのCs、すべてのEsなどでソートされることになります。私は正規表現を見ているべきですか?このようなテキスト何かの各行に対して

+0

これらのデータ/行はどこにありますか?あなたの内部ツールはそれらを1つの変数に入れますか?または、このテキストデータを読み込む必要のあるファイルですか? –

+0

ツールは、それらを単一の変数に置きます。 – scraft3613

+0

Perl初心者がいます!!! 1 – nes1983

答えて

17

私はこの種のもののためにunpackを使います。それは速く、柔軟で、可逆的です。

各列の位置を知るだけでよいので、unpackは各列の余分な空白を自動的にトリミングすることができます。あなたは、列のいずれかで何かを変更した場合

、それは同じフォーマットで再梱包して、元の形式にパックを行くのは簡単です:

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 

は今、追加の考慮事項があります。一つの変数の中にこのような大きな行の複数行のテキストがあるように思えます。スカラーへの参照でファイルハンドルを開くことでファイルと同じように扱います。それよりもはるかに簡単に取得していません

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

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

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

「A23 A8 A7 A *」の書式も有効です。 –

+3

読みやすいPerlの良い例(2年に一度のユーザーでも) – Rook

+0

私が投稿した最初のもので間違いを犯したためにあなたが見たフォーマットはわかりませんが、同じフォーマットで。 –

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

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

それははっきりしていないので、最後の文は、スペースを持っていたりしません場合、私は文のための配列を使用します。必要に応じて@text配列を新しい文字列に結合することができます。最後の文にスペースがない場合、@ textを$ textにすることができます。テキストは単一の変数$先に入れていると仮定すると

+0

で分割されることに注意してください。この場合splitを使用する場合は、3番目の引数を使用して、返される要素の数を制限してください。その最後の列に大きな空白があると、データの一部が失われます。 –

2

、あなたは本質的なPerlのsplit関数を使用して別の行に分割することができます:@linesは、あなたの行の配列である

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

を。 "\ n"は改行の正規表現です。あなたはそれぞれの行をループし、次のことができますよう:あなたはその後、空白の各ラインを分割することができます

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

(\ sが1つの空白文字で正規表現\ sの+、および意味+ 1回以上):

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

、あなたは、その配列のインデックスを経由して直接各フィールドにアクセスすることができます:$フィールド[0]、$フィールド[1]など

か、あなたが行うことができます:

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

これは、各行のフィールドを別々の名前付き変数に置きます。今

- あなたは3列目の文字でソートあなたのラインにしたい場合、あなたはこれを行うことができます:

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

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

今、あなたは「配列の配列」を持っているし。これは以下のように簡単に並べ替えることができる。

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

@fieldsの第3の要素(インデックス2)によって@arrソートれます。

編集2これを行う、自分の変数に同じ3番目の列を持つ行を置くために:

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 
} 

今、あなたは、各キービーイングのための値で、3列目の文字とキーハッシュを持っていますそのキーを含む行その後、ハッシュをループして出力したり、ハッシュ値を使用したりすることができます。

+0

この場合にsplitを使用する場合は、3番目の引数を使用して、返される要素の数を制限します。その最後の列に大きな空白があると、データの一部が失われます。 –

+0

リチャードに感謝 - 各行は大文字でグループ化する必要があります。そのクエリの出力に応じて、私は20行にも2行にもすることができます。 "C"の行は変数に入れる必要があり、 "B"の行は自分自身の変数などに入る必要があります。 – scraft3613

+0

上記の私の答えにsort関数を使用すると、配列は英数字でソートされます。 "A"が最初に表示され、 "B"が次のように表示されます。すべての "A"行を1つの変数に入れる場合は、(プログラミング上の問題のように)いくつかの可能性があります。 "A"などのキーをキーとしたキー付きハッシュ/マップを使用できます。その値は、a)行の配列、またはb)後続の行を見つけるときに追加する単一の文字です。ハッシュの使用に関するチュートリアルについては、hereを参照してください。 –

-1

使用CPAN、そして私のモジュール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 

:ファイルハンドルのものは残りの世話をします。