2009-02-05 31 views
299

Tôi có một chuỗi Unicode bằng Python, và tôi muốn xóa tất cả các dấu (dấu phụ).Cách tốt nhất để xóa dấu trong chuỗi unicode Python là gì?

tôi tìm thấy trên mạng một cách thanh lịch để làm điều này trong Java:

  1. chuyển đổi chuỗi Unicode mẫu bình thường dài của nó (với một nhân vật riêng cho chữ cái và dấu)
  2. loại bỏ tất cả các nhân vật có loại Unicode là "dấu phụ".

Tôi có cần phải cài đặt thư viện như pyICU hoặc có thể chỉ với thư viện chuẩn python không? Và những gì về python 3?

Lưu ý quan trọng: Tôi muốn tránh mã với ánh xạ rõ ràng từ các ký tự có dấu trọng âm đến đối tác không có dấu của chúng.

Trả lời

230

Unidecode là câu trả lời đúng cho việc này. Nó chuyển đổi bất kỳ chuỗi unicode nào thành biểu diễn gần nhất có thể trong văn bản ascii.

Ví dụ:

accented_string = u'Málaga' 
# accented_string is of type 'unicode' 
import unidecode 
unaccented_string = unidecode.unidecode(accented_string) 
# unaccented_string contains 'Malaga'and is of type 'str' 
+0

Vâng, đây là giải pháp tốt hơn chỉ đơn giản là tước dấu. Nó cung cấp nhiều phiên âm hữu ích hơn cho các ngôn ngữ có quy ước để viết các từ trong ASCII. –

+34

Dường như làm việc tốt với người Trung Quốc, nhưng việc chuyển đổi tên tiếng Pháp "François" không may cho "FranASSois", mà không phải là rất tốt, so với "Francois" tự nhiên hơn. – EOL

+8

tùy thuộc vào những gì bạn đang cố gắng đạt được. ví dụ tôi đang thực hiện tìm kiếm ngay bây giờ và tôi không muốn chuyển ngữ tiếng Hy Lạp/tiếng Nga/tiếng Trung, tôi chỉ muốn thay thế "ą/ę/ś/ć" bằng "a/e/s/c" – kolinko

112

Tôi chỉ tìm thấy câu trả lời này trên Web:

import unicodedata 

def remove_accents(input_str): 
    nfkd_form = unicodedata.normalize('NFKD', input_str) 
    only_ascii = nfkd_form.encode('ASCII', 'ignore') 
    return only_ascii 

Nó hoạt động tốt (đối với người Pháp, ví dụ), nhưng tôi nghĩ rằng bước thứ hai (loại bỏ các điểm nhấn) có thể được xử lý tốt hơn so với thả phi Ký tự -ASCII, vì điều này sẽ không thành công đối với một số ngôn ngữ (ví dụ: tiếng Hy Lạp). Giải pháp tốt nhất có thể là xóa một cách rõ ràng các ký tự unicode được gắn thẻ là dấu phụ.

Sửa: đây hiện các trick:

import unicodedata 

def remove_accents(input_str): 
    nfkd_form = unicodedata.normalize('NFKD', input_str) 
    return u"".join([c for c in nfkd_form if not unicodedata.combining(c)]) 

unicodedata.combining(c) sẽ trả về true nếu nhân vật c có thể được kết hợp với các nhân vật trên, đó là chủ yếu nếu đó là một dấu phụ.

Chỉnh sửa 2: remove_accents hy vọng một unicode chuỗi, không phải là một chuỗi byte. Nếu bạn có một chuỗi byte, sau đó bạn phải giải mã nó thành một chuỗi unicode như thế này:

encoding = "utf-8" # or iso-8859-15, or cp1252, or whatever encoding you use 
byte_string = b"café" # or simply "café" before python 3. 
unicode_string = byte_string.decode(encoding) 
+5

Tôi phải thêm 'utf8' vào unicode: 'nkfd_form = unicodedata.normalize ('NFKD', unicode (input_str, 'utf8'))' – Jabba

+0

@Jabba: ', 'utf8'' là một" mạng lưới an toàn " cần thiết nếu bạn đang thử nghiệm đầu vào trong thiết bị đầu cuối (theo mặc định không sử dụng unicode). Nhưng thông thường bạn không * có * để thêm nó, vì nếu bạn đang xóa dấu trọng âm thì 'input_str' rất có khả năng là utf8 rồi. Tuy nhiên, nó không bị tổn thương để được an toàn. – MestreLion

+0

