2011-07-16 33 views
1

,我寫需要做到這一點的程序:最佳方式是在Perl定義(0和「」的定義)

  • 讀取文件的每一行
  • 如果行包含一個有序對(x,y)的存儲有序對
  • 所述下一個有序對之前,將會有一個行的文件的,與「結果」處的,所述端
    • 存儲中的有序對開始行作爲「值」和「錯誤」
  • 打印出對應X,Y,值,誤差以CSV格式
  • 讀下一個(X,Y)的值等,在(X,Y)線和(值,誤差)線將替代文件

這不是一項家庭作業。正如你所看到的,我已經有了17行代碼。我想知道是否可以使用更少的行或更簡潔的代碼完成此任務,同時至少保持此版本具有的可讀性級別,並且保持Perl樣式(例如包含和第一個可執行行之間的換行符)。

,我有至少激動的線是

if (defined($x) && defined($y) && defined($val) && defined($err)) 

有沒有更好的方式做一個斷言照顧交替數據的文件嗎?如果我不使用defined()函數,那麼程序不會按預期運行,因爲某些x和y座標是0值。

#!/usr/bin/perl 
use strict; 

print "X,Y,Val\n"; 
foreach (@ARGV){ 
    open log,$_ or die $!; 
    my ($x,$y,$val,$err); 
    while(<log>){ 
     chomp; 
     ($x,$y) = ($1,$2) if (/\((\d*|-\d*),(\d*|-\d*)\)/); 
     ($val,$err) = ($1,$2) if (/^Results.*\((.*),(.*)\)$/); 
     if (defined($x) && defined($y) && defined($val) && defined($err)){ 
      print "$x,$y,$val:$err\n"; 
      ($x,$y,$val,$err) = undef; 
     } 
    } 
} 

謝謝大家的答案,我正在學習很多新的Perl語法。 我已經想出瞭如何將這個腳本降至10行。我在自己寫這篇文章的行數上挑戰自己。

#!/usr/bin/perl 
use strict; 

print "X,Y,Val\n"; 
open LOG,"<@ARGV[0]" or die $!; 
while(<LOG>){ 
    chomp; 
    print "$1,$2," if (/\((\d*|-\d*),(\d*|-\d*)\)/); 
    print "$1:$2\n" if (/^Results.*\((.*),(.*)\)$/); 
} 

另一個更新。使用答案中的信息,我能夠將其降至8行。我還改進了正則表達式,並確保只有在提供多個文件時纔會打印標題。

#!/usr/bin/perl 
use strict; 

while(<>){ 
    print "X,Y,Val\n" if ($. == 1); 
    print "$1,$2," if (/.*\((-?\d+),(-?\d+)\)/); 
    print "$1:$2\n" if (/^Results.*\((.*)\).*\((.*)\)$/); 
} 
+0

聽起來像您可以將輸入記錄分隔符設置爲'結果',然後搜索每個記錄的x,y值。 – DavidO

+0

@DavidO很遺憾,如果「結果」是正則表達式篩選出的行之一中的有效字符串,那麼這將不起作用。如果文件很大並且座標線和結果線之間有很長的距離,它也可能導致內存問題。 –

+1

如果你提到的標準是可能性,那麼這是行不通的,絕對可能是這種情況......也可能不是。 ;)除非我們看到我們無法確定的數據樣本。無論如何,如果不是這個練習,這是一般性的考慮;我們通常會過分關注換行符分隔的記錄,當通過新鮮眼光查看數據集時可能會發現更方便的記錄分隔符,以方便更簡單的數據刪除。 – DavidO

回答

1

我會切換到讀兩行,而不是一個:

#!/usr/bin/perl 

use strict; 
use warnings; 

use autodie; 

print "X,Y,Val\n"; 
for my $filename (@ARGV) { 
    open my $log, "<", $filename; 

    while (my $coord_line = <$log>) { 
     my ($x, $y) = $coord_line =~ /\((-?[0-9]+),(-?[0-9])\)/ 
      or die "bad coored line"; 
     my $results_line = <$log>; 
     my ($val,$err) = $results_line =~ /^Results.*\((.*),(.*)\)$/ 
      or die "bad results line"; 

     print "$x,$y,$val:$err\n"; 
    } 
} 

