2010-01-31 56 views

回答

20

更快的方式做到這一點是使用str.translate() 這是〜快50倍,比用自己的方式

# You only need to do this once 
>>> title_trans=''.join(chr(c) if chr(c).isupper() or chr(c).islower() else '_' for c in range(256)) 

>>> "[email protected]%^".translate(title_trans) 
'abcde________' 

# Using map+lambda 
$ python -m timeit '"".join(map(lambda x: x if (x.isupper() or x.islower()) else "_", "[email protected]#$".strip()))' 
10000 loops, best of 3: 21.9 usec per loop 

# Using str.translate 
$ python -m timeit -s 'titletrans="".join(chr(c) if chr(c).isupper() or chr(c).islower() else "_" for c in range(256))' '"[email protected]#$".translate(titletrans)' 
1000000 loops, best of 3: 0.422 usec per loop 

# Here is regex for a comparison 
$ python -m timeit -s 'import re;transre=re.compile("[\W\d]+")' 'transre.sub("_","[email protected]#$")' 
100000 loops, best of 3: 3.17 usec per loop 

這是一種爲Unicode版本

# coding: UTF-8 

def format_title_unicode_translate(title): 
    return title.translate(title_unicode_trans) 

class TitleUnicodeTranslate(dict): 
    def __missing__(self,item): 
     uni = unichr(item) 
     res = u"_" 
     if uni.isupper() or uni.islower(): 
      res = uni 
     self[item] = res 
     return res 
title_unicode_trans=TitleUnicodeTranslate() 

print format_title_unicode_translate(u"Metallica Μεταλλικα") 

注意的是,希臘字母數作爲上限和下限,所以它們不被替代。 如果他們將被取代,簡單地改變條件

 if item<256 and (uni.isupper() or uni.islower()): 
+0

+1,非常好的主意。我能想到的唯一缺點是,如果必須考慮非ASCII字符,這將無法在Unicode字符串上正常工作。 –

+1

@Tim,unicode也有一個翻譯 - 雖然語義不同,但讓我看看我能否讓它工作... –

+0

@Tim,unicode版本已啓動。翻譯映射是根據需求構建的,因此當翻譯更多的字符串時,翻譯錯誤會越來越少。 –

17
import re 
title = re.sub("[\W\d]", "_", title.strip()) 

應該更快。

如果你想以單下劃線來代替相鄰的非字母的繼承,使用

title = re.sub("[\W\d]+", "_", title.strip()) 

,而不是它還要快。

我只是跑時間比較:

C:\>python -m timeit -n 100 -s "data=open('test.txt').read().strip()" "''.join(map(lambda x: x if (x.isupper() or x.islower()) else '_', data))" 
100 loops, best of 3: 4.51 msec per loop 

C:\>python -m timeit -n 100 -s "import re; regex=re.compile('[\W\d]+'); data=open('test.txt').read().strip()" "title=regex.sub('_',data)" 
100 loops, best of 3: 2.35 msec per loop 

這將工作在Unicode字符串,太(Python 3中,\W比賽這不是一個Unicode字任何字符下在Python 2中,你會必須另外爲此設置UNICODE標誌)。

+0

您使用timeit的方法是計算所有打開和讀取文件的時間。你應該將這些東西移到'-s'部分以獲得有意義的結果 –

+0

謝謝,你當然是對的。由於這是在兩個例子中完成的,所以這兩個錯誤應該是(而且我試過)都是一樣的。有趣的是,我發現預編譯正則表達式並沒有什麼不同。無論如何,我更新了時序示例。 –

+0

請注意,您不能使用re.sub;您必須使用re.compile()指定標誌並調用結果中的sub()。 (這是一個奇怪的API遺漏。)刪除「+」版本後答案會更好;這不是他要求的,所以這只是分散注意力。 –

2

相反的(x.isupper() or x.islower())你應該能夠使用x.isalpha()isalpha()方法可能會返回True'_'(我不記得它是否存在),但是您最終只會用'_'代替'_',因此不會造成任何損害。 (感謝您指出這一點,KennyTM。)

+0

實際上,它可能會將'_'本身計爲字母字符,所以也許不是。試試看看。 – MatrixFrog

+2

用'_'代替'_'(或不)是無害的。 – kennytm

1

因爲我自己的原因對此感到好奇我寫了一個快速腳本來測試這裏列出的不同方法,並刪除了我期望(不正確)會加快原始速度的lambda解。

