2010-04-06 62 views

回答

15

這可以使用positive lookahead來完成:

perl -pe 's/(.)(?=.*?\1)//g' FILE_NAME 

中使用的正則表達式是:(.)(?=.*?\1)

  • .:匹配任何炭。
  • 第一():記住匹配 單個字符。
  • (?=...):+已經先行
  • .*?:以匹配
  • 之間
  • \1什麼:記住的比賽。
  • (.)(?=.*?\1):匹配並記住 任何字符只有它再次出現 字符串中以後。
  • s///:做 替代的Perl的方式。
  • g:做替代 全球...這是後 第一個換人不停止。
  • s/(.)(?=.*?\1)//g:這將從 刪除輸入字符串 中的字符,只有當該字符在字符串中再次出現 時纔會出現。因爲在輸入字符串中的每個獨特的字符

這將保持在輸入字符的順序,我們保留其最後發生,而不是第一

要保持相對順序不變,我們可以做什麼KennyTM講述了一個評論:

  • 反向輸入線
  • 做替換像以前
  • 扭轉結果打印

這條Perl的一行是:

perl -ne '$_=reverse;s/(.)(?=.*?\1)//g;print scalar reverse;' FILE_NAME 

由於我們在逆轉後手動執行print,我們不使用-p標誌,而是使用-n標誌。

我不確定這是否是最好的單線程來做到這一點。如果他們有更好的選擇,我歡迎其他人編輯這個答案。

+2

訂單已更改(例如「EFAHU」) - 是否重要。 – 2010-04-06 06:59:34

+0

@Gavin:可以通過最初反轉字符串來修復,並在更換後反轉字符串。 – kennytm 2010-04-06 07:28:04

+2

這是驚人的! 但你能解釋一下我的細節,比如 什麼===> s /(。)和(?=。*?\ 1)//正在做什麼? 也有可能有我已經在我的早期查詢中提出的相同的順序,例如。目前我得到的是EFAHU而不是EFUAH,這更有幫助。 Thnax一噸:) – manu 2010-04-06 08:24:50

0

包含你列出一個名爲foo.txt的

python -c "print set(open('foo.txt').read())" 
+2

套在Python中沒有訂單......並且他想要Perl .. – ghostdog74 2010-04-06 06:27:46

+0

他原來的文章沒有指定perl作爲需求(儘管他標記爲perl),只是指出他找到了perl作爲一種可能的方式來做到這一點。他也沒有說重要,只有唯一性。另外,使用單線表示該方法並不重要。 – jkyle 2010-04-06 15:53:11

1

領帶:: IxHash數據的文件是一個很好的模塊,用於存儲哈希順序(但可能是緩慢的,則需要基準,如果速度是重要的)。例如與測試:

use Test::More 0.88; 

use Tie::IxHash; 
sub dedupe { 
    my $str=shift; 
    my $hash=Tie::IxHash->new(map { $_ => 1} split //,$str); 
    return join('',$hash->Keys); 
} 

{ 
my $str='EFUAHUU'; 
is(dedupe($str),'EFUAH'); 
} 

{ 
my $str='EFUAHHUU'; 
is(dedupe($str),'EFUAH'); 
} 

{ 
my $str='UJUJHHACDEFUCU'; 
is(dedupe($str),'UJHACDEF'); 
} 

done_testing(); 
3
perl -ne'my%s;print grep!$s{$_}++,split//' 
+0

這也正在工作,比前一個更短。我被這個迴應所淹沒:) 我想知道它的工作如果可能的話。 – manu 2010-04-06 09:27:43

+0

它的工作方式與gianthare解決方案相同,但更加慣用的Perl和更快。 – 2010-04-07 05:50:35

+0

一個不錯的,我同意。除了'my%s'外,幾乎只有一行。雖然我看不出加速是從哪裏來的。可能來自新的哈希表而不是重置?或者grep比顯式循環更有效率? – 2010-04-08 09:43:11

0

從貝,這個工程:

sed -e 's/$/<EOL>/ ; s/./&\n/g' test.txt | uniq | sed -e :a -e '$!N; s/\n//; ta ; s/<EOL>/\n/g' 

在口頭上:標記每個換行符用<EOL>字符串,然後把每一個人物在自己的行,然後使用uniq刪除重複的行,然後刪除所有換行符,然後放回換行符而不是<EOL>標記。

,我發現在一個論壇帖子的-e :a -e '$!N; s/\n//; ta一部分,我不明白的單獨-e :a部分或$!N部分,因此,如果任何人都可以解釋這些,我會很感激。

嗯,那個只有連續重複;消除所有重複,你可以這樣做:

cat test.txt | while read line ; do echo $line | sed -e 's/./&\n/g' | sort | uniq | sed -e :a -e '$!N; s/\n//; ta' ; done 