>>> def remove_accents (input_str): ... nkfd_form = unicodedata.normalize ('NFKD', unicode (input_str)) ... return u "". Join ([c cho c trong nkfd_form nếu không unicodedata. kết hợp (c)]) ... >>> remove_accents ('é') Traceback (gần đây nhất gọi cuối cùng): file "", dòng 1, trong file "", dòng 2, trong remove_accents UnicodeDecodeError: codec 'ascii' không thể giải mã byte 0xc3 ở vị trí 0: thứ tự không nằm trong phạm vi (128) – rbp

211

Làm thế nào về điều này:

import unicodedata 
def strip_accents(s): 
    return ''.join(c for c in unicodedata.normalize('NFD', s) 
        if unicodedata.category(c) != 'Mn') 

này hoạt động trên chữ Hy Lạp, quá:

>>> strip_accents(u"A \u00c0 \u0394 \u038E") 
u'A A \u0394 \u03a5' 
>>> 

character category "Mn" là viết tắt của Nonspacing_Mark, tương tự như unicodedata.combining trong câu trả lời của MiniQuark (Tôi không nghĩ về unicodedata.combining, nhưng nó có lẽ là soluti tốt hơn trên, bởi vì nó rõ ràng hơn).

Và hãy nhớ, các thao tác này có thể làm thay đổi đáng kể ý nghĩa của văn bản. Dấu trọng âm, Umlauts vv không phải là "trang trí".

+5

Đây không phải là các ký tự sáng tác, không may - mặc dù "ł" có tên "LATIN SMALL LETTER L WITH STROKE"! Bạn sẽ cần phải chơi trò chơi với phân tích cú pháp 'unicodedata.name', hoặc chia nhỏ và sử dụng một bảng trông giống nhau-- mà bạn cần cho các chữ cái Hy Lạp anyway (Α chỉ là" GREEK CAPITAL LETTER ALPHA "). – alexis

+0

@alexis https://mail.python.org/pipermail/python-list/2007-October/446440.html – andi

+0

@andi, tôi e rằng tôi không thể đoán được điểm bạn muốn thực hiện. Việc trao đổi email phản ánh những gì tôi đã viết ở trên: Bởi vì chữ "ł" không phải là một chữ cái có dấu (và không được coi là một trong tiêu chuẩn Unicode), nó không có sự phân hủy. – alexis

11

này xử lý không chỉ điểm nhấn, mà còn "đột quỵ" (như trong ø vv):

import unicodedata as ud 

def rmdiacritics(char): 
    ''' 
    Return the base character of char, by "removing" any 
    diacritics like accents or curls and strokes and the like. 
    ''' 
    desc = ud.name(unicode(char)) 
    cutoff = desc.find(' WITH ') 
    if cutoff != -1: 
     desc = desc[:cutoff] 
    return ud.lookup(desc) 

Đây là cách thanh lịch nhất mà tôi có thể nghĩ đến (và nó đã được đề cập bởi alexis trong một bình luận trên trang này), mặc dù tôi không nghĩ rằng nó là rất thanh lịch thực sự.

Vẫn có các chữ cái đặc biệt không được xử lý bởi điều này, chẳng hạn như các chữ cái được quay và ngược, vì tên unicode của chúng không chứa 'WITH'. Nó phụ thuộc vào những gì bạn muốn làm anyway. Đôi khi tôi cần nhấn mạnh giọng để đạt được thứ tự sắp xếp từ điển.

+4

Bạn nên bắt ngoại lệ nếu biểu tượng mới không tồn tại. Ví dụ: SQUARE WITH VERTICAL FILL ▥, nhưng không có SQUARE. (chưa kể rằng mã này biến đổi UMBRELLA WITH RAIN DROPS ☔ thành UMBRELLA ☂). – janek37

10

Để đối phó với @ MiniQuark của câu trả lời:

Tôi đã cố gắng để đọc trong một tập tin csv đó là nửa tiếng Pháp (có chứa dấu) và cũng có một số chuỗi mà cuối cùng sẽ trở thành số nguyên và nổi. Là một thử nghiệm, tôi đã tạo ra một tập tin test.txt trông như thế này:

Montréal, über, 12.89, Mère, Françoise, noël, 889

tôi phải bao gồm dòng 23 để làm cho nó làm việc (mà tôi tìm thấy trong một vé python), cũng như kết hợp @ bình luận Jabba của:

import sys 
reload(sys) 
sys.setdefaultencoding("utf-8") 
import csv 
import unicodedata 

def remove_accents(input_str): 
    nkfd_form = unicodedata.normalize('NFKD', unicode(input_str)) 
    return u"".join([c for c in nkfd_form if not unicodedata.combining(c)]) 

with open('test.txt') as f: 
    read = csv.reader(f) 
    for row in read: 
     for element in row: 
      print remove_accents(element) 

kết quả:

Montreal 
uber 
12.89 
Mere 
Francoise 
noel 
889 

(Lưu ý: tôi đang trên Mac OS X 10.8.4 và sử dụng Python 2.7.3)

