2009-07-22 2 views
0

Я использую python 2.4, и у меня возникают некоторые проблемы с регулярными выражениями unicode. Я попытался составить очень четкий и краткий пример моей проблемы. Похоже, что есть некоторая проблема с тем, как Python распознает различные кодировки символов или проблему с моим пониманием. Большое спасибо за то, что посмотрели!Python Unicode Regular Expression

#!/usr/bin/python 
# 
# This is a simple python program designed to show my problems with regular expressions and character encoding in python 
# Written by Brian J. Stinar 
# Thanks for the help! 

import urllib # To get files off the Internet 
import chardet # To identify charactor encodings 
import re # Python Regular Expressions 
#import ponyguruma # Python Onyguruma Regular Expressions - this can be uncommented if you feel like messing with it, but I have the same issue no matter which RE's I'm using 

rawdata = urllib.urlopen('http://www.cs.unm.edu/~brian.stinar/legal.html').read() 
print (chardet.detect(rawdata)) 
#print (rawdata) 

ISO_8859_2_encoded = rawdata.decode('ISO-8859-2') # Let's grab this as text 
UTF_8_encoded = ISO_8859_2_encoded.encode('utf-8') # and encode the text as UTF-8 
print(chardet.detect(UTF_8_encoded)) # Looks good 

# This totally doesn't work, even though you can see UNSUBSCRIBE in the HTML 
# Eventually, I want to recognize the entire physical address and UNSUBSCRIBE above it 
re_UNSUB_amsterdam = re.compile(".*UNSUBSCRIBE.*", re.UNICODE) 
print (str(re_UNSUB_amsterdam.match(UTF_8_encoded)) + "\t\t\t\t\t--- RE for UNSUBSCRIBE on UTF-8") 
print (str(re_UNSUB_amsterdam.match(rawdata)) + "\t\t\t\t\t--- RE for UNSUBSCRIBE on raw data") 

re_amsterdam = re.compile(".*Adobe.*", re.UNICODE) 
print (str(re_amsterdam.match(rawdata)) + "\t--- RE for 'Adobe' on raw data") # However, this work?!? 
print (str(re_amsterdam.match(UTF_8_encoded)) + "\t--- RE for 'Adobe' on UTF-8") 

''' 
# In additon, I tried this regular expression library much to the same unsatisfactory result 
new_re = ponyguruma.Regexp(".*UNSUBSCRIBE.*") 
if new_re.match(UTF_8_encoded) != None: 
    print("Ponyguruma RE matched! \t\t\t--- RE for UNSUBSCRIBE on UTF-8") 
else: 
    print("Ponyguruma RE did not match\t\t--- RE for UNSUBSCRIBE on UTF-8") 

if new_re.match(rawdata) != None: 
    print("Ponyguruma RE matched! \t\t\t--- RE for UNSUBSCRIBE on raw data") 
else: 
    print("Ponyguruma RE did not match\t\t--- RE for UNSUBSCRIBE on raw data") 

new_re = ponyguruma.Regexp(".*Adobe.*") 
if new_re.match(UTF_8_encoded) != None: 
    print("Ponyguruma RE matched! \t\t\t--- RE for Adobe on UTF-8") 
else: 
    print("Ponyguruma RE did not match\t\t\t--- RE for Adobe on UTF-8") 

new_re = ponyguruma.Regexp(".*Adobe.*") 
if new_re.match(rawdata) != None: 
    print("Ponyguruma RE matched! \t\t\t--- RE for Adobe on raw data") 
else: 
    print("Ponyguruma RE did not match\t\t\t--- RE for Adobe on raw data") 
''' 

Я работаю над проектом замены, и у меня возникают трудное время с не-ASCII кодируются файлы. Эта проблема является частью более крупного проекта - в конечном итоге я хотел бы заменить текст другим текстом (я получил это в ASCII, но пока не могу идентифицировать вхождения в других кодировках.) Еще раз спасибо.

http://brian-stinar.blogspot.com

-Брайан J. Stinar-

+0

Что-то, что полностью отсутствующими из вашего описания является способ, в котором ваш код не удается. Вы пишете в своем коде * «# Это полностью не работает» *, но вы не даете никаких подсказок о том, как это не работает. Печатаются ли печатные строки? Вы получаете сообщения об ошибках/трассировки стека? – ThomasH

