2013-02-11 5 views
0

Je suis à la recherche d'une expression rationnelle pour identifier ce bloc pour un modèle afin que je puisse fournir un texte pour remplacer ce bloc entierpython remplacer chaîne avec regexp

<div> 
{% for link in links %} 
    textext 
{% endfor %} 
</div> 

et obtenir quelque chose comme ça

<div> 
mytext 
</div> 
+0

J'ai essayé re.sub ('{% pour le lien dans les liens%}.* {% endfor%} ',' mytest ', stringHTML) – Zed

+0

@Zed Ce que vous avez dit avoir essayé était presque le bon. Il suffisait d'ajouter un ''? '' Après '. *' Pour rendre l'expression rationnelle non répréhensible, et de placer le drapeau 're.DOTALL' pour que le point puisse correspondre aux nouvelles lignes' '\ n''. - Maintenant, vous devriez regarder ma solution, dans laquelle j'ai utilisé '' [^ \ r \ n] '' pour obtenir un symbole qui ne correspond toujours pas aux extrémités des lignes même dans un contexte DOTALL, généralisant ainsi l'expression rationnelle à varié forme de blocs '' {% ....%} '' - Enfin, vous devriez considérer si la réponse de Logan mérite vraiment d'être acceptée. J'ose dire que je ne le pense pas – eyquem

Répondre

1

Essayez:

re.sub('\{.*[\w\s]*.*\}','mytext',txt) 

Sortie:

'<div>\n mytext\n</div>' 

\{ correspond à la première accolade, puis .*[\w\s]*.* correspond à tous les autres (y compris les espaces et les sauts de ligne) jusqu'à ce que la dernière accolade \}.

Vous pouvez être plus précis avec quelque chose comme:

re.sub('\{% for link in links.*[\w\s]*.*end for %\}','mytext',txt) 

et vous pouvez être sûr qu'il ne correspondra une boucle du type que vous avez spécifié. L'éditeur a souligné que ma réponse était insuffisante pour un certain nombre de cas, en particulier s'il y avait des symboles au milieu. Au risque de malentendu naïvement pourquoi ma solution ne fonctionne pas, j'ai simplement ajouté un peu plus à mon modèle qui correspond avec succès même ses cas de test, donc nous allons voir si cela fonctionne:

re.sub('\{.*[\W\w\s]*.*\}', 'mytext', txt) 

RÉSULTAT (où txt est par exemple Pink Floyd de Eyquems):

"Pink Floyd" 
<div> 
mytext 
</div> 
"Fleetwood Mac" 

donc, je pense que l'ajout de tous les symboles non-alphanumériques le fixe. Ou je l'ai peut-être cassé encore plus évidemment pour un autre cas. Je suis sûr que quelqu'un va le signaler. :) '

EDIT2: Il convient également de noter que nos deux solutions échouent dans le cas où il y a plus d'un for -loop sur la page. Exemple:

"Beatles" 
<div> 
{% for link in links %} 
    iiiY=uuu 
    12345678 
{% endfor %} 
</div> 
"Tino Rossi" 
{ for link in links % } 
    asdfasdfas 
{% endfor% } 

cède

"Beatles" 
<div> 
mytext 

Et Réduisons le reste en faisant correspondre le jeu suivant APRÈS la.

EDIT 2: l'eyquem est à nouveau à droite dans la fixation de son pour ne pas couper le si il y en a un après. Son correctif répare le mien aussi:

re.sub('\{.*[\W\w\s]*?.*\}', 'mytext', txt) 

est le nouveau modèle.

+0

cela fonctionne, merci! – Zed

+0

@Logan @Zed J'ai mis à jour ma solution (modification minimale à faire: ajouter un signe ''? '' Après '' '. +' '' Pour rendre l'expression rationnelle non réorganisée) et maintenant cela fonctionne parfaitement sans avoir la faille de Logan un, désolé. – eyquem

+0

@Logan Non cela ne fonctionne toujours pas quand il y a un signe ''} '' placé dans le bloc entre '' {% pour le lien dans les liens%} '' et '' {% endfor%} ''. Vous devriez exécuter avec des groupes dans le modèle, pour voir comment il fonctionne: '' '(\ {. *) ([\ W \ w \ s] *?) (. * \})' '' – eyquem

0

L'approche de marteau de forgeron serait:

In [540]: txt = """<div> 
{% for link in links %} 
    textext 
{% endfor %} 
</div>""" 

In [541]: txt 
Out[541]: '<div>\n {% for link in links %}\n  textext\n {% endfor %}\n</div>' 

In [542]: re.sub("(?s)<div>.*?</div>", "<div>mytext</div>", txt) 
Out[542]: '<div>mytext</div>' 
+0

merci mais mon erreur est que je n'ai pas dit, parfois il y aura div autour pour bloquer et parfois pas, je ne peux pas savoir ça – Zed

+1

Voilà pourquoi utiliser regexp sur ce genre de les choses sont généralement une mauvaise idée. Utiliser '' lxml'' ou l'un des autres parseurs serait une meilleure idée car vous pouvez affiner le 'div'' dont vous parlez en utilisant Xpath. Je vous ai dit que c'était un marteau, vous avez besoin d'un scalpel. Xml/Html sont des documents structurés pour une raison. – sotapme

+0

