पायथन यूनिकोड स्ट्रिंग में लहजे को हटाने का सबसे अच्छा तरीका क्या है?


503

मेरे पास पायथन में एक यूनिकोड स्ट्रिंग है, और मैं सभी उच्चारण (विकृति विज्ञान) को दूर करना चाहूंगा।

मुझे वेब पर जावा में ऐसा करने का एक सुंदर तरीका मिला:

  1. यूनिकोड स्ट्रिंग को उसके लंबे सामान्यीकृत रूप में परिवर्तित करें (अक्षरों और विकृति विज्ञान के लिए एक अलग चरित्र के साथ)
  2. उन सभी पात्रों को हटा दें, जिनका यूनिकोड प्रकार "डाइक्रिटिक" है।

क्या मुझे pyICU जैसी लाइब्रेरी स्थापित करने की आवश्यकता है या क्या यह सिर्फ पायथन मानक पुस्तकालय के साथ संभव है? और अजगर 3 के बारे में क्या?

महत्वपूर्ण नोट: मैं स्पष्ट वर्णों से उनके गैर-उच्चारण समकक्ष के उच्चारण अक्षर से कोड से बचना चाहूंगा।

जवाबों:


446

इसके लिए यूनिडबॉस सही उत्तर है। यह किसी भी यूनिकोड स्ट्रिंग को एससीआई पाठ में निकटतम संभावित प्रतिनिधित्व में बदल देता है।

उदाहरण:

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'

67
चीनी के साथ अच्छी तरह से काम करने लगता है, लेकिन फ्रांसीसी नाम "फ्रांस्वा" का परिवर्तन दुर्भाग्य से "फ्रैंकोइस" देता है, जो कि अधिक प्राकृतिक "फ्रेंकोइस" की तुलना में बहुत अच्छा नहीं है।
एरिक ओ लेबिगॉट सेप

10
निर्भर करता है कि आप क्या हासिल करने की कोशिश कर रहे हैं। उदाहरण के लिए, मैं अभी एक खोज कर रहा हूं, और मैं ग्रीक / रूसी / चीनी का अनुवाद नहीं करना चाहता, मैं बस "ę / ą / ś / ć" को "ए / ई / एस / सी" से बदलना चाहता हूं
kolinko

58
@ ईओडी यूनिडॉब "फ्रैंकोइस" जैसे स्ट्रिंग्स के लिए महान काम करता है, अगर आप इसे यूनिकोड ऑब्जेक्ट पास करते हैं। ऐसा लगता है कि आपने एक सादे बाइट स्ट्रिंग के साथ कोशिश की।
कार्ल बार्टल

26
ध्यान दें कि unidecode> = 0.04.10 (Dec 2012) GPL है। यदि आपको अधिक अनुमेय लाइसेंस की आवश्यकता है, तो पहले के संस्करणों का उपयोग करें या github.com/kmike/text-unidecode जांचें ।
मिखाइल कोरोबोव

10
unidecodeके °साथ बदल देता है deg। यह केवल लहजे को हटाने से अधिक है।
एरिक डुमिनील

273

इस बारे में कैसा है:

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

यह ग्रीक अक्षरों पर भी काम करता है:

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

चरित्र श्रेणी "Mn" खड़ा के लिए Nonspacing_Markजो MiniQuark के जवाब में unicodedata.combining के समान है, (मैं unicodedata.combining नहीं सोचा था, लेकिन यह शायद, बेहतर समाधान है, क्योंकि यह अधिक स्पष्ट है)।

और ध्यान रखें, ये जोड़तोड़ पाठ के अर्थ को महत्वपूर्ण रूप से बदल सकते हैं। उच्चारण, उमलट्स आदि "सजावट" नहीं हैं।


6
ये अक्षर नहीं हैं, दुर्भाग्य से - भले ही "ł" का नाम "LATIN SMALL LETTER L विद STROKE" हो! आपको या तो पार्सिंग के साथ गेम खेलना होगा unicodedata.name, या फिर ब्रेक-अप करना होगा और लुक-अलाइक टेबल का उपयोग करना होगा - जिसे आपको ग्रीक अक्षरों के लिए वैसे भी आवश्यकता होगी (anyway सिर्फ "GREEK CAPITAL LETTER ALPHA")।
एलेक्सिस

2
@ कंडी, मुझे डर है कि मैं अनुमान नहीं लगा सकता कि आप किस बिंदु पर बनाना चाहते हैं। ईमेल एक्सचेंज यह दर्शाता है कि मैंने ऊपर क्या लिखा है: क्योंकि पत्र "ł" एक उच्चारण पत्र नहीं है (और यूनिकोड मानक में एक के रूप में नहीं माना जाता है), इसमें अपघटन नहीं होता है।
एलेक्सिस