一個這種方法的好處是,你的變量現在可以正常範圍的。這項計劃的一個簡單的版本是:

#!/usr/bin/perl 

use strict; 
use warnings; 

use ARGV::readonly; #prevent files like "|ls" from breaking us 

print "X,Y,Val\n"; 
while (<>) { 
    my ($x, $y) = /\((-?[0-9]+),(-?[0-9]+)\)/ 
     or die "bad coored line"; 
    my ($val,$err) = <> =~ /^Results.*\((.*),(.*)\)$/ 
     or die "bad results line"; 

    print "$x,$y,$val:$err\n"; 
} 

另一種變型,考慮到我們所關心的兩行之間的線的可能性。它假定第一座標對是正確的:

#!/usr/bin/perl 

use strict; 
use warnings; 

use ARGV::readonly; #prevent files like "|ls" from breaking us 

print "X,Y,Val\n"; 
while (<>) { 
    next unless my ($x, $y) = /\((-?[0-9]+),(-?[0-9]+)\)/; 
    my ($val, $err); 
    while (<>) { 
     last if ($val, $err) = /^Results.*\((.*),(.*)\)$/; 
    } 
    die "bad format" unless defined $val; 
    print "$x,$y,$val:$err\n"; 
} 

而這一次處理這樣的情形,你想要的最後一個座標線的情況下:

你可以做
#!/usr/bin/perl 

use strict; 
use warnings; 

use ARGV::readonly; #prevent files like "|ls" from breaking us 

print "X,Y,Val\n"; 
my ($x, $y); 
while (<>) { 
    ($x, $y) = ($1, $2) if /\((-?[0-9]+),(-?[0-9]+)\)/; 
    next unless my ($val, $err) = /^Results.*\((.*),(.*)\)$/; 
    print "$x,$y,$val:$err\n"; 
} 
+0

我得到了一個印象,那就是相關標籤之間可能存在不相關的線條。如果是這樣,「結果」一行跟隨x,y線並不確定。 – TLP

+0

@TLP然後添加第二個while循環很簡單,該循環搜索直到找到「Results」行。 –

+0

@TLP,是的,這是正確的。這些文件超過100,000行,其中大部分與此數據無關。 – OregonTrail

0

一個改進剛剛打開@ARGV文件直接如下。獲取四個目標變量的值時,您還可以跳過if語句。您可以將檢查和模式匹配與if-else分開,以節省一些處理,並限制$val$err的範圍。

此外,您不需要chomp,因爲您不使用行或行結尾。

不知道它有多大幫助,但它是一些東西。

use warnings; 
use strict; 

my ($x,$y); 
while (<ARGV>) { 
    if (defined $x && defined $y) { 
     my ($val,$err) = /^Results.*\((.*),(.*)\)$/; 
     if (defined $val && defined $err) { 
      print "$x,$y,$val:$err\n"; 
      ($x,$y) = undef; 
     } 
    } else { 
      ($x,$y) = /\((\d*|-\d*),(\d*|-\d*)\)/; 
    } 
} 
+0

爲什麼使用ARGV手柄?只要說'<>'。 –

+0

如果你有一個像'| ls'這樣的文件名,你將會對結果感到驚訝(提示,它在引擎蓋下使用了兩個arg版本的'open')。 –

+0

我沒有看到打開'| ls'的危害。你什麼意思會發生? – TLP

1

我是一個可讀性強,而不是簡潔的支持者。 Perl非常擅長優化代碼,所以你不必擔心它。不要過分擔心行數,並保持代碼的可讀性。在CPU時間中保存的任何東西(如果保存任何東西)將浪費在試圖維護難以閱讀的程序所產生的時間和錯誤上。

在這方面:

  • 不要使用後綴後if語句,除非它的東西很簡單,如next if (s/^\s*$/);
  • 使用變量名稱並不取決於$_
  • 在逗號後面使用空格。

