Python нежадное регулярное выражение для очистки xml

#python #regex #regex-greedy #non-greedy

#python #Регулярное выражение #регулярное выражение-жадное #нежадный

Вопрос:

У меня есть файл «xml file», в котором есть несколько нежелательных символов

 <data>
  <tag>blar </tag><tagTwo> bo </tagTwo>
  some extra 
  characters not enclosed that I want to remove
  <anothertag>bbb</anothertag>
</data>
  

Я думал, что следующая нежадная замена удалит символы, которые не были должным образом заключены в <sometag></sometag>

 re.sub("</([a-zA-Z] )>.*?<","</\1><",text)
            ^          ^ ^     ^      text is the xml txt.  
         remember tag, | |     put tag back without and reopen next tag
               read everything until the next '<' (non-gready) 
  

Это регулярное выражение, похоже, находит только позицию, указанную с [[]] помощью </tag>[[]]<tagTwo>
Что я делаю не так?

РЕДАКТИРОВАТЬ: мотивация для этого вопроса решена (см. Комментарии, у меня было отклонение amp; в файле xml, из-за которого он не анализировался — это не имело никакого отношения к символам, которые я хочу удалить). Тем не менее, мне все еще интересно, возможно ли регулярное выражение (и что было не так с моей попыткой), поэтому я не удаляю вопрос.

Комментарии:

1. @cwallenpoole: я не пытаюсь анализировать xml. Я пытаюсь очистить XML-файл, чтобы он мог быть принят синтаксическим анализатором. Любые предложения, отличные от регулярных выражений, относительно того, как это сделать, приветствуются

2. Какой анализатор это не принимает?

3. @IgnacioVazquez-Abrams xml.dom.mindom Я отредактировал вопрос, чтобы включить обратную трассировку

4. Похоже, у вас там есть случайный амперсанд.

5. Ссылка на символьную сущность для амперсанда равна amp;amp; . Вы не можете просто выполнить глобальный S R, так как это может испортить другие действительные вещи.

Ответ №1:

Точка не соответствует новой строке, если вы не укажете re.DOTALL флаг.

 re.sub("</([a-zA-Z] )>.*?<","</\1><",text, flags=re.DOTALL)
  

должно работать нормально. (Если это не так, виноват мой python, а не регулярное выражение. Пожалуйста, исправьте.)

Я думаю, что хорошей практикой является быть как можно более точным при определении классов символов, которые должны повторяться. Это помогает предотвратить катастрофическое отслеживание возврата. Поэтому я бы использовал [^<]* вместо .*? с дополнительным бонусом, что теперь он находит случайные символы после последнего тега. Для этого больше не нужен re.DOTALL флаг, поскольку [^<] он соответствует новым строкам.

Ответ №2:

  "</[^>] ?>[^<>] ?<" 
  

в ipython:

 In [1]: a="<data>  <tag>blar </tag><tagTwo> bo </tagTwo>  some extra   characters not enclosed that I want to remove  <anothertag>bbb</anothertag></data>"

In [2]: import re

In [3]: re.sub( "(</[^>] ?>)[^<>] ?<" ,"\1<",a)
Out[3]: '<data>  <tag>blar </tag><tagTwo> bo </tagTwo><anothertag>bbb</anothertag></data>'