2
@alexis (देर से अनुवर्ती): यह ग्रीक के लिए पूरी तरह से अच्छी तरह से काम करता है - जैसे। "डासिया और वारिया के साथ ग्रेग कैपिटल लेटर अल्फा" को उम्मीद के मुताबिक "ग्रेग कैपिटल लेटर अल्फा" में सामान्यीकृत किया गया है। जब तक आप लिप्यंतरण (उदाहरण के लिए। "α" → "ए") का उल्लेख नहीं कर रहे हैं , जो "लहजे को हटाने" के समान नहीं है ...
lenz

@lenz, मैं ग्रीक से लहजे को हटाने के बारे में बात नहीं कर रहा था, लेकिन ईल पर "स्ट्रोक" के बारे में। चूँकि यह एक प्रकार का वृक्ष नहीं है, इसलिए इसे सादे ईल में बदलना ग्रीक अल्फा को बदलने के समान है A। यदि वह ऐसा नहीं करना चाहता है, लेकिन यह दोनों मामलों में आप एक लैटिन (निकट) एक जैसे दिख रहे हैं।
एलेक्सिस

ज्यादातर अच्छा काम करता है :) लेकिन यह उदाहरण ßमें एससीआई ssमें नहीं बदलता है । मैं अभी भी unidecodeदुर्घटनाओं से बचने के लिए उपयोग करूंगा ।
आर्ट '

145

मुझे यह उत्तर वेब पर मिला:

import unicodedata

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

यह ठीक काम करता है (उदाहरण के लिए फ्रेंच के लिए), लेकिन मुझे लगता है कि दूसरे चरण (उच्चारण को हटाना) को गैर-एएससीआईआई पात्रों को छोड़ने से बेहतर तरीके से नियंत्रित किया जा सकता है, क्योंकि यह कुछ भाषाओं (उदाहरण के लिए ग्रीक) के लिए विफल हो जाएगा। सबसे अच्छा समाधान संभवतः यूनिकोड वर्णों को स्पष्ट रूप से हटाने के लिए होगा जिन्हें डायट्रीटिक के रूप में चिह्नित किया जाता है।

संपादित करें : यह चाल है:

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)यह सच है कि अगर चरित्र cको पूर्ववर्ती चरित्र के साथ जोड़ा जा सकता है, तो यह मुख्य रूप से यदि यह एक बाल चिकित्सा है।

संपादित करें 2 : remove_accentsएक यूनिकोड स्ट्रिंग की अपेक्षा करता है , न कि एक बाइट स्ट्रिंग की। यदि आपके पास एक बाइट स्ट्रिंग है, तो आपको इसे यूनिकोड स्ट्रिंग में इस तरह से डीकोड करना होगा:

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
मुझे यूनिकोड में 'utf8' जोड़ना था: nkfd_form = unicodedata.normalize('NFKD', unicode(input_str, 'utf8'))
Jabba

@ जब्बा: , 'utf8'यदि आप टर्मिनल में इनपुट का परीक्षण कर रहे हैं (जो डिफ़ॉल्ट रूप से यूनिकोड का उपयोग नहीं करता है) "सुरक्षा जाल" की जरूरत है। लेकिन आमतौर पर आपको इसे जोड़ना नहीं पड़ता है, क्योंकि यदि आप लहजे को हटा रहे हैं तो input_strपहले से ही utf8 होने की बहुत संभावना है। यह सुरक्षित होने के लिए चोट नहीं करता है, यद्यपि।
MestreLion

1
@ आरबीपी: आपको एक यूनिकोड स्ट्रिंग पास करनी चाहिए remove_accents एक नियमित स्ट्रिंग (यू "" के बजाय "é") के बजाय । आपने एक नियमित स्ट्रिंग पास किया remove_accents, इसलिए जब अपने स्ट्रिंग को यूनिकोड स्ट्रिंग में बदलने की कोशिश की गई, तो डिफ़ॉल्ट asciiएन्कोडिंग का उपयोग किया गया। यह एन्कोडिंग किसी भी बाइट का समर्थन नहीं करता है जिसका मूल्य> 127 है। जब आपने अपने शेल में "é" टाइप किया, तो आपके OS ने एन्कोड किया, संभवतः UTF-8 या कुछ विंडोज कोड पेज एन्कोडिंग के साथ, और जिसमें बाइट्स> 127 शामिल थे। यूनिकोड में रूपांतरण को हटाने के लिए मैं अपना फ़ंक्शन बदलूँगा: यदि कोई गैर-यूनिकोड स्ट्रिंग पास किया गया है तो यह अधिक स्पष्ट रूप से बम होगा।
मिनीक्वार

1
@MiniQuark जो पूरी तरह से काम करता है >>> remove_accents (यूनिकोड ('é'))
rbp

