2013-03-19 118 views
3

我有一個源字符串,可能包含任何字符,包括空格,回車符和換行符(控制字符)。控制字符可能出現在任何地方,包括單詞的中間。如何執行忽略控制字符的文本搜索?

我有一個搜索字符串可能與源字符串具有相同的字符選擇,但通常是源的子字符串。此搜索字符串中控制字符的順序和數量可能與源不同。

當搜索字符串中的非控制字符匹配時,我需要從源字符串中刪除包含字符串中任何控制字符的字符串。源字符串中其他位置的控制字符不應被刪除。

我的計劃是在搜索字符串中的每個字符之後加上\s*。這很好,但是我需要在搜索字符串中轉義任何正則表達式特殊字符,否則它們將被視爲正則表達式命令而不是它們真正的純文本。

我可以在每個字符('mytext.scan(/./).join("\\s*")')後添加\s*,但是如何才能轉義特殊字符但不能插入正則表達式代碼?如果我這樣做,那麼我可以逃避正則表達式特殊字符,但是我不能簡單地在每個字符後面加上\s*;我需要避免逃脫的角色。

爲了清楚 控制字符=空間或\噸或\ R或\ n或\˚F

編輯:改性第三段改善的我的要求清楚

+0

製作源字符串和搜索字符串的副本。消除兩個副本中的所有控制字符。在源字符串的副本中搜索搜索字符串的副本。如果您需要(或強調刪除,或...),您也可以進行大小寫轉換。使用大量'\ s *'可能會大大減慢你的正則表達式。 – 2013-03-20 00:01:47

+0

@Jonathan Leffler但是,你如何重做原始字符串上的替換? – Patashu 2013-03-20 00:02:30

+0

搜索字符串只需要被複制和預處理一次。源字符串需要每次都被複制和預處理。如果最壞的情況出現了,當你知道有匹配的時候,你可以回到原始的源字符串,並且創建一個新的搜索字符串副本,以便在每個常規字符之間有's * *' ,並將第二個(殘缺的)搜索字符串副本中的正則表達式應用於原始源字符串。因爲你知道有匹配,所以即使匹配失敗模式太慢,性能也應該是合理的。 – 2013-03-20 00:08:26

回答

0

天真的方法是

1)分割搜索字符串成(單個字符的列表中的每個字符串)

2)消毒每個單獨的字符(仍然是一個字符串列表)

3)\s* *

*加入列表除了\s*將無法​​正常工作,順便說一句 - \s*將匹配0或多個空白,這是不一樣的0個或多個控制字符。見http://www.regular-expressions.info/posixbrackets.html#class,並使用的,在你的正則表達式的味道:)

\W*作品「控制字符」的形式可能工作太,因爲\W是任何字符不是在A-ZA-Z0-9_。但我從來沒有測試過,看看是否匹配控制字符或只能打印字符。

+0

這是一個很好的解決方案,錯過了簡單的一個,呵呵!額外的處理不是一個大問題。我將讀取並驗證用於匹配的正確正則表達式。感謝您的快速回復 – user2188711 2013-03-20 00:13:45

+0

'\ w'相當於'[[:alnum:] _]'不是'[a-zA-Z0-9]' – dbenhur 2013-03-20 03:01:25

+0

@dbenhur你說得對,我忘了_ – Patashu 2013-03-20 03:09:13

0

或多或少在評論中討論:

使源字符串和搜索字符串的副本。消除兩個副本中的所有控制字符。在源字符串的副本中搜索搜索字符串的副本。如果您需要(或強調刪除,或...),您也可以進行大小寫轉換。使用大量的\s*可能會顯着減慢你的正則表達式。

搜索字符串只需要被複制和預處理一次。每個源字符串也需要被複制和預處理一次。如果最糟糕的是,當你知道有匹配的時候,你可以返回到原始的源字符串,並創建一個新的搜索字符串副本,這樣你就可以在每個常規字符之間找到類似\s*的東西,並應用從搜索字符串的第二個(殘缺的)副本到原始源字符串的正則表達式。因爲你知道有匹配,所以即使匹配失敗模式太慢,性能也應該是合理的。

