Регулярное выражение для удаления разрывов строк

Я полный новичок в Python, и я застрял в проблеме с регулярным выражением. Я пытаюсь удалить символ разрыва строки в конце каждой строки в текстовом файле, но только если он следует строчной букве, то есть [a-z]. Если конец строки заканчивается строчной буквой, я хочу заменить символ разрыва строки/новой строки пробелом.

Это то, что у меня есть до сих пор:

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()

Ответ 1

Попробуйте

re.sub(r"(?<=[a-z])\r?\n"," ", textblock)

\Z совпадает только в конце строки, после последней строки, так что это определенно не то, что вам нужно здесь. \Z не распознается механизмом регулярного выражения Python.

(?<=[a-z]) является положительным утверждением lookbehind, который проверяет, является ли символ перед текущей позицией строчным символом ASCII. Только тогда двигатель регулярных выражений попытается сопоставить разрыв строки.

Также всегда используйте необработанные строки с регулярными выражениями. Делает обратную косую черту легче обрабатывать.

Ответ 2

Как альтернативный ответ, хотя он требует больше строк, я думаю, что следующее может быть более четким, поскольку регулярное выражение проще:

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)

... и это позволяет избежать загрузки всего файла в строку. Если вы хотите использовать меньшее количество строк, но все же избегайте появления postive lookbehind, вы можете сделать:

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))

Части этого регулярного выражения:

  • (?m) [включить многострочное сопоставление]
  • ([a-z]) [совпадение с одним строчным символом в качестве первой группы]
  • [\r\n]+ [совпадение с одним или несколькими символами возврата каретки или новой строки, для покрытия \n, \r\n и \r]
  • $ [соответствует концу строки]

... и если это соответствует строке, строчная буква и окончание строки заменяются на \\1, что будет буквой нижнего регистра, за которой следует пробел.

Ответ 3

моя точка зрения заключалась в том, что избежать использования положительного lookbehind может сделать код более удобочитаемым

OK. Хотя, лично, я не считаю это менее читаемым. Это вопрос вкуса.

В своем EDIT:

  • Во-первых, (? m) не требуется, так как для строки в ifp: выбирает одну строку за раз, и поэтому на конец каждой строки строки

  • Во-вторых, $, поскольку он помещен, не имеет полезности, потому что он всегда будет соответствовать концу строковой строки.

В любом случае, принимая вашу точку зрения, я нашел два способа избежать утверждения 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)

и

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)

второй лучше: только одна строка, простое совпадение с тестом, нет необходимости в группах(), естественно логике

EDIT: oh Я понимаю, что этот второй код - это просто ваш первый код, переписанный в одной строке, Longair