파이썬 유니 코드 문자열에서 악센트를 제거하는 가장 좋은 방법은 무엇입니까?


506

파이썬에는 유니 코드 문자열이 있으며 모든 악센트 (분음 부호)를 제거하고 싶습니다.

웹에서 Java로 이것을 수행하는 우아한 방법을 찾았습니다.

  1. 유니 코드 문자열을 긴 정규화 된 형식으로 변환하십시오 (문자와 분음 부호에 별도의 문자 사용)
  2. 유니 코드 유형이 "분음 부호"인 모든 문자를 제거하십시오.

pyICU와 같은 라이브러리를 설치해야합니까, 아니면 파이썬 표준 라이브러리만으로 가능합니까? 그리고 파이썬 3은 어떻습니까?

중요 사항 : 악센트 문자에서 악센트가 아닌 문자로 명시 적으로 매핑 된 코드를 피하고 싶습니다.

답변:


447

Unidecode 가 이에 대한 정답입니다. 유니 코드 문자열을 ASCII 텍스트에서 가장 가까운 표현으로 음역합니다.

예:

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
중국어와 잘 작동하는 것 같지만, "François"라는 프랑스 이름의 변환은 불행히도 "FranASSois"를 제공합니다.
Eric O Lebigot

10
당신이 달성하려는 것에 달려 있습니다. 예를 들어 지금은 검색을하고 있어요 및 I / 그리스어 러시아어 / 중국어 음역 싶지 않아, 난 그냥 "A / E / S / C" "A / E / S / C"로 대체 할
kolinko

58
유니 코드 객체를 전달하면 @EOL unidecode는 "François"와 같은 문자열에 적합합니다. 일반 바이트 문자열로 시도한 것처럼 보입니다.
Karl Bartel

26
유니 코드> = 0.04.10 (2012 년 12 월)은 GPL입니다. 보다 허가 된 라이센스가 필요하고 약간 더 나쁜 구현이 가능한 경우 이전 버전을 사용하거나 github.com/kmike/text-unidecode를 확인하십시오 .
Mikhail Korobov 2019

10
unidecode로 대체 °됩니다 deg. 악센트를 제거하는 것 이상을 수행합니다.
Eric Duminil

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_MarkMiniQuark의 대답에 unicodedata.combining 유사하다이, (나는 unicodedata.combining 생각하지 않았지만, 좀 더 명시 적으로 있기 때문에 그것은 아마도 더 나은 솔루션입니다).

이러한 조작으로 인해 텍스트의 의미가 크게 변경 될 수 있습니다. 악센트, 움라우트 등은 "장식"이 아닙니다.


6
불행하게도 "ł"의 이름이 "LATIN SMALL LETTER L WITH STROKE"라고하더라도 이러한 문자는 구성되지 않습니다. 구문 분석을 unicodedata.name사용하여 게임을 하거나, 그리스어 문자가 필요한 모양의 테이블 을 분석 하고 사용해야합니다 (Α는 "그리스 대문자 알파벳").
Alexis

2
@andi, 나는 당신이 만들고 싶은 점을 추측 할 수 없습니다. 전자 메일 교환은 위에 쓴 내용을 반영합니다. "ł"문자는 악센트 부호 문자가 아니며 (유니 코드 표준에서는 문자로 취급되지 않기 때문에) 분해되지 않습니다.
Alexis

2
@alexis (늦은 후속 조치) : 이것은 그리스어에도 완벽하게 적용됩니다 – 예. "대시와 VARIA가있는 녹색 대문자 알파벳"이 예상대로 "녹색 대문자 알파벳"으로 정규화됩니다. 음역을 언급하지 않는 한 (예 : "α"→ "a"), "악센트 제거"와 동일하지 않습니다.
lenz

@lenz, 나는 그리스어에서 악센트를 제거하는 것이 아니라 엘의 "스트로크"에 대해 이야기하지 않았습니다. 분음 부호가 아니므로 일반 엘로 변경하는 것은 그리스어 알파를로 변경하는 것과 같습니다 A. 원하지 않으면 원하지 않지만 두 경우 모두 라틴어 (거의) 모양을 대체합니다.
Alexis

