2010-06-07 68 views
2

我正在使用Perl來執行一些文件清理,並遇到一些性能問題。我的代碼的主要部分之一涉及標準化名稱字段。我有幾個部分看起來像這樣:Perl正則表達式 - 查找/替換的冷凝組

sub substitute_titles 
{ 
    my ($inStr) = @_; 
    ${$inStr} =~ s/ PHD./ PHD /; 
    ${$inStr} =~ s/ P H D/PHD /; 
    ${$inStr} =~ s/ PROF./ PROF /; 
    ${$inStr} =~ s/ P R O F/PROF /; 
    ${$inStr} =~ s/ DR./ DR /; 
    ${$inStr} =~ s/ D.R./ DR /; 
    ${$inStr} =~ s/ HON./ HON /; 
    ${$inStr} =~ s/ H O N/HON /; 
    ${$inStr} =~ s/ MR./ MR /; 
    ${$inStr} =~ s/ MRS./ MRS /; 
    ${$inStr} =~ s/ M R S/MRS /; 
    ${$inStr} =~ s/ MS./ MS /; 
    ${$inStr} =~ s/ MISS./ MISS /; 
} 

我通過引用傳遞嘗試並獲得至少有一點速度,但我擔心,運行這麼多(數以百計的)特定字符串的對數代替數千(最終可能有數十萬)記錄會損害性能。

有沒有更好的方式來實現這種邏輯比我目前做的是?

感謝

編輯:快速注意,不是所有的替換功能只是移除句點和空格。有串缺失,同音團體等

+2

如果你想匹配句點,你是不是錯過了一些反斜槓?請記住'.'是一個符合句點的元字符,但也包含任何其他內容。 – cjm 2010-06-07 22:13:42

+0

哎呀,好點。感謝您指出了這一點。 – brydgesk 2010-06-07 22:48:53

回答

5

這裏是一個要工作得很好,如果所有搜索項目的固定字符串的技術:

my %title_replacements = (
    ' PHD.' => ' PHD ', 
    ' P H D ' => ' PHD ', 
    # ..., 
); 

my $titles_to_replace = join '|', 
    map quotemeta, 
    keys %title_replacements; 

$titles_to_replace = qr/$titles_to_replace/; 

sub substitute_titles { 
    my ($in) = @_; 
    $$in =~ s/($titles_to_replace)/$title_replacements{$1}/g; 
} 

如果你在一個Perl運行早於5.10 .0或5.8.9,您應該考慮使用Regexp::TrieRegexp::Assemble來構建正則表達式,但是在當前的perls上,正則表達式編譯器會自動對這樣的大型列表進行優化,因此我省去了不必要的複雜因素。

5

不是分別運行的每個替代,創建一個閉包,可以做的工作對你更有效的方式:

sub make_translator { 
    my %table = @_; 
    my $regex = join '|' => map {quotemeta} keys %table; 
    $regex = qr/$regex/; 

    return sub {s/($regex)/$table{$1}/g} 
} 

my $translator = make_translator 
    ' PHD.' => ' PHD ', 
    ' P H D ' => ' PHD ', 
    ' PROF.' => ' PROF '; # ... the rest of the pairs 

my @list_of_strings = qw/.../; 

$translator->() for @list_of_strings; 

這是最快的,以不通過任何事情,使用$_別名爲數組值( for循環爲你做的)。

0

我很可能會爲我創建我的模式的子。這樣我所要做的就是傳入我想要標準化的標題數組。例如:

sub make_pattern { 
    my $list_ref = shift; 
    my %patterns; 
    for my $title (@{$list_ref}) { 
     my $result = uc $title; 
     my $pattern = '/' . join('\s*', (//, $title)) . '\.*/i'; 
     $patterns{$pattern} = $result; 
    } 
return \%patterns; 
} 

my @titles = qw (PHD MD DR PROF) #... plus whatever other titles you have 
my $conversion_hash = make_pattern(\@titles); 

然後你將得到的哈希結合在一些其他答案列在這裏。我還沒有時間來測試我的代碼,但它應該工作。