2013-06-20 138 views
1

我是新來的perl,最近有以下問題。在Perl中的正則表達式由括號括起來

我有一個格式爲「$ num1 $ num2 $ num3 $ num4」的字符串,那$ num1,$ num2,$ num3,$ num4是實數可以是一個科學數字或常規格式。

現在我想使用正則表達式從字符串中提取4個數字。

$real_num = '\s*([+-]?[0-9]+\.?[0-9]*([eE][+-]?[0-9]+)?)' 
while (<FP>) { 
    if (/$real_num$real_num$real_num$real_num/) { 
     print $1; print $2; print$3; print$4; 
    } 
} 

我怎樣才能得到$ NUM1,NUM2 $,$ NUM3,$ num4從$ 1,$ 2,$ 3,$ 4〜由於$ real_num正則表達式中有必要的支架,所以$ 1,$ 2,$ 3,$ 4不是我現在所期望的。

感謝所有熱烈的回覆,非捕獲組是我需要的答案!

+0

HTTP://計算器。COM /問題/ 638565 /解析,科學的表示法,合理 – 2013-06-20 06:16:37

+0

哦如何在4號分開嗎?你可能分裂和遍歷它們 – 2013-06-20 06:18:59

+0

你的意思是括號'()'當你說括號(這是'[]')?反正它是不是「必要的」,就可以使括號非捕獲詳細的[羅希特的答案](http://stackoverflow.com/a/17206530/1743811)。 – doubleDown

回答

5

只需使用非捕獲組,請$real_num正則表達式,使正則表達式本身捕獲組:

$real_num = '\s*([+-]?[0-9]+\.?[0-9]*(?:[eE][+-]?[0-9]+)?)' 

現在的問題是:/$real_num$real_num$real_num$real_num/會很容易失敗,如果有超過4個號碼的開出那裏。可能現在情況並非如此。但是,你也應該照顧。 A 分割將是一個更好的選擇。

+0

謝謝,這解決了我的問題! – SpectreV

+0

點擊「打勾」圖標,然後! :-D – Massa

+0

這種失敗的輸入作爲.3簡單,除了你提到的問題。例如,「9 2.3 6 7 8」將返回3個6 7 8 –

3

如果您確信您的線路包含數字,那麼就可以避免正則表達式,使用split功能:

while (<FP>) { 
    my @numbers = split /\s+/; #<-- an array with the parsed numbers 
} 

如果您需要壽檢查,如果提取的字符串是真正的號碼,使用Scalar::Util looks_like_number。例如:

use strict; 
use warnings; 
use Scalar::Util qw/looks_like_number/; 

while(<DATA>) { 
    my @numbers = split /\s+/; 
    @numbers = map { looks_like_number($_) ? $_ : undef } @numbers; 
    say "@numbers"; 
} 


__DATA__ 
1 2 NaN 4 -1.23 
5 6 f 8 1.32e12 

打印:

1 2 NaN 4 -1.23 
5 6 8 1.32e12 
+1

爲什麼沒有人意識到這代碼將產生有關使用未初始化的警告鰺連接或字符串中的值是否存在一個非數字的數據片段?原則上你的回答並不差,但你至少應該知道使用grep而不是像這樣的工作。 –

+0

我正在運行perl 5.18,並且您所說的警告沒有出現。無論如何,這段代碼試圖展示一個想法;在這種情況下,更好實施的具體細節不是重點。 –

+0

實際上,即使在您自己的DATA上運行程序時,也會出現一條警告。在第二行中,「f」會顯示警告。只要使用grep而不是map,你的解決方案就可以正常工作,即'grep {looks_like_number($ _)} @ numbers',但由於使用了較慢的look_like_number庫子例程,它仍然會變慢。 –

1

兩個重要問題的答案將影響你是否甚至需要使用正則表達式來匹配不同的數字格式,或者如果你可以做一些簡單得多:

  1. 你確定你的行只包含數字還是還包含其他數據(或者可能某些行根本沒有數字,只有其他數據)?
  2. 您是否確定所有數字都至少被一個空格相互隔開和/或其他數據?如果不是,他們怎麼分開? (例如,從portsnap fetch輸出產生大量的數字像這樣3690 .... 3700 ....用小數點和所有沒有空格用來分隔它們。

如果你的行只包含數字並沒有其他的數據和數字之間用空格隔開,那麼你甚至不需要檢查結果是否數字,但只開行拆分:

my @numbers = split /\s+/; 

如果你不知道你的行包含數字,但你確定每個數字與其他數字或其他數據之間至少有一個空格,那麼下一行代碼就是一個qui利用聰明的方式讓Perl自己識別所有不同的合法數字格式,這是一種很好的提取數字的好方法。(假定您不想將其他數據值轉換爲NaN。)@numbers中的結果將正確識別當前輸入行中的所有數字。

my @numbers = grep { 1*$_ eq $_ } m/(\S*\d\S*)/g; 
# we could do simply a split, but this is more efficient because when 
# non-numeric data is present, it will only perform the number 
# validation on data pieces that actually do contain at least one digit 

您可以確定是否至少有一個數字是目前通過檢查表達@numbers > 1的真值,如果正好四個存在使用條件@numbers == 4

如果你的號碼顛簸例如5.17e + 7-4.0e-1,那麼你將會遇到更困難的時間。這是唯一一次你需要複雜的正則表達式。

注意:更新的代碼更快/更好。注意2:由於存儲undef的值時map的工作方式的細微之處,最高位答案有一個問題。當使用該程序從第一行數據(如HTTP日誌文件)中提取數字時,可以通過該程序的輸出來說明。輸出看起來是正確的,但該陣列實際上有許多空的元素,並且如預期那樣找不到存儲在$numbers[0]中的第一個數字。事實上,這是完整的輸出:

$ head -1 http | perl prog1.pl 
Use of uninitialized value $numbers[0] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[1] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[2] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[3] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[4] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[5] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[6] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[7] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[10] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[11] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[12] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[13] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[14] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[15] in join or string at prog1.pl line 8, <> line 1. 
Use of uninitialized value $numbers[16] in join or string at prog1.pl line 8, <> line 1. 
     200 2206 

(請注意這些數字的壓痕顯示許多空數組元素是如何出現在@numbers,並已通過空間的實際數字之前連接在一起,當陣列已經轉換爲字符串)

但是,我的解決方案在視覺上和實際數組內容中產生了正確的結果,即$ numbers [0],$ number [1]等實際上是第一個和第二個數據文件行中包含的數字。

while (<>) { 
my @numbers = m/(\S*\d\S*)/g; 
@numbers = grep { $_ eq 1*$_ } @numbers; 
print "@numbers\n"; 
} 

$ head -1 http | perl prog2.pl

此外,使用慢庫函數使得較慢的另一溶液運行50%。在10,000行數據上運行程序時,輸出是相同的。

0

我以前的回答沒有解決非空格分隔號碼的問題。這在我看來需要單獨的答案,因爲輸出可能與相同的數據有很大不同。

my $number = '([-+]?(?:\d+\.\d+|\.\d+|\d+)(?:[Ee][-+]\d+)?)'; 

my $type = shift; 

if ($type eq 'all') { 

while (<>) { 
my @all_numbers = m/$number/g; 
# finds legal numbers whether space separated or not 
# this can be great, but it also means the string 
# 120.120.120.120 (an IP address) will return 
# 120.120, .120, and .120 
print "@all_numbers\n"; 
} 

} else { 
while (<>) { 
my @ss_numbers = grep { m/^$number$/ } split /\s+/; 
# finds only space separated numbers 
print "@ss_numbers\n"; 
} 
} 

用法:

$ prog-jkm2.pl all < input # prints all numbers 
$ prog-jkm2.pl < input # prints just space-separated numbers 

的OP可能需要的唯一代碼:

my $number = '(-?(?:\d+\.\d+|\.\d+|\d+)(?:[Ee][-+]\d+)?)'; 
my @numbers = grep { m/^$number$/ } split /\s+/; 

在這一點上,$numbers[0]將是第一個數字,$numbers[1]是第二個號碼等

輸出的210個

實例:

$ head -1 http | perl prog-jkm2.pl 
200 2206 
    $ head -1 http | perl prog-jkm2.pl all 
67.195 .114 .38 19 2011 01 20 31 -0400 1 1 1.0 200 2206 5.0