,雖然使人物在每一行按字母順序排列。

1

這看起來像積極lookbehind的經典應用程序,但不幸的是perl不支持。事實上,我認爲這樣做(將字符串中的前一個字符與長度不確定的完整正則表達式匹配)只能用.NET正則表達式類來完成。

然而,正向前查找支持完整的正則表達式,因此,所有你需要做的是反向的字符串,應用正前瞻(如unicornaddict說):

perl -pe 's/(.)(?=.*?\1)//g' 

和扭轉回來,因爲沒有相反的是」只會將重複的字符保留在一行中的最後一個地方。

MASSIVE編輯

我已經花了最後半在這一個小時,這看起來像這樣的作品,沒有倒車

perl -pe 's/\G$1//g while (/(.).*(?=\1)/g)' FILE_NAME 

我不知道是否感到自豪或恐懼。我基本上是做正looakahead,然後用指定的\ G替換字符串 - 這使正則表達式引擎從匹配的最後一個匹配的位置開始匹配(由pos()變量內部表示)。

隨着測試輸入這樣的:

aabbbcbbccbabb

EFAUUUUH

ABCBBBBD

DEEEFEGGH

AABBCC

輸出是這樣的:

ABC

EFAUH

ABCD

DEFGH

ABC

認爲它的工作...

說明 - 好吧,如果我的解釋最後一次是不夠清晰 - 超前會去停在重複變量的最後一場比賽[在代碼中,你可以做一個print pos();在循環內部檢查]和s/\ G // g將會刪除它[你確實不需要/ g]。因此在循環中,替換將繼續刪除,直到所有這樣的重複都被刪除。當然,這對你的口味來說可能有點過於集中處理,但大多數基於正則表達式的解決方案也是如此。不過,倒車/前瞻方法可能會比這更有效率。

+2

更確切地說,它是*可變長度* lookbehinds Perl不支持。除.NET之外,它們還受到JGSoft(EditPad Pro,PowerGrep)的支持,並且受到Java的限制。 – 2010-04-06 11:51:24

+0

編輯並添加新的解決方案。不知道它是否是全面的咖啡因...太多的咖啡因。 :-P – 2010-04-06 13:44:45

1

如果設定可能遇到的字符被限制,例如只有字母,那麼最簡單的解決方案將與tr
perl -p -e 'tr/a-zA-Z/a-zA-Z/s'
它將自行替換所有字母,其他字符不會受到影響和/ s修飾符將擠壓重複出現的同一字符(替換後),從而刪除重複項

我壞 - 它只消除相鄰的外觀。無視

4

這裏是一個解決方案,我認爲應該比前瞻工作更快,但不是基於正則表達式的並使用散列表。

perl -n -e '%seen=();' -e 'for (split //) {print unless $seen{$_}++;}' 

它分割的每一行轉換爲字符和僅打印所述第一外觀通過計數內部%外觀可見散列表

0
use strict; 
use warnings; 

my ($uniq, $seq, @result); 
$uniq =''; 
sub uniq { 
    $seq = shift; 
    for (split'',$seq) { 
    $uniq .=$_ unless $uniq =~ /$_/; 
    } 
    push @result,$uniq; 
    $uniq=''; 
} 

while(<DATA>){ 
    uniq($_); 
} 
print @result; 

__DATA__ 
EFUAHUU 
UUUEUUUUH 
UJUJHHACDEFUCU 

輸出:

EFUAH 
UEH 
UJHACDEF 
4

如果Perl是不是必須的,你也可以使用awk。這裏是針對awk發佈的Perl一行內容的有趣基準。 awk對於具有3百萬++行的文件要快10秒以上

$ wc -l <file2 
220 

$ time awk 'BEGIN{FS=""}{delete _;for(i=1;i<=NF;i++){if(!_[$i]++) printf $i};print""}' file2 >/dev/null 

real 1m1.761s 
user 0m58.565s 
sys  0m1.568s 

$ time perl -n -e '%seen=();' -e 'for (split //) {print unless $seen{$_}++;}' file2 > /dev/null 

real 1m32.123s 
user 1m23.623s 
sys  0m3.450s 

$ time perl -ne '$_=reverse;s/(.)(?=.*?\1)//g;print scalar reverse;' file2 >/dev/null 

real 1m17.818s 
user 1m10.611s 
sys  0m2.557s 

$ time perl -ne'my%s;print grep!$s{$_}++,split//' file2 >/dev/null 

real 1m20.347s 
user 1m13.069s 
sys  0m2.896s 
+0

+1,不錯的工作:) – codaddict 2010-04-08 07:42:33

+0

我很驚訝正則表達式解決方案的速度有多快 – 2010-04-08 09:47:06