Какой самый быстрый способ снять и заменить документ с высокими символами Юникода с помощью Python?
Я хочу заменить из большого документа все высокие символы юникода, такие как акцентированные Es, левые и правые кавычки и т.д., с "нормальными" аналогами в низком диапазоне, такими как обычный "E" и прямой кавычки. Мне нужно довольно часто выполнять это на очень большом документе. Я вижу пример этого в том, что, на мой взгляд, может быть perl: http://www.designmeme.com/mtplugins/lowdown.txt
Есть ли быстрый способ сделать это в Python без использования s.replace(...). replace (...). replace (...)...? Я пробовал это на нескольких символах для замены, и снятие документа стало очень медленным.
EDIT, моя версия кода unutbu, которая, похоже, не работает:
# -*- coding: iso-8859-15 -*-
import unidecode
def ascii_map():
data={}
for num in range(256):
h=num
filename='x{num:02x}'.format(num=num)
try:
mod = __import__('unidecode.'+filename,
fromlist=True)
except ImportError:
pass
else:
for l,val in enumerate(mod.data):
i=h<<8
i+=l
if i >= 0x80:
data[i]=unicode(val)
return data
if __name__=='__main__':
s = u'"fancy"fancy2'
print(s.translate(ascii_map()))
Ответы
Ответ 1
# -*- encoding: utf-8 -*-
import unicodedata
def shoehorn_unicode_into_ascii(s):
return unicodedata.normalize('NFKD', s).encode('ascii','ignore')
if __name__=='__main__':
s = u"éèêàùçÇ"
print(shoehorn_unicode_into_ascii(s))
# eeeaucC
Обратите внимание, что, как любезно указывает @Mark Tolonen, метод выше удаляет некоторые символы, такие как
ß "" ". Если приведенный выше код обрезает символы, которые вы хотите перевести, вам может понадобиться использовать метод translate
для исправления этих проблем вручную. Другой вариант - использовать unidecode (см. J.F. Ответ Себастьяна).
Когда у вас есть большая строка юникода, использование метода translate
будет много
гораздо быстрее, чем при использовании метода replace
.
Изменить: unidecode
имеет более полное сопоставление кодов Unicode для ascii.
Тем не менее, unidecode.unidecode
проходит через строковый символ по символу (в цикле Python), который медленнее, чем метод translate
.
Следующая вспомогательная функция использует файлы данных unidecode
и метод translate
для достижения более высокой скорости, особенно для длинных строк.
В моих тестах с текстовыми файлами 1-6 Мбайт использование ascii_map
примерно в 4-6 раз быстрее, чем unidecode.unidecode
.
# -*- coding: utf-8 -*-
import unidecode
def ascii_map():
data={}
for num in range(256):
h=num
filename='x{num:02x}'.format(num=num)
try:
mod = __import__('unidecode.'+filename,
fromlist=True)
except ImportError:
pass
else:
for l,val in enumerate(mod.data):
i=h<<8
i+=l
if i >= 0x80:
data[i]=unicode(val)
return data
if __name__=='__main__':
s = u"éèêàùçÇ"
print(s.translate(ascii_map()))
# eeeaucC
Edit2: Rhubarb, если # -*- encoding: utf-8 -*-
вызывает SyntaxError, попробуйте
# -*- encoding: cp1252 -*-
. Какая кодировка для объявления зависит от того, какую кодировку использует текстовый редактор для сохранения файла. Linux имеет тенденцию использовать utf-8, и (кажется, возможно) Windows имеет тенденцию к cp1252.
Ответ 2
Нет такой вещи, как "высокий символ ascii". Набор символов ASCII ограничен порядковым номером в диапазоне (128).
В стороне, это FAQ. Здесь один ответ. В общем, вам следует ознакомиться с str.translate() и unicode.translate() - очень удобно для нескольких подстановок одиночных байтов/символов. Остерегайтесь ответов, в которых упоминается только трюк unicodedata.normalize(); что только одна часть решения.
Обновление: принятый в настоящее время ответ удаляет символы, которые не имеют разложения, как указал Марк Толонен. Кажется, что нет знания того, чем способна unicode.translate()
. Он МОЖЕТ перевести один символ на несколько символов. Вот результат от help(unicode.translate)
:
S.translate(table) → unicode
Верните копию строки S, где все символы были сопоставлены с помощью данной таблицы перевода, которая должна быть отображением ордеров Unicode в ординалы Unicode, Unicode string или None. Неизменяемые символы остаются нетронутыми. Символы, сопоставленные None, удаляются.
Вот пример:
>>> u"Gau\xdf".translate({0xdf: u"ss"})
u'Gauss'
>>>
Вот таблица исправлений от решения, на которое я указал:
CHAR_REPLACEMENT = {
# latin-1 characters that don't have a unicode decomposition
0xc6: u"AE", # LATIN CAPITAL LETTER AE
0xd0: u"D", # LATIN CAPITAL LETTER ETH
0xd8: u"OE", # LATIN CAPITAL LETTER O WITH STROKE
0xde: u"Th", # LATIN CAPITAL LETTER THORN
0xdf: u"ss", # LATIN SMALL LETTER SHARP S
0xe6: u"ae", # LATIN SMALL LETTER AE
0xf0: u"d", # LATIN SMALL LETTER ETH
0xf8: u"oe", # LATIN SMALL LETTER O WITH STROKE
0xfe: u"th", # LATIN SMALL LETTER THORN
}
Это можно легко расширить, чтобы удовлетворить причудливые кавычки и другие символы, отличные от латинского-1, найденные в cp1252 и братьях и сестрах.
Ответ 3
Я считаю, что unicodedata
не работает для причудливых кавычек. Вы можете использовать Unidecode
в этом случае:
import unidecode
print unidecode.unidecode(u"ß‘’""")
# -> ss''""
Ответ 4
Если unicodedata.normalize(), как предложено ~unubtu
, не делает трюк, для Например, если вы хотите больше контролировать отображение, вы должны посмотреть в
str.translate()
наряду с str.maketrans(), утилитой для создания таблицы карт, str.translate эффективен и удобен для такого типа перевода.
В Python 2.x и для строк unicode нужно использовать unicode.translate(), а не str.translate(), и трюк, подобный тому, который показан в фрагменте кода ниже, вместо maketrans(). (спасибо Джону Мачину за это!)
Эти методы также доступны в Python 3.x, например, Документация Python 3.1.2 (по какой-то причине я сделал что это может измениться в Python 3.x). Конечно, под Python 3 все строки являются строками unicode, но это другая проблема.
#Python 3.1
>>> intab = 'àâçêèéïîôù'
>>> outtab = 'aaceeeiiou'
>>> tmap = str.maketrans(intab, outtab)
>>> s = "à la fête de l'été, où il fait bon danser, les Français font les drôles"
>>> s
"à la fête de l'été, où il fait bon danser, les Français font les drôles"
>>> s.translate(tmap)
"a la fete de l'ete, ou il fait bon danser, les Francais font les droles"
>>>
#Python 2.6
>>> intab = u'àâçêèéïîôù'
>>> outtab = u'aaceeeiiou'
>>> s = u"à la fête de l'été, où il fait bon danser, les Français font les drôles"
>>> #note the trick to replace maketrans() since for unicode strings the translation
>>> # map expects integers (unicode ordinals) not characters.
>>> tmap = dict(zip(map(ord, intab), map(ord, outtab)))
>>> s.translate(tmap)
u"a la fete de l'ete, ou il fait bon danser, les Francais font les droles"
>>>
Ответ 5
Здесь решение, которое обрабатывает символы латинского-1 (на основе потока usenet 2003):
>>> accentstable = str.join("", map(chr, range(192))) + "AAAAAAACEEEEIIIIDNOOOOOxOUUUUYTsaaaaaaaceeeeiiiidnooooo/ouuuuyty"
>>> import string
>>> s = u"éèêàùçÇ"
>>> print string.translate(s.encode('latin1', 'ignore'), accentstable)
eeeaucC
Некоторые из отображений не идеальны, например. Thorn отображает T, а не Th, но он выполняет терпимую работу.