대부분 잘 작동합니다 :) 그러나 예제에서는 ßASCII ss로 변환되지 않습니다 . 나는 여전히 unidecode사고를 피하기 위해 사용 합니다.
Art

146

방금 웹 에서이 답변을 찾았습니다.

import unicodedata

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

(예를 들어 프랑스어) 잘 작동하지만 비 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를 선행 문자와 결합 할 수있는 경우 , 즉 분음 부호 인 경우 true를 리턴합니다 .

편집 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

@Jabba : , 'utf8'터미널에서 입력을 테스트 할 때 필요한 "안전 네트워크"입니다 (기본적으로 유니 코드를 사용하지 않음). 그러나 일반적으로 악센트를 제거하면 이미 utf8 일 가능성이 높 으므로 추가 하지 않아도 됩니다 input_str. 안전하다고해도 아프지 않습니다.
MestreLion

1
@rbp : remove_accents일반 문자열 대신 유니 코드 문자열을 전달해야 합니다 ( "é"대신 u "é"). 에 일반 문자열을 전달 remove_accents했으므로 문자열을 유니 코드 문자열로 변환하려고 할 때 기본 ascii인코딩이 사용되었습니다. 이 인코딩은 값이> 127 인 바이트를 지원하지 않습니다. 쉘에 "é"를 입력하면 OS는 UTF-8 또는 일부 Windows 코드 페이지 인코딩으로 인코딩하고 바이트> 127을 포함하도록 인코딩했습니다. 유니 코드로의 변환을 제거하기 위해 함수를 변경합니다. 유니 코드가 아닌 문자열이 전달되면 더 명확하게 폭격합니다.
MiniQuark

1
@MiniQuark 완벽하게 작동했습니다 >>> remove_accents (unicode ( 'é'))
rbp

1
이 답변은 대용량 데이터 세트에서 최상의 결과를 얻었습니다. 유일한 예외는 "ð"입니다. unicodedata는이를 만지지 않습니다!
s29

43

실제로 나는 프로젝트 호환 파이썬 2.6, 2.7 및 3.4에서 일하고 있으며 무료 사용자 항목에서 ID를 만들어야합니다.

덕분에 놀라운 기능을하는이 기능을 만들었습니다.

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
Daniel Reis

아주 큰소리! 내 경우에는 일했다. 브라질리아의 종유석 종 (Capacidade de escuta dos alunos idioma Portugue)의 우세한 선택.
Aaron

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

이것은 내가 생각할 수있는 가장 우아한 방법입니다 (그리고이 페이지의 주석에서 알렉시스에 의해 언급되었습니다). 그러나 그것이 정말로 우아하다고 생각하지는 않습니다. 실제로, 주석에서 지적했듯이 유니 코드 이름은 실제로 이름 일 뿐이므로 일관성 또는 기타 사항을 보장하지 않습니다.

유니 코드 이름에 'WITH'가 포함되어 있지 않기 때문에 회전 및 반전 문자와 같이 여전히 처리되지 않는 특수 문자가 있습니다. 어쨌든 당신이하고 싶은 일에 달려 있습니다. 때로는 사전 정렬 순서를 달성하기 위해 악센트 제거가 필요했습니다.

참고 수정 :

주석 (처리 조회 오류, Python-3 코드)의 제안을 통합했습니다.


8
새 심볼이 존재하지 않으면 예외를 포착해야합니다. 예를 들어 SQUARE WITH VERTICAL FILL ▥은 있지만 SQUARE는 없습니다. (이 코드는 UMBRELLA WITH RAIN DROPS ☔을 UMBRELLA ☂로 변환합니다).
janek37

이것은 사용 가능한 문자의 의미 론적 설명을 활용하는 데 우아하게 보입니다. unicode파이썬 3에서 함수 호출이 정말로 필요 합니까? 나는 대신에 엄격한 정규 표현식이 위의 find주석에서 언급 한 모든 문제를 피할 것이라고 생각하며 메모는 중요한 코드 경로 일 때 성능에 도움이 될 것입니다.
matanster

