Внутри моего скрипта Python я получаю некоторую строку из функции, которую я не писал. Кодировка его варьируется. Мне нужно преобразовать его в формат ascii. Есть ли какой-нибудь надежный способ сделать это? Я не против заменить не-ascii-символы пробелами или чем-то еще...
Каков надежный способ преобразования некоторой строки (utf-8 или еще) в простую строку ASCII в python
Ответы (5)
Если вам нужна строка ASCII, однозначно представляющая то, что у вас есть, без потери информации, ответ прост:
Не возитесь с кодированием/декодированием, используйте функцию repr()
(Python 2.X) или функцию ascii()
(Python 3.x).
Вы говорите, что «кодировка варьируется». Я предполагаю, что под «этим» вы подразумеваете «строку» Python 2.x, которая на самом деле представляет собой последовательность байтов.
Ответ на первую часть: если вы не знаете кодировку этой закодированной строки, то нет, с ней вообще невозможно что-либо сделать*. Если вы знаете кодировку, то первым шагом будет преобразование str
в unicode
:
encoded_string = i_have_no_control()
the_encoding = 'utf-8' # for the sake of example
text = unicode(encoded_string, the_encoding)
Затем вы можете перекодировать свой объект unicode как ASCII, если хотите.
ascii_garbage = text.encode('ascii', 'replace')
* Существуют эвристические методы угадывания кодировок, но они медленные и ненадежные. Вот одна отличная попытка в Python.
ä
в a
, что вы, конечно, не можете сделать, предполагая, что ваша кодировка один байт на символ и маскирование байтов, отличных от ascii, как вы предлагаете!
- person Jonathan Feinberg; 24.11.2009
Я бы попытался нормализовать строку, а затем закодировать ее. Что о :
import unicodedata
s = u"éèêàùçÇ"
print unicodedata.normalize('NFKD',s).encode('ascii','ignore')
Это работает, только если у вас есть юникод в качестве входных данных. Поэтому вы должны знать, что может кодировать функция, и декодировать ее. Если вы этого не сделаете, существуют эвристики обнаружения кодирования, но для коротких строк они ненадежны.
Конечно, вам может повезти, и выходные данные функции зависят от различных неизвестных кодировок, но с использованием ascii в качестве базы кода, поэтому они будут выделять одно и то же значение для байтов от 0 до 127 (например, utf-8 ).
В этом случае вы можете просто избавиться от нежелательных символов, отфильтровав их с помощью OrderedSets:
import string.printable # asccii chars
print "".join(OrderedSet(string.printable) & OrderedSet(s))
Или, если вы хотите вместо этого пробелы:
print("".join(((char if char in string.printable else " ") for char in s )))
«Перевести» может помочь вам сделать то же самое.
Единственный способ узнать, повезло ли вам, - это попробовать... Иногда большой удачный день - это то, что нужно любому разработчику :-)
Под «защитой от дурака» понимается то, что функция не дает сбоев даже при самых неясных, невозможных входных данных — это означает, что вы можете передать функции случайные двоичные данные, и ОНА НИКОГДА НЕ ОТКАЖЕТСЯ, НЕЗАВИСИМО ЧТО. Вот что значит "защита от дурака".
Затем функция должна сделать все возможное, чтобы преобразовать кодировку в целевую. Если ему приходится выбрасывать весь мусор, который он не понимает, то это совершенно нормально и на самом деле является наиболее желательным результатом. Зачем пытаться спасти весь хлам? Просто выбросьте хлам. Скажите пользователю, что он не просто идиот из-за того, что использует что-то Microsoft, но нестандартный идиот из-за того, что использует что-то нестандартное из Microsoft... или за попытку отправить двоичные данные!
У меня точно такая же потребность (хотя моя потребность в PHP), и у меня также есть пользователи, которые по меньшей мере такие же дебилы, как и я, а иногда и более; однако они определенно приятнее и, без сомнения, более терпеливы.
Лучшее, что я нашел до сих пор (в PHP 5.3):
$fixed_string = iconv('ISO-8859-1', 'UTF-8//ИГНОРИРОВАТЬ//ПЕРЕВОД', $in_string);
Это пытается перевести все, что может, и просто отбрасывает весь мусор, в результате чего выводится допустимая строка UTF-8. Я также не смог сломать его или вызвать его сбой или отклонить какой-либо входящий текст или данные, даже загрузив его двоичными ненужными данными.
Найти iconv() и заставить его работать несложно; Что так сводит с ума и расточительно, так это чтение всего этого мусора и идиотизма, который, кажется, поддерживает так много программистов, имея дело с этим фиаско кодирования. Что стало с завидным (и респектабельным) менталитетом старой школы программирования «Сожгите идиотов»? Вернемся к основам. Используйте iconv() и выбросьте их мусор, и не будьте застенчивы, говоря им, что вы выбросили их мусор - короче говоря, не упустите возможность поколотить придурков, которые кормят вас мусором. И вы можете сказать им, что я сказал вам об этом.
Если все, что вы хотите сделать, это сохранить ASCII-совместимые символы и отбросить остальные, то в большинстве кодировок это сводится к удалению всех символов с установленным старшим битом, т. е. символов со значением выше 127. Это работает, потому что почти все наборы символов являются расширениями 7-битного ASCII.
Если это нормальная строка (т. е. не unicode
), вам нужно декодировать ее в произвольном наборе символов (например, iso-8859-1
, потому что она принимает любые байтовые значения), а затем закодировать в ascii, используя опцию ignore
или replace
для ошибок:
>>> orig = '1ä2äö3öü4ü'
>>> orig.decode('iso-8859-1').encode('ascii', 'ignore')
'1234'
>>> orig.decode('iso-8859-1').encode('ascii', 'replace')
'1??2????3????4??'
Шаг декодирования необходим, поскольку для использования кодирования вам нужна строка Unicode. Если у вас уже есть строка Unicode, это проще:
>>> orig = u'1ä2äö3öü4ü'
>>> orig.encode('ascii', 'ignore')
'1234'
>>> orig.encode('ascii', 'replace')
'1??2????3????4??'
'1ä2äö3öü4ü'.decode("ascii", "ignore")
. Тот факт, что вы используете упрощенный набор символов, не делает тип Unicode плохим выбором для текстовых строк IMO.
- person u0b34a0f6ae; 24.11.2009
'ignore'
, но когда вы используете 'replace'
, он даст вам строку Unicode с: u'1\ufffd\ufffd2\ufffd\ufffd\ufffd\ufffd3\ufffd\ufffd\ufffd\ufffd4\ufffd\ufffd'
- person intgr; 24.11.2009
ucity = u"\u041c\u043e\u0441\u043a\u0432\u0430"; ''.join(chr(ord(c) & 0x7f) for c in ucity.encode('koi8_r'))
производит 'mOSKWA'
.
- person John Machin; 26.11.2009