+1

'remove_accents' có nghĩa là xóa dấu trọng âm khỏi chuỗi unicode. Trong trường hợp nó được truyền qua một chuỗi byte, nó cố gắng chuyển nó thành một chuỗi unicode với 'unicode (input_str)'. Điều này sử dụng mã hóa mặc định của python, đó là "ascii". Vì tệp của bạn được mã hóa bằng UTF-8, điều này sẽ không thành công. Các dòng 2 và 3 thay đổi mã hóa mặc định của python thành UTF-8, vì vậy nó hoạt động, như bạn đã tìm ra. Một tùy chọn khác là truyền 'remove_accents' một chuỗi unicode: loại bỏ các dòng 2 và 3, và trên dòng cuối thay thế' phần tử' bằng 'element.decode (" utf-8 ")'. Tôi đã thử nghiệm: nó hoạt động. Tôi sẽ cập nhật câu trả lời của mình để làm cho điều này rõ ràng hơn. – MiniQuark

+0

Chỉnh sửa tốt, điểm tốt. (Một lưu ý khác: Vấn đề thực sự tôi đã nhận ra là tệp dữ liệu của tôi dường như được mã hóa trong 'iso-8859-1', mà tôi không thể làm việc với hàm này, thật không may!) – aseagram

+0

aseagram: chỉ cần thay thế " utf-8 "với" iso-8859-1 "và nó sẽ hoạt động. Nếu bạn đang ở trên cửa sổ, sau đó bạn có lẽ nên sử dụng "cp1252" thay thế. – MiniQuark

14

Thực ra tôi làm việc trên dự án tương thích python 2.6, 2.7 và 3.4 và tôi phải tạo ID từ các mục nhập người dùng miễn phí.

Nhờ bạn, tôi đã tạo chức năng này hoạt động kỳ diệu.

import re 
import unicodedata 

def strip_accents(text): 
    """ 
    Strip accents from input String. 

    :param text: The input string. 
    :type text: String. 

    :returns: The processed String. 
    :rtype: String. 
    """ 
    try: 
     text = unicode(text, 'utf-8') 
    except (TypeError, NameError): # unicode is a default on python 3 
     pass 
    text = unicodedata.normalize('NFD', text) 
    text = text.encode('ascii', 'ignore') 
    text = text.decode("utf-8") 
    return str(text) 

def text_to_id(text): 
    """ 
    Convert input text to id. 

    :param text: The input string. 
    :type text: String. 

    :returns: The processed String. 
    :rtype: String. 
    """ 
    text = strip_accents(text.lower()) 
    text = re.sub('[ ]+', '_', text) 
    text = re.sub('[^0-9a-zA-Z_-]', '', text) 
    return text 

kết quả:

text_to_id("Montréal, über, 12.89, Mère, Françoise, noël, 889") 
>>> 'montreal_uber_1289_mere_francoise_noel_889' 
+0

chuỗi unicode với python3: http://stackoverflow.com/a/6812069/1569144 – Jer42

+2

Với Py2.7, truyền một lỗi chuỗi đã unicode tại 'text = unicode (văn bản, 'utf-8')'. Một cách giải quyết cho điều đó là thêm 'ngoại lệ TypeError: pass' –

+0

Tôi không biết cái gì là nhưng nó hoạt động (Y) –

0

Một số ngôn ngữ đã kết hợp dấu như chữ ngôn ngữ và dấu giọng để xác định giọng.

Tôi nghĩ rằng đó là an toàn hơn để xác định một cách rõ ràng những gì diactrics bạn muốn dải:

def strip_accents(string, accents=('COMBINING ACUTE ACCENT', 'COMBINING GRAVE ACCENT', 'COMBINING TILDE')): 
    accents = set(map(unicodedata.lookup, accents)) 
    chars = [c for c in unicodedata.normalize('NFD', string) if c not in accents] 
    return unicodedata.normalize('NFC', ''.join(chars)) 
6
import unicodedata 
s = 'Émission' 
search_string = ''.join((c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn')) 

Đối với Python 3.x

print (search_string) 

Đối với Python 2.x

print search_string 
+2

Điều này dường như hoạt động tốt. 'unidecode' dịch' ° 'thành' deg', có thể không phải là hành vi mong muốn. –

0

gensim.utils.deaccent(text) từ Gensim - topic modelling for humans:

deaccent("Šéf chomutovských komunistů dostal poštou bílý prášek") 'Sef chomutovskych komunistu dostal postou bily prasek'

giải pháp khác là unidecode.

Không phải là giải pháp đề nghị với unicodedata thường loại bỏ dấu chỉ trong một số ký tự (ví dụ nó quay 'ł' vào '', chứ không phải vào 'l').

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