Работа со строками Юникода, а не со строками байтов. Ваш источник кодируется как UTF-8, поэтому символы кодируются от одного до четырех байтов каждый. Поможет декодирование в строки Unicode или использование констант Unicode. Код также, кажется, основан на Python 2, поэтому в узких сборках Python 2 (по умолчанию в Windows) у вас все еще будет проблема. У вас также могут возникнуть проблемы, если у вас есть графемы, построенные с двумя или более кодами Unicode:
# coding: utf-8
import re
t = u"? [°] \n € dsf $ ¬ 1 Ä 2 t3¥4Ú";
print re.sub(ur'[^A-Za-z0-9 !#%&()*+,-./:;<=>?[\]^_{|}~"\'\\]', '_', t, flags=re.UNICODE)
Вывод (в узкой сборке Windows Python 2.7):
__ [_] _ _ dsf _ _ 1 _ 2 t3_4_
Обратите внимание, что у первого смайлика по-прежнему двойное подчеркивание. Символы Unicode, превышающие U + FFFF, кодируются как суррогатные пары. Это можно сделать, явно проверив их. Первая кодовая точка суррогатной пары - от U + D800 до U + DBFF, а вторая - от U + DC00 до U + DFFF:
# coding: utf-8
import re
t = u"? [°] \n € dsf $ ¬ 1 Ä 2 t3¥4Ú";
print re.sub(ur'[\ud800-\udbff][\udc00-\udfff]|[^A-Za-z0-9 !#%&()*+,-./:;<=>?[\]^_{|}~"\'\\]', '_', t, flags=re.UNICODE)
Выход:
_ [_] _ _ dsf _ _ 1 _ 2 t3_4_
Но у вас все равно будет проблема со сложными смайликами:
# coding: utf-8
import re
t = u"????????";
print re.sub(ur'[\ud800-\udbff][\udc00-\udfff]|[^A-Za-z0-9 !#%&()*+,-./:;<=>?[\]^_{|}~"\'\\]', '_', t, flags=re.UNICODE)
Выход:
___________