最重要的是,我想補充:

  • 不要害怕,如果他們幫助澄清你在做什麼加括號。我傾向於使用括號如果函數有兩個以上的參數只是爲了幫助持有參數一起:

例如:

open my $foo, "<", $bar or die qq(This is the end!\n); 

open (my $foo, "<", $bar) or die qq(This is the end!\n); 

它現在比較明顯的哪一部分該行的參數是open函數中的參數。

,我用最少的興奮行:

if (defined($x) && defined($y) && defined($val) && defined($err)){ 

有什麼問題這條線?這很清楚你想說什麼。我會用更多的現代語法,並添加了一些括號,以幫助重組,以使其更清晰:

if ((defined $x) and (defined $y) and (defined $val) and (defined $err)) { 

看着你在做什麼,我會重新安排事情有點...

#! /usr/bin/env perl 

use strict; 
use warnings; 
use features qw(say); 

say "X, Y, Val"; 

for my $filename (<>) { 
    open (my $log, "<", $filename) or die $!; 

    my ($x, $y, $value, $err); 
    while (chomp (my $coord_line = <$log>)) { 
     if ($coord_line =~ /\((-?[0-9]+),(-?[0-9])\)/) { 
      ($x, $y) = ($1, $2); 
     } 
     elsif ($coord_line =~ /^Results.*\((.*),(.*)\)$/) { 
      ($val, $err) = ($1, $2); 
      say "$x, $y, $val:$err"; 
     } 
    } 
} 

}

注意我現在只是檢查一行。而且,請注意,我在打印結果時無需檢查是否設置了所有變量。

另請注意,您不需要ARGV::readonly,因爲您在open函數中使用了兩個以上的參數。在這種情況下,打開文件ls|不會導致任何問題。只有在open語句中只有兩個參數時纔會出現問題。

上述程序假定您只有座標和結果或垃圾線。但是,如果您有多個座標,您只需要第一組座標,您需要跟蹤它們。我建議使用一個單獨的變量爲了這個目的,你可以使用常量來幫助澄清你在做什麼:

#! /usr/bin/env perl 

use strict; 
use warnings; 
use features qw(say); 

use autodie; 

use constants { 
    SET  => 1, 
    NOT_SET => 0, 
}; 

say "X, Y, Val"; 

for my $filename (<>) { 
    if (not open my $log, "<", $filename) { 
     warn qq(Cannot open file "$filename": $!); 
     next; 
    } 

    my ($x, $y, $value, $err); 
    my $coordinates = NOT_SET; 
    while (my chomp($coord_line = <$log>)) { 
     if ($coord_line =~ /\((-?[0-9]+),(-?[0-9])\)/) { 
      if ($coordinates == NOT_SET)) { 
       ($x, $y) = ($1, $2); 
       $coordinates = SET; 
      } 
     } 
     elsif ($coord_line =~ /^Results.*\((.*),(.*)\)$/) { 
      ($val, $err) = ($1, $2); 
      say "$x, $y, $val:$err"; 
      $coordinates = NOT_SET; 
     } 
    } 
} 

通過使用if/elsif語句,你現在檢查每行只有一次。它還讓用戶知道每條線是座標線還是結果線,並且一條線不是兩條線。在你的原始程序中,你正在檢查每一行,因此不清楚單行是否可以同時存在。

如果文件無法打開,我也不會死。相反,我會打印一條警告並繼續下一條。你可以做任何一種方式。 (我第一次去世,但第二次繼續前進)。

順便說一下,您的偏好是否可以組合前兩個if語句而不是嵌套。我也有誰不喜歡使用數字常量,因爲它的方式很容易說的朋友:

如果($座標= SET){

而不是

如果($座標== SET ){

如果你有這樣的:

use constants { 
    SET  => "set", 
    NOT_SET => "", 
}; 

你會得到使用,以這樣做:

if ($coordinates eq SET) { 

並且不碰到===問題。

相關問題