簡短的說法是str.translate方法將其他方法吹走。正如上面所寫的那樣,正則表達式解決方案在接近秒的情況下是正確的。

這裏是我的測試程序:

import re 
from time import time 


def format_title(title): 
    return ''.join(map(lambda x: x if (x.isupper() or x.islower()) else "_", 
         title.strip())) 


def format_title_list_comp(title): 
    return ''.join([x if x.isupper() or x.islower() else "_" for x in 
        title.strip()]) 


def format_title_list_comp_is_alpha(title): 
    return ''.join([x if x.isalpha() else "_" for x in title.strip()]) 


def format_title_is_alpha(title): 
    return ''.join(map(lambda x: x if x.isalpha() else '_', title.strip())) 


def format_title_no_lambda(title): 

    def trans(c): 
     if c.isupper() or c.islower(): 
      return c 
     return "_" 

    return ''.join(map(trans, title.strip())) 


def format_title_no_lambda_is_alpha(title): 

    def trans(c): 
     if c.isalpha(): 
      return c 
     return "_" 

    return ''.join(map(trans, title.strip())) 


def format_title_re(title): 
    return re.sub("[\W\d]+", "_", title.strip()) 


def format_title_re_corrected(title): 
    return re.sub("[\W\d]", "_", title.strip()) 


TITLE_TRANS = ''.join(chr(c) if chr(c).isalpha() else '_' for c in range(256)) 


def format_title_with_translate(title): 
    return title.translate(TITLE_TRANS) 


ITERATIONS = 200000 
EXAMPLE_TITLE = "abc123def_$%^!FOO BAR*bazx-bif" 


def timetest(f): 
    start = time() 
    for i in xrange(ITERATIONS): 
     result = f(EXAMPLE_TITLE) 
    diff = time() - start 
    return result, diff 


baseline_result, baseline_time = timetest(format_title) 


def print_result(f, result, time): 
    if result == baseline_result: 
     msg = "CORRECT" 
    else: 
     msg = "INCORRECT" 
    diff = time - baseline_time 
    if diff < 0: 
     indicator = "" 
    else: 
     indicator = "+" 
    pct = (diff/baseline_time) * 100 
    print "%s: %0.3fs %s%0.3fs [%s%0.4f%%] (%s - %s)" % (
     f.__name__, time, indicator, diff, indicator, pct, result, msg) 


print_result(format_title, baseline_result, baseline_time) 

print "----" 

for f in [format_title_is_alpha, 
      format_title_list_comp, 
      format_title_list_comp_is_alpha, 
      format_title_no_lambda, 
      format_title_no_lambda_is_alpha, 
      format_title_re, 
      format_title_re_corrected, 
      format_title_with_translate]: 
    alt_result, alt_time = timetest(f) 
    print_result(f, alt_result, alt_time) 

而且這裏的結果:

format_title: 3.121s +0.000s [+0.0000%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
---- 
format_title_is_alpha: 2.336s -0.785s [-25.1470%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_list_comp: 2.369s -0.751s [-24.0773%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_list_comp_is_alpha: 1.735s -1.386s [-44.4021%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_no_lambda: 2.992s -0.129s [-4.1336%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_no_lambda_is_alpha: 2.377s -0.744s [-23.8314%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_re: 1.290s -1.831s [-58.6628%] (abc_def__FOO_BAR_bazx_bif - INCORRECT) 
format_title_re_corrected: 1.338s -1.782s [-57.1165%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
format_title_with_translate: 0.098s -3.022s [-96.8447%] (abc___def_____FOO_BAR_bazx_bif - CORRECT) 
  • 編輯:我添加了一個變化,顯示列表推導顯著提高原始的實現以及一個正確的正則表達式實現,顯示它仍然幾乎一樣快時,正確的。當然str.translate仍然贏得雙手。
+0

正則表達式解決方案不正確,因爲它將幾個相鄰的非字母替換爲一個下劃線。在字符類之後放下'+ +,儘管速度較慢,但​​它將是正確的。我想問題是你是否真的想在你的替換字符串中有很長的下劃線或者不是... –

0
import string,sys 
letters=string.letters 
mystring = list("abc134#[email protected]##$%%$*&(()#def") 
for n,c in enumerate(mystring): 
    if not c in letters: 
    mystring[n]="_" 
print ''.join(mystring) 
相關問題