1
इस जवाब ने मुझे एक बड़े डेटा सेट पर सबसे अच्छा परिणाम दिया, एकमात्र अपवाद "-" है - यूनिकोडाटा इसे नहीं छूएगा!
s29

43

वास्तव में मैं प्रोजेक्ट संगत अजगर 2.6, 2.7 और 3.4 पर काम करता हूं और मुझे मुफ्त उपयोगकर्ता प्रविष्टियों से आईडी बनाना होगा।

आपके लिए धन्यवाद, मैंने यह फ़ंक्शन बनाया है जो अद्भुत काम करता है।

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

परिणाम:

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

2
Py2.7 के साथ, पहले से ही यूनिकोड स्ट्रिंग त्रुटियों को पारित करना text = unicode(text, 'utf-8')। इसके लिए एक समाधान जोड़ना थाexcept TypeError: pass
डैनियल रीस

बहुत शोर! मेरे मामले में काम किया। उमा सेलेकाओ डे पोसिया ब्रासिलेरा पैरा डेसेनवोल्वर एक कैपेसिडडे डी एस्कटे डॉस एलुनोस इडियोमा पोर्टुगुएस।
आरोन

23

यह न केवल उच्चारण को संभालता है, बल्कि "स्ट्रोक" (जैसे ø इत्यादि में):

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(char)
    cutoff = desc.find(' WITH ')
    if cutoff != -1:
        desc = desc[:cutoff]
        try:
            char = ud.lookup(desc)
        except KeyError:
            pass  # removing "WITH ..." produced an invalid name
    return char

यह सबसे सुंदर तरीका है जिसके बारे में मैं सोच सकता हूं (और इसका उल्लेख एलेक्सिस ने इस पृष्ठ पर एक टिप्पणी में किया है), हालांकि मुझे नहीं लगता कि यह वास्तव में बहुत ही सुरुचिपूर्ण है। वास्तव में, यह एक हैक का अधिक है, जैसा कि टिप्पणियों में बताया गया है, क्योंकि यूनिकोड नाम हैं - वास्तव में सिर्फ नाम, वे लगातार या कुछ भी होने की कोई गारंटी नहीं देते हैं।

अभी भी कुछ विशेष पत्र हैं जो इसके द्वारा नियंत्रित नहीं किए जाते हैं, जैसे कि मुड़ गए और उल्टे अक्षर, क्योंकि उनके यूनिकोड नाम में 'विदाउट' नहीं है। यह इस पर निर्भर करता है कि आप क्या करना चाहते हैं। मुझे कभी-कभी शब्दकोश सॉर्ट ऑर्डर प्राप्त करने के लिए उच्चारण स्ट्रिपिंग की आवश्यकता होती है।

संपादित करें नोट:

टिप्पणियों से शामिल सुझाव (लुकअप त्रुटियों को संभालना, पायथन -3 कोड)।


8
यदि नया प्रतीक मौजूद नहीं है, तो आपको अपवाद को पकड़ना चाहिए। उदाहरण के लिए सर्जिकल फिल ARE के साथ SQUARE है, लेकिन कोई SQUARE नहीं है। (यह उल्लेख नहीं है कि यह कोड UMBRELLA को RAIN DROPS MB के साथ UMBRELLA this में बदल देता है)।
जनक ३।

यह उपलब्ध वर्णों के शब्दार्थ वर्णन का उपयोग करने में सुरुचिपूर्ण लगता है। क्या हमें वास्तव unicodeमें अजगर 3 के साथ फंक्शन कॉल की आवश्यकता है ? मुझे लगता है कि के स्थान पर एक तंग regex findऊपर टिप्पणी में वर्णित सभी परेशानी से बचने के लिए, और यह भी, संस्मरण प्रदर्शन में मदद करेगा जब यह एक महत्वपूर्ण कोड पथ है।
Matanster

1
@matanster नहीं, यह पायथन -2 युग से एक पुराना जवाब है; unicodeसमान किरदार अब अजगर 3. में उचित है किसी भी मामले में, मेरे अनुभव में है इस समस्या का कोई सार्वभौमिक, सुरुचिपूर्ण समाधान। आवेदन के आधार पर, किसी भी दृष्टिकोण में इसके पेशेवरों और विपक्ष हैं। गुणवत्ता संपन्न उपकरण जैसे unidecodeहाथ से तैयार की गई तालिकाओं पर आधारित होते हैं। कुछ संसाधन (टेबल, एल्गोरिदम) यूनिकोड द्वारा प्रदान किए जाते हैं, जैसे। टक्कर के लिए।
लॉन्ज़

1
मैं सिर्फ दोहराता हूं, ऊपर क्या है (py3): 1) यूनिकोड (चार) -> चार 2) कोशिश करें: KeyError को छोड़कर ud.lookup (desc) लौटें: वापसी char
mirek