1
@matanster 아니오, 이것은 Python-2 시대의 오래된 답변입니다. unicode배역은 더 이상이 문제에 대한 보편적 인, 우아한 해결책이없는 내 경험에, 어떤 경우 파이썬 3에 적절한되지 않습니다. 응용 프로그램에 따라 모든 접근 방식에는 장단점이 있습니다. 품질이 우수한 도구 unidecode는 수공예 테이블을 기반으로합니다. 일부 리소스 (테이블, 알고리즘)는 예를 들어 유니 코드로 제공됩니다. 데이터 정렬
lenz

1
위의 내용을 반복합니다. (py3) : 1) unicode (char)-> char 2) try : KeyError를 제외한 ud.lookup (desc)를 반환합니다. return char
mirek

@mirek 당신 말이 맞아요 :이 글타래는 매우 유명하기 때문에,이 답변은 약간의 업데이트 / 개선이 필요합니다. 편집했습니다.
lenz

15

@MiniQuark 님의 답변에 답변 :

하프 프랑스어 (액센트 포함) 인 csv 파일과 정수 및 부동 소수점이되는 일부 문자열을 읽으려고했습니다. 테스트로 test.txt다음과 같은 파일을 만들었습니다 .

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

나는 선을 포함했다 23(나는 파이썬 티켓에 있음) 작업에 얻을뿐만 아니라, 자바 더의 코멘트 @ 법인 (法人) :

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

(참고 : Mac OS X 10.8.4에서 Python 2.7.3을 사용하고 있습니다)


1
remove_accents유니 코드 문자열에서 악센트를 제거하기위한 것입니다. 바이트 문자열이 전달되면를 사용하여 유니 코드 문자열로 변환하려고 시도합니다 unicode(input_str). 이것은 파이썬의 기본 인코딩 인 "ascii"를 사용합니다. 파일이 UTF-8로 인코딩되었으므로 실패합니다. 줄 2와 3은 파이썬의 기본 인코딩을 UTF-8로 변경하므로 알다시피 작동합니다. 또 다른 옵션은 전달하는 remove_accents유니 코드 문자열 : 제거 라인 2, 3, 마지막 줄에 교체 elementelement.decode("utf-8"). 나는 시험했다 : 그것은 작동한다. 나는 이것을 명확하게하기 위해 대답을 업데이트 할 것입니다.
MiniQuark

좋은 편집, 좋은 지적. (또 다른 참고 사항 : 내가 깨달은 실제 문제는 내 데이터 파일이 분명히 인코딩되어 iso-8859-1있다는 것입니다. 불행히도이 기능으로 작업 할 수 없습니다!)
aseagram

aseagram : 단순히 "utf-8"을 "iso-8859-1"로 바꾸면 작동합니다. Windows를 사용하는 경우 "cp1252"를 대신 사용해야합니다.
MiniQuark

BTW reload(sys); sys.setdefaultencoding("utf-8")는 Windows 시스템에 권장되는 모호한 해킹입니다. 자세한 내용은 stackoverflow.com/questions/28657010/… 을 참조하십시오.
PM 2Ring

14

gensim.utils.deaccent (텍스트) 에서 Gensim - 인간에 대한 주제 모델링 :

'Sef chomutovskych komunistu dostal postou bily prasek'

또 다른 해결책은 unidecode 입니다.

와 제안 된 솔루션을 유의 한편 UnicodeData은 일반적으로 (예를 들어,이 회전 일부 문자에 악센트를 제거 'ł'''대신에보다 'l').


1
deaccent여전히 ł대신에 제공 합니다 l.
lcieslak

당신은 설치할 필요가 없습니다 NumPySciPy제거 악센트를 얻을 수 있습니다.
Nuno André

gensim 참조 주셔서 감사합니다! 속도 또는 정확도 측면에서 유니 코드와 어떻게 비교됩니까?
Etienne Kintzler

3

일부 언어에는 발음 부호를 발음 문자와 악센트 부호로 결합하여 발음 부호를 지정합니다.

나는 어떤 diactrics를 제거하고 싶은지를 명시 적으로 지정하는 것이 더 안전하다고 생각합니다.

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))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.