2010-02-12 70 views
1

這是我想要做的:使用Perl,如何從兩個可能的記錄分隔符的文件中讀取記錄?

我想讀取一個文本文件到一個字符串數組。我希望字符串在文件讀入某個字符時終止(主要是;|)。

例如,下面的文本

Would you; please 
hand me| my coat?

將放好這樣的:

$string[0] = 'Would you;'; 
$string[1] = ' please hand me|'; 
$string[2] = ' my coat?'; 

我能得到這樣的幫助嗎?

+2

爲什麼'|'變成'!'? – 2010-02-12 02:08:21

+0

請注意,如上所示,您還希望從輸入中刪除換行符。 – darch 2010-02-12 22:48:02

回答

3

一種方式是注入其他字符,像\n,只要您的特殊字符被發現,然後split\n

use warnings; 
use strict; 
use Data::Dumper; 

while (<DATA>) { 
    chomp; 
    s/([;|])/$1\n/g; 
    my @string = split /\n/; 
    print Dumper(\@string); 
} 

__DATA__ 
Would you; please hand me| my coat? 

打印出:

$VAR1 = [ 
      'Would you;', 
      ' please hand me|', 
      ' my coat?' 
     ]; 

更新:原來的問題James提出的輸入文本顯示在單行上,如上面的__DATA__所示。因爲這個問題格式不好,其他人編輯了這個問題,把1行分成2個。只有詹姆斯知道1行還是2行是有意的。

+1

不錯的答案。注意行's /([; |])/ $ 1 \ n/g;',$ 1在輸出中包含括號內的匹配模式(「()」)。 – mctylr 2010-02-12 02:40:37

+0

這引入\ n作爲第三個記錄分隔符。 – darch 2010-02-12 17:52:03

+0

@darch:首先,唯一的'\ n'被'chomp'移除。其次,使用's // // g'爲每個特殊字符注入'\ n'。第三,所有注入的'\ n'都被'split'移除。如果您發現此方法存在問題,請詳細說明。這是解決原始問題中提出的問題的一種方法。 – toolic 2010-02-12 18:02:51

0

東西沿着

$text = <INPUTFILE>; 

@string = split(/[;!]/, $text); 

行應該做的伎倆更多或更少。

編輯:我已將「/;!/」更改爲「/ [;!] /」。

+0

如果你打算這樣做,我認爲你必須使用某種形式的「啜食」文件,因爲通常會逐行處理它。例如,請參閱http://www.perl.com/pub/a/2003/11/21/slurp.html – 2010-02-12 02:18:51

+0

不要忘記執行's/\ n// g'。 – 2010-02-12 02:19:13

+0

這完全不能像OP想要的那樣工作。試一試。它在2個字符的序列';!'上分開,這在輸入中不會出現。 – toolic 2010-02-12 02:27:12

1

我更喜歡@toolic's answer,因爲它很容易處理多個分隔符。

但是,如果你想過於複雜的事情,你總是可以嘗試:

#!/usr/bin/perl 

use strict; use warnings; 

my @contents = (''); 

while (my $line = <DATA>) { 
    last unless $line =~ /\S/; 
    $line =~ s{$/}{ }; 
    if ($line =~ /^([^|;]+[|;])(.+)$/) { 
     $contents[-1] .= $1; 
     push @contents, $2; 
    } 
    else { 
     $contents[-1] .= $1; 
    } 
} 

print "[$_]\n" for @contents; 

__DATA__ 
Would you; please 
hand me| my coat? 
0

讓Perl的爲你做了一半的工作通過設置$/(輸入記錄分隔符),以豎條,然後提取分號分隔的字段:

#!/usr/bin/perl 

use warnings; 
use strict; 

my @string; 

*ARGV = *DATA; 

$/ = "|"; 
while (<>) { 
    s/\n+$//; 
    s/\n/ /g; 
    push @string => $1 while s/^(.*;)//; 
    push @string => $_; 
} 

for (my $i = 0; $i < @string; ++$i) { 
    print "\$string[$i] = '$string[$i]';\n"; 
} 

__DATA__ 
Would you; please 
hand me| my coat? 

輸出:

$string[0] = 'Would you;'; 
$string[1] = ' please hand me|'; 
$string[2] = ' my coat?';
+0

+1好的和有效的方法。 – 2010-02-12 09:56:48

6

這將做到這一點。在保留要分割的令牌的同時使用拆分的技巧是使用零寬度的回溯匹配:split(/(?<=[;|])/, ...)

注意:mctylr的答案(當前最高評分)實際上並不正確 - 它將在新行上拆分字段,b/c只能在文件的單行上一次處理。

gbacon的回答使用輸入記錄分隔符($/)非常聰明 - 既省時又省時 - 但我不認爲我想在生產代碼中看到它。在記錄分隔符中放置一個分割標記,在分割中放入另一個分割標記,這有點不明顯(你必須用Perl來對抗...))這將使它難以維護。我也不確定他爲什麼要刪除多個換行符(我認爲你並不需要這麼做?),以及爲什麼他只是爲了'|' - 終止記錄而這樣做。

# open file for reading, die with error message if it fails 
open(my $fh, '<', 'data.txt') || die $!; 

# set file reading to slurp (whole file) mode (note that this affects all 
# file reads in this block) 
local $/ = undef; 

my $string = <$fh>; 

# convert all newlines into spaces, not specified but as per example output 
$string =~ s/\n/ /g; 

# split string on ; or |, using a zero-width lookback match (?<=) to preserve char 
my (@strings) = split(/(?<=[;|])/, $string); 
+0

我的解決方案(mctylr響應)對於原始問題中提供的輸入是正確的。後來修改了這個問題,改變了輸入。在我看來,這個問題是不明確的:OP是想爲整個文件使用單個數組還是爲每個文件行使用數組?詹姆斯應該澄清。 – toolic 2010-02-12 13:34:52

+0

糟糕,對不起,工具,你是對的 - 我的意思是你的回覆,不是mctylr的!在對輸入進行更改後,我進來了。 使用回溯匹配的一個限制是,您的代碼沒有:回溯匹配必須是恆定寬度。它在這裏工作得很好 - ;和|都只是1個字符 - 但是如果詹姆斯有一個寬度不同的標記,比如說他想分離的「//」,那麼你就是技術(用單一的標記代替不同的標記)會更好。 – curveship 2010-02-12 15:01:44

相關問題