Expression régulière pour supprimer les sauts de ligne
je suis un débutant complet de Python, et je suis coincé avec un problème de regex. J'essaie de supprimer le caractère line break À la fin de chaque ligne dans un fichier texte, mais seulement s'il suit une lettre minuscule, i.e. [a-z]
. Si la fin de la ligne se termine par une lettre minuscule, je veux remplacer le caractère rupture de ligne/nouvelle ligne par un espace.
C'est ce que j'ai obtenu jusqu'à présent:
import re
import sys
textout = open("output.txt","w")
textblock = open(sys.argv[1]).read()
textout.write(re.sub("[a-z]z","[a-z] ", textblock, re.MULTILINE) )
textout.close()
3 réponses
re.sub(r"(?<=[a-z])\r?\n"," ", textblock)
\Z
correspond seulement à la fin de la chaîne, après le dernier linebreak, donc ce n'est certainement pas ce dont vous avez besoin ici. \z
n'est pas reconnu par le moteur Python regex.
(?<=[a-z])
est un assertion positive lookbehind qui vérifie si le caractère avant la position courante est un caractère ASCII en minuscules. Ce n'est qu'à ce moment-là que le moteur regex tentera d'égaler une coupure de ligne.
de plus, Utilisez toujours des chaînes brutes avec des regexes. Rend backslashs plus facile à manipuler.
tout comme une réponse alternative, bien qu'elle prenne plus de lignes, je pense que la suivante peut être plus claire puisque l'expression régulière est plus simple:
import re
import sys
with open(sys.argv[1]) as ifp:
with open("output.txt", "w") as ofp:
for line in ifp:
if re.search('[a-z]$',line):
ofp.write(line.rstrip("\n\r")+" ")
else:
ofp.write(line)
... et cela évite de charger le fichier entier en chaîne de caractères. Si vous souhaitez utiliser moins de lignes, mais tout de même éviter lookbehind postive, vous pouvez faire:
import re
import sys
with open(sys.argv[1]) as ifp:
with open("output.txt", "w") as ofp:
for line in ifp:
ofp.write(re.sub('(?m)([a-z])[\r\n]+$','\1 ',line))
Les pièces de cette expression régulière:
(?m)
[activer correspondance multi-ligne]([a-z])
[correspondre à un seul minuscule que le premier groupe][\r\n]+
[correspondre à un ou plusieurs des retours de chariot ou de retours à la ligne, à couvrir\n
,\r\n
et\r
]$
[correspondre à la fin de la chaîne]
... et si cela correspond à la ligne, la lettre minuscule et la fin de ligne sont remplacées par \1
, qui sera la lettre minuscule suivie d'un espace.
mon point était que le fait d'éviter d'utiliser lookbehind positif pourrait rendre le code plus lisible
OK. Bien que, personnellement, je ne trouve pas que c'est moins lisible. C'est une question de goût.
Dans votre EDIT:
tout d'Abord, (?m) n'est pas nécessaire puisque pour la ligne à ifp: sélectionne une ligne à la fois et il n'y a donc qu'une nouvelle ligne à la fin de chaque ligne chaîne
Ensuite, $ comme il est placé, il n'a pas d'utilité parce qu'il correspondra toujours à la fin de la ligne de corde.
de toute façon, en adoptant votre point de vue, j'ai trouvé deux manières d'éviter l'assertion lookbehind:
with open(sys.argv[1]) as ifp:
with open("output.txt", "w") as ofp:
for line in ifp:
ante_newline,lower_last = re.match('(.*?([a-z])?$)',line).groups()
ofp.write(ante_newline+' ' if lower_last else line)
et
with open(sys.argv[1]) as ifp:
with open("output.txt", "w") as ofp:
for line in ifp:
ofp.write(line.strip('\r\n')+' ' if re.search('[a-z]$',line) else line)
la seconde est meilleure: une seule ligne, une correspondance simple à tester, pas besoin de groups (), logique naturelle
EDIT: oh, je me rends compte que ce le second code est simplement votre premier code réécrit en une ligne, Longair