ответ

2

Вы, вероятно, хотите либо включить флаг DOTALL или вы хотите использовать метод search вместо метода match , то есть:

# DOTALL makes . match newlines 
re_UNSUB_amsterdam = re.compile(".*UNSUBSCRIBE.*", re.UNICODE | re.DOTALL) 

или:

# search will find matches even if they aren't at the start of the string 
... re_UNSUB_amsterdam.search(foo) ... 

Это даст вам разные результаты, но оба должны дать вам спички. (Посмотрите, какой из них вам нужен.)

В стороне: вы, похоже, путаете закодированный текст (который является байтами) и декодированный текст (символы). Это не редкость, особенно в pre-3.x Python. В частности, это очень подозрительно:

ISO_8859_2_encoded = rawdata.decode('ISO-8859-2') 

Вы де -coding с ISO-8859-2, не ан -coding, так называют эту переменную "декодируется". (Почему бы не «ISO_8859_2_decoded»? Поскольку ISO_8859_2 - это кодировка. Декодированная строка больше не имеет кодировки.)

Остальная часть вашего кода пытается выполнить совпадения по rawdata и по UTF_8_encoded (обе кодированные строки), когда вероятно, вместо этого следует использовать строку декодированного юникода.

+0

спасибо. После добавления флага re.DOTALL это было точно так, как я ожидал. Кажется, что. * Ведет себя по-разному на ASCII; в ASCII это совпадало с новыми строками для меня, но с декодированным не-ASCII не было, но я, возможно, просто не понял этого. Спасибо за разъяснение закодированного текста и расшифрованного текста. Это мой первый проект, посвященный различным кодировкам, и я ценю разъяснения. –

0

С настройками флага по умолчанию,. * Не соответствует символам новой строки. UNSUBSCRIBE появляется только один раз, после первой новой строки. Adobe появляется перед первой новой строкой. Вы можете исправить это, используя re.DOTALL.

ОДНАКО вы не изучили, что получили с совпадением Adobe: это ширина 1478 байтов! Включите re.DOTALL, и он (и соответствующий шаблон UNSUBSCRIBE) будет соответствовать всему тексту !!

Вам определенно нужно потерять трейлинг. * - вам неинтересно, и это замедляет матч. Также вы должны потерять ведущий. * И использовать search() вместо match().

В этом случае флаг re.UNICODE бесполезен для вас - прочитайте руководство и посмотрите, что он делает.

Почему вы перекодируете свои данные в UTF-8 и ищете? Просто оставьте в Юникоде.

Кто-то отметил, что в целом вы должны декодировать Ӓ и т.д. рюшечки, прежде чем делать какие-либо серьезные работы на ваших данных ... но не упомянул и т.д. рюшечки «, с которым ваши данные приправлены :-)

0

Ваш вопрос о регулярных выражениях, но ваша проблема может быть решена без них; вместо этого используйте стандартную строку replace.

import urllib 
raw = urllib.urlopen('http://www.cs.unm.edu/~brian.stinar/legal.html').read() 
decoded = raw.decode('iso-8859-2') 
type(decoded) # decoded is now <type 'unicode'> 
substituted = decoded.replace(u'UNSUBSCRIBE', u'whatever you prefer') 

Если ничего другого, выше показано, как обрабатывать кодирование: просто декодировать в строку Юникода и работать с этим. Но учтите, что это работает только для случая, когда вы делаете только одно или очень небольшое количество подстановок (и эти подстановки не основаны на шаблонах), потому что replace() может обрабатывать только одну подстановку за раз.

Для обеих строк и шаблонов на основе замен вы можете сделать что-то подобное, чтобы произвести несколько замен сразу:

import re 
REPLACEMENTS = ((u'[aA]dobe', u'!twiddle!'), 
       (u'UNS.*IBE', u'@[email protected]'), 
       (u'Dublin', u'Sydney')) 

def replacer(m): 
    return REPLACEMENTS[list(m.groups()).index(m.group(0))][1] 

r = re.compile('|'.join('(%s)' % t[0] for t in REPLACEMENTS)) 
substituted = r.sub(replacer, decoded)