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()
10
demandé sur Tim Pietzcker 2011-02-22 10:25:04

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.

21
répondu Tim Pietzcker 2011-02-22 09:05:01

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.

2
répondu Mark Longair 2011-02-22 09:35:32

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

1
répondu eyquem 2011-02-22 11:07:56