下面是討論的思想的Perl實現。

#!/usr/bin/env perl 
use strict; 
use warnings; 
use Data::Dumper; 

$Data::Dumper::Useqq = 1; 

my $source = "'Twas (Tweedle-Dee's)\fBirthday\n\n\f\f\nand\ta\tl\tl\this friends were happy\n"; 
my $search = "(\fTwee\ndle\t-\tDee\r'\rs)\nBi\frth\fday"; 

print Data::Dumper->Dump([$source], [qw($source)]); 
print Data::Dumper->Dump([$search], [qw($search)]); 

my $c_source = $source; 
my $c_search = $search; 

$c_source =~ s/ |[[:cntrl:]]//g; # Or s/\s//g; 
$c_search =~ s/ |[[:cntrl:]]//g; # Or s/\s//g; 

print Data::Dumper->Dump([$c_source], [qw($c_source)]); 
print Data::Dumper->Dump([$c_search], [qw($c_search)]); 

if ($c_source =~ m/\Q$c_search\E/) 
{ 
    # Locating the search in the original source...hard work... 
    my @a_search = split //, $c_search; 
    printf "Lengths: c_search %d; a_search %d\n", length($c_search), scalar(@a_search); 

    @a_search = map { s/[][\\.*?+(){}]/\\$&/g; $_ } @a_search; # Escape regex metacharacters 
    #print Data::Dumper->Dump([\@a_search], [qw(@a_search)]); 
    my $r_search = join "\\s*", @a_search; 
    print Data::Dumper->Dump([$r_search], [qw($r_search)]); 

    my $t_source = $source; 
    $t_source =~ s/$r_search//g; 
    print Data::Dumper->Dump([$t_source], [qw($t_source)]); 
} 

好乾淨的象形文字的樂趣 - 清晰如泥,毫無疑問。前三行檢查是否沒有任何愚蠢的錯誤。 Data::Dumper模塊明確地打印數據;它在那裏進行調試。變量Useqq調整數據的打印方式。

變量$source$search是源字符串和搜索字符串。儘管所有控制角色都在其中,但還是有匹配的。請注意,混合中有一些正則表達式元字符 - 圓括號是正則表達式元字符。這些字符串被轉儲以供參考。

接下來的兩行創建搜索和源字符串的副本。控制字符和空格被刪除,使用基於POSIX的正則表達式類指定所有控制字符。這些轉換後的字符串被轉儲以供檢查。

if語句將轉換的源與轉換的搜索進行比較。 \Q...\E部分在兩者之間壓制正則表達式元字符的含義。如果有匹配,那麼我們在花括號中輸入代碼塊。

split操作從轉換後的搜索字符串中創建單個字符數組。 printf檢查智慧。 map操作會用反斜槓和元字符替換每個正則表達式元字符,並保持其他字符不變。 join將數組@a_search中的每個字符或字符對收集爲一個字符串$r_search\s*分隔數組條目。

變量$t_source是源的另一個副本。 $r_search的正則表達式應用於$t_search,任何匹配都被替換爲無。結果被傾倒。從這個腳本的輸出是:

​​

字符串$t_source確實對應$source與「(特威德爾-Dee的)生日」去除,這似乎符合要求。

把這個轉換成Ruby是對感興趣的讀者的自虐感的一個練習。顯然,您可以簡單地創建並使用$r_search字符串作爲正則表達式,並將其直接應用於(副本)$source;它會工作。但是我深信,如果將它應用於千字節長度的源字符串,代碼將運行得非常緩慢。我還沒有做過測量來證明它。

+0

感謝喬納森,在一些睡眠和非常完整的反應之後,我現在明白了你在原始答案中的意思。我將運行一些計時來查看ruby中的性能影響並在此處發佈。 – user2188711 2013-03-20 09:59:14