2009-09-29 48 views
2

我正在寫一個Perl腳本運行通過,並抓住各種數據元素,如:如何從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 

我可以抓住這個文本文件沒有問題的每一行。

我有工作正則表達式來抓住每個這些領域。一旦我在變量中有行,例如$ line - 我如何獲取這些字段中的每一個,並將它們放入自己的變量中,即使它們具有不同的分隔符?

+1

你能更具體一點關於你的分隔符是什麼嗎? –

+0

@Paul:這就是問題所在。此輸出由應用程序(splunk)生成。我無法設置分隔符,並且它們似乎只是每列之間的兩個空格字符 - 但是有空白字符延伸過去。 – Greg

+3

@Greg:這實際上看起來不像「分隔」文件。這些是固定寬度的列。下面的FM解決方案實際上是解析這個問題的最乾淨的方式。 –

回答

-1

您可以分割線。看起來你的分隔符只是空格?您可以按以下順序執行操作:

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

這將匹配所有空格。然後,您可以執行邊界檢查並通過$ line [0],$ line [1]等訪問每個字段。

Split也可以採用正則表達式而不是字符串作爲分隔符。

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

這可能會做同樣的事情。

+5

我認爲他正在談論固定寬度編碼。 –

+0

使用該方法 - 偉大工程,輸出: 時間:1253592000 Livereporter: 跨度: Bcreporter: 時間:1253678400 Livereporter:86400 跨度:6183.000000 Bcreporter: 時間:1253764800 Livereporter:86400 跨度:4486.000000 Bcreporter: 時間:1253851200 Livereporter:36.000000 跨度:86400 Bcreporter:10669.000000 時間:1253937600 Livereporter:0.000000 跨度:86400 Bcreporter:9126.000000 時間:1254024000 Livereporter:0.000000 跨度:86400 Bcreporter:2930.000000 時間:1254110400 Livereporter:0.000000 跨度:86400 Bcreporter:2895.000000 時間:1254196800 Livereporter:0.000000 跨度:8828.000000 – Greg

+1

你不能分割空白,因爲有些字段是空的。當你這樣做時,你會失去列順序。 –

-2

固定寬度界定可以做這樣的:

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 


} 

我的Perl是很生疏,所以我相信有語法錯誤在那裏。但這是它的要點。

+1

你爲什麼這樣ch??你認爲這是什麼打印?有關線索,請參閱chomp的文檔。不要太吝嗇,但如果你猜測並展示一些你從未嘗試或甚至沒有跑過的東西,你應該等待一個更有經驗的人來回答。 –

-1

如果所有字段具有相同固定寬度,用空格的格式,你可以使用下面的split

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

其中N是與現場的。這將爲每個空白字段生成一個空間。

+0

我不認爲這是你所想的。在這一行中有兩個主要的錯誤:一個在邏輯上,另一個在語法上。 –

+0

@brian d foy:謝謝,修正。對於低質量的答案抱歉。無論如何,'解包'解決方案更好。 –

13

此示例說明如何使用空格作爲分隔符(split)或使用固定列布局(unpack)解析該行。如果使用大寫字母(A10等),則unpack將爲您刪除空格。 注意:正如brian d foy指出的那樣,split方法對於缺少字段的情況(例如第二行數據)不適用,因爲字段位置信息將丟失;除非我們誤解了您的數據,否則要走到這裏去。

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

對於解包,給定樣本數據的佈局 – Hobo

+3

每個人都會忘記Perl已經打包,但它確實非常方便,我應該更多地使用它自己。我只是編輯下一版Effective Perl Programming的那一章。 :) –

+1

'split m'\ s +''會突出顯示更好。 –

0

我不能確定的列名和格式的,但你應該使用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

使用my module DataExtract::FixedWidth能夠將此配方調整自己的喜好。這是功能最全面,經過充分測試的,適用於perl中的固定寬度列。如果這個速度不夠快,你可以傳入一個unpack_string,並且不需要啓發式檢測邊界。

#!/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

我以前使用過這個模塊,列檢測是光滑的。 – Demosthenex

相關問題