@ मिरेक आप सही हैं: चूंकि यह धागा इतना लोकप्रिय है, इसलिए यह उत्तर कुछ अद्यतन / सुधार के योग्य है। मैंने इसे संपादित किया।
लेन्ज

15

@ MiniQuark के जवाब के जवाब में:

मैं एक सीएसएसवी फाइल में पढ़ने की कोशिश कर रहा था जो कि आधी-फ्रांसीसी (उच्चारण युक्त) थी और कुछ तार भी जो अंत में पूर्णांक और फ्लोट बन जाते थे। परीक्षण के रूप में, मैंने एक test.txtफाइल बनाई जो इस तरह दिखती थी:

मॉन्ट्रियल, über, 12.89, एमईआर, फ्रेंकोइस, नोएल, 889

मुझे लाइनों को शामिल करना था 2और 3इसे काम करने के लिए प्राप्त करना था (जो मुझे एक अजगर टिकट में मिला था), साथ ही साथ @ जेबा की टिप्पणी को भी शामिल करना था:

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)

परिणाम:

Montreal
uber
12.89
Mere
Francoise
noel
889

(नोट: मैं मैक ओएस एक्स 10.8.4 पर हूं और पायथन 2.7.3 का उपयोग कर रहा हूं)


1
remove_accentsएक यूनिकोड स्ट्रिंग से लहजे को हटाने के लिए था। यदि यह एक बाइट-स्ट्रिंग से गुजरता है, तो यह इसे यूनिकोड स्ट्रिंग के साथ परिवर्तित करने की कोशिश करता है unicode(input_str)। यह अजगर के डिफ़ॉल्ट एन्कोडिंग का उपयोग करता है, जो कि "एससीआई" है। चूंकि आपकी फ़ाइल UTF-8 के साथ एन्कोडेड है, इसलिए यह विफल हो जाएगी। लाइनें 2 और 3 अजगर के डिफ़ॉल्ट एन्कोडिंग को यूटीएफ -8 में बदल देती हैं, इसलिए यह काम करता है, जैसा कि आपको पता चला है। एक अन्य विकल्प remove_accentsएक यूनिकोड स्ट्रिंग को पास करना है: लाइनों 2 और 3 को हटा दें, और अंतिम पंक्ति elementद्वारा प्रतिस्थापित करें element.decode("utf-8")। मैंने परीक्षण किया: यह काम करता है। मैं इसे स्पष्ट करने के लिए अपने उत्तर को अपडेट करूंगा।
मिनीक्वार्क

अच्छा संपादन, अच्छी बात है। (एक और नोट पर: मुझे जो वास्तविक समस्या का एहसास हुआ है, वह यह है कि मेरी डेटा फ़ाइल जाहिरा तौर पर इनकोडेड है iso-8859-1, जिसे मैं इस फ़ंक्शन के साथ काम नहीं कर सकता, दुर्भाग्य से!)
aseagram

aseagram: बस "utf-8" को "iso-8859-1" से बदलें, और यह काम करना चाहिए। यदि आप खिड़कियों पर हैं, तो आपको संभवतः "cp1252" का उपयोग करना चाहिए।
मिनीक्वार

BTW, reload(sys); sys.setdefaultencoding("utf-8")एक संदिग्ध हैक है जिसे कभी-कभी विंडोज सिस्टम के लिए अनुशंसित किया जाता है; देख stackoverflow.com/questions/28657010/... जानकारी के लिए।
PM 2Ring

14

gensim.utils.deaccent (पाठ) से Gensim - मनुष्य के लिए विषय मॉडलिंग :

'Sef chomutovskych komunistu dostal postou bily prasek'

एक और उपाय है यूनिडबॉक्‍स

ध्यान दें कि साथ सुझाव दिया समाधान unicodedata आम तौर पर (जैसे बदल जाता केवल कुछ चरित्र में लहजे को हटा 'ł'में ''नहीं बल्कि में से, 'l')।


1
deaccentके łबजाय अभी भी देता है l
lcieslak

आपको स्थापित करने NumPyऔर SciPyहटाए गए लहजे प्राप्त करने की आवश्यकता नहीं है।
नूनो आंद्रे

गेंसिम संदर्भ के लिए धन्यवाद! यह यूनिडोस (गति या सटीकता के संदर्भ में) की तुलना कैसे करता है?
एटिने किन्त्ज़लर

3

कुछ भाषाओं में उच्चारण को निर्दिष्ट करने के लिए भाषा के अक्षरों और लहजे के रूप में diacritics का संयोजन होता है।

मुझे लगता है कि यह स्पष्ट रूप से निर्दिष्ट करना अधिक सुरक्षित है कि आप किस डायक्टिक्स को स्ट्रिप करना चाहते हैं:

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))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.