Проблема с регулярным выражением Python

Я пытаюсь извлечь ВСЕ разрешения экрана телефона из XML-файла WURFL с помощью приведенного ниже скрипта Python. Проблема в том, что я получаю только первое совпадение. Почему? Как я мог получить все совпадения?

XML-файл WURFL можно найти по адресу http://sourceforge.net/projects/wurfl/files/WURFL/latest/wurfl-latest.zip/download?use_mirror=freefr

def read_file(file_name):
    f = open(file_name, 'rb')
    data = f.read()
    f.close()
    return data

text = read_file('wurfl.xml')

import re
pattern = '<device id="(.*?)".*actual_device_root="true">.*<capability name="resolution_width" value="(\d+)"/>.*<capability name="resolution_height" value="(\d+)"/>.*</device>'
for m in re.findall(pattern, text, re.DOTALL):
    print(m)

person AOO    schedule 11.05.2011    source источник


Ответы (4)


Во-первых, используйте синтаксический анализатор XML вместо регулярных выражений. Вы будете счастливее в долгосрочной перспективе.

Во-вторых, если вы настаиваете на использовании регулярных выражений, используйте finditer() вместо findall().

В-третьих, ваше регулярное выражение соответствует от первой записи до последней (.* является жадным, и вы установили режим DOTALL), поэтому либо просмотрите первый абзац, либо хотя бы измените свое регулярное выражение на

pattern = r'<device id="(.*?)".*?actual_device_root="true">.*?<capability name="resolution_width" value="(\d+)"/>.*?<capability name="resolution_height" value="(\d+)"/>.*?</device>'

Кроме того, всегда используйте необработанные строки с регулярными выражениями. \d работает, однако \b будет вести себя неожиданно в "нормальной" строке.

person Tim Pietzcker    schedule 11.05.2011
comment
Спасибо за ваш вклад, но у меня все еще есть только одно совпадение - person AOO; 11.05.2011
comment
Упс, я проглядел один жадный квантификатор. Пожалуйста, попробуйте еще раз с отредактированным регулярным выражением. - person Tim Pietzcker; 11.05.2011

Это странность в поведении findall, в частности, findall возвращает только первую совпадающую группу из каждого совпадения с образцом. См. этот вопрос.

person verdesmarald    schedule 11.05.2011
comment
Уточнение: я получаю интересующие меня группы, но findall по какой-то причине возвращает только ОДНО совпадение (первое совпадение). - person AOO; 11.05.2011

Вы используете «жадные» совпадения: .* будет соответствовать столько текста, сколько сможет захватить, что означает, что .* перед <capabilities> соответствует большей части файла.

text = open('wurfl.xml').read()
pattern = r'<device id="(.*?)".*?actual_device_root="true">.*?<capability name="resolution_width" value="(\d+)"/>.*?<capability name="resolution_height" value="(\d+)"/>.*?</device>'
for m in re.findall(pattern, text, re.DOTALL):
    print m
person Dan    schedule 11.05.2011

Я, конечно, не против обработки xml с помощью регулярного выражения, если требования просты, но, возможно, в этом случае лучше использовать настоящий парсер xml. Используя модуль stdlib etree и разбрызгивание (imho) отвратительных xpaths:

import xml.etree.ElementTree as ET

def capability_value(cap_elem):
    if cap_elem is None:
        return None
    return int(cap_elem.attrib.get('value'))

def devices(wurfl_doc):
    for el in wurfl_doc.findall("/devices/device[@actual_device_root='true']"):
        width = el.find("./group[@id='display']/capability[@name='resolution_width']")
        width = capability_value(width)
        height = el.find("./group[@id='display']/capability[@name='resolution_height']")
        height = capability_value(height)
        device = {
            'id' : el.attrib.get('id'), 
            'resolution' : {'width': width, 'height': height}
        }
        yield device

doc = ET.ElementTree(file='wurfl.xml')
for device in devices(doc):
    print device
person Rob Cowie    schedule 11.05.2011