2013-12-10 16 views
1

我的問題是雙重的:Perl的拆分與額外的正則表達式組合

場景:我生成一個使用標準的逗號分隔符一個CSV日誌文件。目前CSV文件中有四個「列」,但未來可能會添加更多。我有興趣將REGEX搜索/替換限制爲特定的數據列(例如,COL 2)。

第2列中的數據因響應設備而異。最終,我使用REGEX將條件值「浮動」爲浮點數,保留兩位小數。一些條目帶有各種僞像(例如,額外的字母或其他表示),我使用REGEX轉換爲適當的浮點表示。所有的REGEXs現在都適用於我的目的,但我希望將這些REGEX限制在第二列(COL 2)中的數據中,所以如果我將來添加類似格式的列(例如浮點數),它們不受第2列REGEX的影響。

我認爲這樣做的一種方式是使用拆分並將列數據保存到標量變量中,然後通過包含REGEX的循環或子例程運行COL2標量,然後將條件化的COL2值重新寫回到一個新的CSV文件。 (我目前做這與Perl的$^I變量)。

$^I = ".org"; 

while (<>) { 
    my ($col1, $col2, $col3, $col4) = (split /,/); 
    $col2 =~ s/EXP1/FORMATTED/; 
    $col2 =~ s/EXP2/FORMATTED/; 
    $col2 =~ s/EXP3/FORMATTED/; 

    my $new_rec = join ",", $col1,$col2,$col3,$col4; 

    print $new_rec; 
} 

所以問題1:我不知道這是否足夠有效,或者如果我做不必要的工作?我是否可以在REGEX中特別添加逗號,使其僅適用於COL2?

問題2:針對re:split(SPLIT Question)的問題,brian d foy對Text :: CSV_XS說了如下:「速度非常快,這就是爲什麼我說」非常優化「的原因。它也能正確處理CSV,而不是分割。「

如果這是準確的,意思分裂不能正確處理CSV文件,我的上述解決方案將工作一段時間?我不確定他分裂的意思是不能正確處理CSV。

+2

CSV允許逗號成爲數據的一部分,以及分隔符。通常,數據中的逗號位於引號內。 CSV解析器將忽略引號中的逗號,但「split」不會。 – toolic

回答

0

這取決於您的數據從何而來。如果你是某些該字段將永遠不會被引用,那麼split是好的,並且是最好的解決方案。

下面是我將如何編寫代碼。 for循環在塊的範圍內暫時別名$_$fields[1],並允許您在未明確提及該變量的情況下操縱該值。輸出發送到STDOUT

use strict; 
use warnings; 

while (<>) { 
    chomp; 

    my @fields = split /,/; 
    for ($fields[1]) { 
    s/EXP1/FORMATTED/; 
    s/EXP2/FORMATTED/; 
    s/EXP3/FORMATTED/; 
    } 

    print join(',', @fields), "\n"; 
} 
+0

這回答我的原始問題。正如你所提到的,我保證第二列不會被引用,也不會包含現場逗號。我喜歡使用$ _來訪問'@ fields'數組,這是一種乾淨的方式,只關注我感興趣的列。我確實有關於你的解決方案的第二個問題......如果CSV文件的每一行都以換行符結尾,我可能只是不會扼殺該行並放棄將新行附加到聯接語句,對嗎?也就是說,這可能是更好的編碼練習,以重新編寫並重新添加換行符。感謝您的解決方案。 – secJ

1

迴應什麼toolic提到,只有split CSV數據有風險,因爲您可能錯誤地將split作爲列值。鑑於此,這裏有一個Text::CSV_XS選項:

use strict; 
use warnings; 
use Text::CSV_XS; 

my $csv = Text::CSV_XS->new({ binary => 1, auto_diag => 1 }) 
    or die "Cannot use CSV: " . Text::CSV->error_diag(); 
my $sepChar = $csv->sep_char(); 

open my $fh, "<:encoding(utf8)", $ARGV[0] or die "$ARGV[0]: $!"; 
while (my $row = $csv->getline($fh)) { 
    $row->[2] =~ s/this/that/; 
    print join ',', map { /$sepChar/ ? qq{"$_"} : $_ } @$row; 
} 

$csv->eof or $csv->error_diag(); 
close $fh; 

命令行用法:>perl script.pl inFile [>outFile]

最後,可選參數輸出定向到一個文件中。

請注意,您可以在col 2上完成所有s ///。map包含對數組元素中分隔符(通常爲逗號)的檢查。如果存在,則用雙引號括起來 - 以維護CSV格式。

希望這會有所幫助!

編輯:

既然你一定有你的CSV字段沒有逗號,你真的不需要split數據,做了換人,重新組合數據,然後print它。您可以設置一個散列,其中的鍵/值對是替換中使用的匹配/替換對。然後,只需使用正則表達式來捕捉COL2值替換:

use strict; 
use warnings; 

my %hash = (1 => '1.00', 'unk' => '0.00'); 

while (<DATA>) { 
    s/^(?:.+?,)\K([^,]+)/exists $hash{$1} ? $hash{$1} : $1/e; 
    print; 
} 

__DATA__ 
12345,1,342,789.0 
47.42,unk,17.6,12 
17,34,12.5,0 

輸出:

12345,1.00,342,789.0 
47.42,0.00,17.6,12 
17,34,12.5,0 
+0

@toolic和Kenosis,使用拆分的CSV單元格的潛在不需要的拆分是有意義的。感謝澄清。關於[Text :: CSV_XS]的使用(http://search.cpan.org/~hmbrand/Text-CSV_XS-1.02/CSV_XS.pm)上面的例子應該很好。假設'$ row [2]實際上是$ row [1](行中的第二個元素)',並且多個REGEX可以內聯地顯示在同一個變量上......'$ row [1] - > =〜s/1/1.00 /;'和'$ row [1] - > =〜s/unk/0.00 /;'etc ...是否正確? – secJ

+0

@ user3000685 - 非常歡迎您!是的,'$ row - > [1]'表示訪問第二個數組元素。請回顧[Borodin](http://stackoverflow.com/users/622310/borodin)的出色答案。我的意思並不是要讓分裂的CSV數據看起來*明顯不好。 – Kenosis

+0

再次感謝您的回覆。我將來無疑會使用這種技術。這裏使用了很多強大的Perl。在這個特例中,鮑羅廷的解決方案更加適用,因爲我不必擔心在我正在評估的領域內容納額外引用的逗號。不過,我也從你那裏學到了一些新東西。謝謝。 – secJ