@sotapme Je ne connaissais pas la notation '' (? S) '' dans le modèle de regex. J'ai essayé les codes et j'ai compris que c'est équivalent au drapeau re.DOTALL. Mais je n'ai jamais vu aucune information concernant ce '' (? S) '' dans les documentations Python. Il n'appartient pas à Python 3 seulement parce qu'il fonctionne dans mes codes Python 2. Où puis-je trouver de la documentation donnant des informations à ce sujet, s'il vous plaît? – eyquem

1

Je regrette de dire que la anwer de Logan ne fonctionne pas dans les cas suivants:

import re 

ss1 = '''"Pink Floyd" 
<div> 
{% for link in links %} 
    aaaY}eee 
    12345678 
{% endfor %} 
</div> 
"Fleetwood Mac"''' 

pat = '(\{.*)([\w\s]*)(.*)(\})' 
print ss1 
print '---------------------------' 
for el in re.findall(pat,ss1): 
    print el 
print '---------------------------' 
print re.sub(pat,':::::',ss1) 

RÉSULTAT

"Pink Floyd" 
<div> 
{% for link in links %} 
    aaaY}eee # <--------- } here 
    12345678 
{% endfor %} 
</div> 
"Fleetwood Mac" 
--------------------------- 
('{% for link in links %}', '\n aaaY', '', '}') 
('{% endfor %', '', '', '}') 
--------------------------- 
"Pink Floyd" 
<div> 
:::::eee 
    12345678 
::::: 
</div> 
"Fleetwood Mac" 

.
.

import re 

ss2 = '''"Beatles" 
<div> 
{% for link in links %} 
    iiiY=uuu # <-------- = here 
    12345678 
{% endfor %} 
</div> 
"Tino Rossi"''' 

pat = '(\{.*)([\w\s]*)(.*)(\})' 
print ss2 
print '---------------------------' 
for el in re.findall(pat,ss2): 
    print el 
print '---------------------------' 
print re.sub(pat,':::::',ss2) 

RÉSULTAT

"Beatles" 
<div> 
{% for link in links %} 
    iiiY=uuu 
    12345678 
{% endfor %} 
</div> 
"Tino Rossi" 
--------------------------- 
('{% for link in links %', '', '', '}') 
('{% endfor %', '', '', '}') 
--------------------------- 
"Beatles" 
<div> 
::::: 
    iiiY=uuu 
    12345678 
::::: 
</div> 
"Tino Rossi" 

Le problème est le suivant (les résultats de findall() mis à mon aide de code à comprendre):

Les premiers .* runs aussi longtemps qu'il doesn 't rencontre une nouvelle ligne.
Puis [\w\s]* s'exécute tant qu'il y a des caractères de ces catégories: lettres, chiffres, trait de soulignement, espaces blancs.
Parmi les espaces sont les retours à la ligne, alors [\w\s]* peut passer d'une ligne à l'autre.
Mais si un caractère ne se trouvant pas dans ces catégories est rencontré par [\w\s]*, il s'arrête à ce caractère.

S'il s'agit d'un }, le dernier .* correspond à '' avant cela }.
Ensuite, la regex cherche le prochain match.

S'il s'agit d'un =, le dernier .* ne peut pas correspondre à la suite du texte avant d'atteindre le } suivant, car il ne peut pas passer le saut de ligne suivant. D'où le résultat différent qu'avec un } dans le texte.

.

Remplacement .* avec .+ ne change rien car il sera vu par le remplacement .* avec .+ dans les codes ci-dessus.

.

.

Ma solution

Je propose la Patern dans ce code:

import re 
pat = ('\{%[^\r\n]+%\}' 
     '.+?' 
     '\{%[^\r\n]+%\}') 


ss = '''"Pink Floyd" 
<div> 
{% for link in links %} 
    aaaY}eee 
    12345678 
{% endfor %} 
</div> 
"Fleetwood Mac" 
"Beth Hart" 
"Jimmy Cliff" 
"Led Zepelin" 
Beatles" 
<div> 
{% for link in links %} 
    iiiY=uuu 
    12345678 
{% endfor %} 
</div> 
"Tino Rossi"''' 


print '\n',ss,'\n\n---------------------------\n' 
print re.sub(pat,':::::',ss,flags=re.DOTALL) 

entraînant

"Pink Floyd" 
<div> 
{% for link in links %} 
    aaaY}eee 
    12345678 
{% endfor %} 
</div> 
"Fleetwood Mac" 
"Beth Hart" 
"Jimmy Cliff" 
"Led Zepelin" 
Beatles" 
<div> 
{% for link in links %} 
    iiiY=uuu 
    12345678 
{% endfor %} 
</div> 
"Tino Rossi" 

--------------------------- 

"Pink Floyd" 
<div> 
::::: 
</div> 
"Fleetwood Mac" 
"Beth Hart" 
"Jimmy Cliff" 
"Led Zepelin" 
Beatles" 
<div> 
::::: 
</div> 
"Tino Rossi" 

EDIT

Simpler:

pat = ('\{%[^}]+%\}' 
     '.+?' 
     '\{%[^}]+%\}') 

que si les lignes {%.....%} ne contiennent pas le signe }

+0

Eh bien, c'est malheureux . :(Bonne réponse, cependant, je ne voulais pas le rendre trop compliqué (paresseux :)) mais je suppose qu'il ne l'a pas coupé cette fois. – Logan

+0

@Logan Puis-je mettre un commentaire pour l'OP pour lui signaler ma réponse? Le risque est pour vous, qu'il change son acceptation de réponse. – eyquem

+0

Eh bien, j'allais le faire moi-même, sauf que je pense avoir corrigé ma réponse. Je vais mettre à jour le mien et voyons ce que vous en pensez. – Logan