2009-08-24 22 views
54

Tôi có đoạn mã sau:Tôi làm cách nào để str.translate hoạt động với chuỗi Unicode?

import string 
def translate_non_alphanumerics(to_translate, translate_to='_'): 
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>[email protected][\]^_`{|}~' 
    translate_table = string.maketrans(not_letters_or_digits, 
             translate_to 
             *len(not_letters_or_digits)) 
    return to_translate.translate(translate_table) 

Những công trình vĩ đại cho các chuỗi không unicode:

>>> translate_non_alphanumerics('<foo>!') 
'_foo__' 

Nhưng không cho các chuỗi unicode:

>>> translate_non_alphanumerics(u'<foo>!') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in translate_non_alphanumerics 
TypeError: character mapping must return integer, None or unicode 

tôi không thể thực hiện bất kỳ ý nghĩa của đoạn trên "đối tượng Unicode" trong Python 2.6.2 docs cho phương thức str.translate().

Làm cách nào để thực hiện công việc này cho chuỗi Unicode?

+0

đây là [ví dụ xóa dấu chấm câu Unicode khỏi chuỗi bằng phương thức 'unicode.translate()' (http://stackoverflow.com/a/11066687/4279). – jfs

+0

sử dụng tốt hơn 'chuỗi nhập; string.punctuation' thay vì hardcoding 'not_letters_or_digits' trong mã thực. Tôi nhận được rằng ở đây bạn muốn được rõ ràng. –

Trả lời

50

Phiên bản Unicode của bản dịch yêu cầu ánh xạ từ các bộ phận Unicode (mà bạn có thể truy xuất cho một ký tự đơn với ord) đến các bộ phận Unicode. Nếu bạn muốn xóa các ký tự, bạn ánh xạ tới None.

tôi đã thay đổi chức năng của bạn để xây dựng một dict lập bản đồ thứ tự của tất cả các nhân vật với thứ tự của những gì bạn muốn dịch sang:

def translate_non_alphanumerics(to_translate, translate_to=u'_'): 
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>[email protected][\]^_`{|}~' 
    translate_table = dict((ord(char), translate_to) for char in not_letters_or_digits) 
    return to_translate.translate(translate_table) 

>>> translate_non_alphanumerics(u'<foo>!') 
u'_foo__' 

chỉnh sửa: Nó chỉ ra rằng các bản đồ dịch phải ánh xạ từ Unicode thứ tự (thông qua ord) đến một thứ tự Unicode khác, một chuỗi Unicode, hoặc None (để xóa). Do đó, tôi đã thay đổi giá trị mặc định cho translate_to thành chữ Unicode. Ví dụ:

>>> translate_non_alphanumerics(u'<foo>!', u'bad') 
u'badfoobadbad' 
+9

Cảm ơn bạn! (Như một quyết định thiết kế câm để có một chức năng được đặt tên giống hệt nhau hoạt động khác nhau.) – Sabuncu

+1

Ngoài ra, nếu bạn không muốn xác định thủ công các dấu chấm câu: nhập chuỗi; translate_table = {ord (unicode (c)) cho c trong string.punctuation} Lưu ý: Điều này sẽ không dịch tất cả các ký tự dấu chấm câu đặc biệt (có tấn ...) – dpb

+0

'not_letters_or_digits' của bạn bị thiếu' $ 'và '&'. Hãy để tôi đề xuất sử dụng 'string.punctuation' thay vì hardcoding tập hợp hoặc ký tự –

5

tôi đã đưa ra với sự kết hợp sau đây của chức năng ban đầu của tôi và phiên bản Mike 's làm việc với Unicode và ASCII chuỗi:

def translate_non_alphanumerics(to_translate, translate_to=u'_'): 
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>[email protected][\]^_`{|}~' 
    if isinstance(to_translate, unicode): 
     translate_table = dict((ord(char), unicode(translate_to)) 
           for char in not_letters_or_digits) 
    else: 
     assert isinstance(to_translate, str) 
     translate_table = string.maketrans(not_letters_or_digits, 
              translate_to 
               *len(not_letters_or_digits)) 
    return to_translate.translate(translate_table) 

Cập nhật: "ép buộc" translate_to để unicode cho unicode translate_table. Cảm ơn Mike.

+0

Tôi sẽ đề nghị bạn coerce the translate_to thành Unicode cho phiên bản Unicode, nếu không cuộc gọi dịch sẽ phát ra nếu bạn truyền chuỗi Unicode và" bình thường " chuỗi. –

+0

Điều này có vẻ giống như một phần của ngôn ngữ. +1 – bukzor

4

Đối với một hack đơn giản mà sẽ làm việc trên cả hai đối tượng str và unicode, chuyển đổi bảng dịch sang Unicode trước khi chạy dịch():

import string 
def translate_non_alphanumerics(to_translate, translate_to='_'): 
    not_letters_or_digits = u'!"#%\'()*+,-./:;<=>[email protected][\]^_`{|}~' 
    translate_table = string.maketrans(not_letters_or_digits, 
             translate_to 
             *len(not_letters_or_digits)) 
    translate_table = translate_table.decode("latin-1") 
    return to_translate.translate(translate_table) 

Việc nắm bắt ở đây là nó sẽ ngầm chuyển đổi tất cả các đối tượng str để unicode, ném lỗi nếu to_translate chứa ký tự không phải ascii.

0

Thay vì phải xác định tất cả các nhân vật mà cần phải được thay thế, bạn cũng có thể xem nó là cách khác xung quanh và, thay vào đó, chỉ rõ những chữ số có giá trị, như vậy:

import re 

def replace_non_alphanumerics(source, replacement_character='_'): 
    result = re.sub("[^_a-zA-Z0-9]", replacement_character, source) 

    return result 

này làm việc với unicode cũng như các chuỗi thông thường và bảo toàn loại (nếu cả hai kiểu replacement_charactersource đều có cùng loại, rõ ràng).

7

Trong phiên bản này bạn tương đối có thể làm cho chữ của một người để

khác
def trans(to_translate): 
    tabin = u'привет' 
    tabout = u'тевирп' 
    tabin = [ord(char) for char in tabin] 
    translate_table = dict(zip(tabin, tabout)) 
    return to_translate.translate(translate_table) 
0

tôi thấy rằng nơi trong python 2.7, với loại str, bạn sẽ viết

import string 
table = string.maketrans("123", "abc") 
print "135".translate(table) 

trong khi với loại unicode bạn sẽ nói

table = {ord(s): unicode(d) for s, d in zip("123", "abc")} 
print u"135".translate(table) 

Trong python 3.6 bạn sẽ viết

table = {ord(s): d for s, d in zip("123", "abc")} 
print("135".translate(table)) 

có lẽ đây là hữu ích.

Các vấn đề liên quan