2010-01-31 38 views

Trả lời

20

Cách nhanh hơn để làm điều đó là sử dụng str.translate() Đây là ~ nhanh hơn 50 lần so với cách của bạn

# 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 

Dưới đây là một phiên bản dành cho 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 Μεταλλικα") 

Lưu ý rằng các chữ cái Hy Lạp đếm như trên và dưới, vì vậy chúng không được thay thế. Nếu chúng được thay thế, chỉ cần thay đổi điều kiện để

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

+1, ý tưởng rất hay. Hạn chế duy nhất tôi có thể nghĩ là điều này sẽ không hoạt động đúng trên chuỗi Unicode nếu các ký tự không phải ASCII phải được xem xét. –

+1

@Tim, unicode cũng có một bản dịch - ngữ nghĩa là khác nhau mặc dù, hãy để tôi xem nếu tôi có thể làm cho nó hoạt động ... –

+0

@Tim, phiên bản unicode là lên. Bản đồ dịch được xây dựng theo yêu cầu, vì vậy sẽ có ít và ít bị bỏ lỡ khi nhiều chuỗi được dịch. –

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

sẽ nhanh hơn.

Nếu bạn muốn thay thế một chuỗi các liền kề phi thư với một dấu gạch dưới duy nhất, sử dụng

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

thay vì đó thậm chí còn nhanh hơn.

Tôi chỉ cần chạy một so sánh thời gian:.

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 

này sẽ làm việc trên chuỗi Unicode, quá (dưới Python 3, \W trận đấu bất kỳ ký tự mà không phải là một nhân vật từ Unicode Dưới Python 2, bạn muốn cũng phải đặt cờ UNICODE cho điều này).

+0

Cách bạn đang sử dụng thời gian là luôn luôn mở và đọc tệp.Bạn nên di chuyển công cụ đó đến phần '-s' để có được kết quả có ý nghĩa –

+0

Cảm ơn bạn, tất nhiên là bạn đúng. Vì điều này được thực hiện trong cả hai ví dụ, nên có lỗi (và, tôi vừa thử) giống nhau ở cả hai. Thật thú vị, tôi thấy rằng tiền biên dịch regex không tạo nên sự khác biệt. Tôi đã cập nhật các ví dụ về thời gian. –

+0

Lưu ý rằng bạn không thể đặt cờ với re.sub; bạn phải sử dụng re.compile() để chỉ định cờ và gọi sub() trên kết quả. (Đây là một thiếu sót API lạ.) Câu trả lời của bạn sẽ tốt hơn khi xóa phiên bản "+"; nó không phải là những gì anh ta yêu cầu, vì vậy nó chỉ mất tập trung. –

2

Thay vì (x.isupper() or x.islower()) bạn sẽ có thể sử dụng x.isalpha(). Phương pháp isalpha() có thể trả lại True cho '_' (Tôi không nhớ có hay không) nhưng sau đó bạn sẽ chỉ kết thúc thay thế '_' với '_' để không gây hại. (Cảm ơn vì đã chỉ ra điều đó, KennyTM.)

+0

Thực ra, nó có thể tự tính là '_' ký tự chữ cái, vì vậy có thể không. Hãy thử nó và xem. – MatrixFrog

+2

Thay thế '_' bằng' _' (hoặc không) là vô hại. – kennytm

1

Tò mò về điều này vì lý do của riêng tôi, tôi đã viết kịch bản nhanh để kiểm tra các cách tiếp cận khác nhau được liệt kê ở đây cùng với việc loại bỏ lambda mà tôi mong đợi (không đúng) sẽ tăng tốc bản gốc dung dịch.

Phiên bản ngắn gọn là cách tiếp cận str.translate thổi những thứ khác đi. Như một giải pháp regex sang một bên, trong khi một giây thứ hai, là chính xác như được viết ở trên.

Đây là chương trình thử nghiệm của tôi:

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) 

Và đây là kết quả:

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) 
  • EDITED: Tôi đã thêm một biến thể hiển thị danh sách comprehensions cải thiện đáng kể việc thực hiện ban đầu cũng như thực hiện chính xác regex cho thấy nó vẫn gần như nhanh khi chính xác. Tất nhiên str.translate vẫn thắng tay.
+0

Giải pháp regex không chính xác vì nó thay thế một vài chữ cái liền kề chỉ bằng một dấu gạch dưới. Thả '+' sau lớp nhân vật và nó sẽ đúng dù chậm hơn. Tôi đoán câu hỏi là liệu bạn có thực sự muốn trải dài các dấu gạch dưới trong dây thay thế hay không ... –

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) 
Các vấn đề liên quan