Удаление крошек из реструктурированных текстовых файлов в pycharm - PullRequest
2 голосов
/ 11 июня 2019

У меня есть около 13 000 файлов, из которых мне нужно удалить панировочные сухари. Шаблон в начале каждого файла выглядит примерно так:

Title
=====

| |image0| `link <link1.html>`__ |image1| ::
  `link2 <link2.html>`__ ::
  `link3 <link3.html>`__
| **Introduced** : VersionXXX

Однако в некоторых файлах раздел между строками заголовка и последней строкой равен 2 или 4, в зависимости от глубины дерева. Независимо от того, какие строки между строками заголовка и последней строкой показаны здесь, я хочу полностью удалить эту среднюю часть. Я не могу понять, как это сделать, и был бы признателен за помощь. Я использую pycharm, и у них есть инструмент регулярных выражений (с которым я еще не добился успеха), но я также счастлив использовать альтернативы, такие как sed или python, для перебора файлов.

Ожидаемый результат:

Title
=====

| **Introduced** : VersionXXX

Спасибо за все отличные решения. Окончательное решение , чтобы избежать записи в отдельный файл:

import os

src_dir = '/PycharmProjects/docs/testfiles'
logf = open('failed_file_log.txt', 'w')

for filename in os.listdir(src_dir):
    print(filename)

    with open('{}/{}'.format(src_dir, filename), 'r') as f:
        lines = f.readlines()
    with open('{}/{}'.format(src_dir, filename), 'w') as f:
        try:
            for i in range(3):
                f.write(lines[i])
            copy = False
            for line in lines:
                if copy:
                    f.write(line)
                elif line.startswith('| **Introduced**'):
                    copy = True
                    f.write(line)
        except Exception as e:
            logf.write('Failed to rewrite {}'.format(filename))
        finally:
            pass

Ответы [ 5 ]

2 голосов
/ 11 июня 2019

Поскольку sed помечен в вопросе OP, следующие два однострочных для получения желаемого результата:

sed -n  '/Title/{N;N;p}; /Introduced/{p}' input
Title
=====

| **Introduced** : VersionXXX

Или

awk:

awk '/Title/{print;getline;print;getline;print}/Introduced/{print}' input
Title
=====

| **Introduced** : VersionXXX
1 голос
/ 11 июня 2019

Поскольку вы ищете в основном фиксированные шаблоны, я бы использовал Python без регулярных выражений для копирования файлов.Процесс довольно прост: скопируйте первые три строки, затем пропустите все, пока не дойдете до | **Introduced**, и скопируйте остальные.

with open('myfile.rst') as fin, open('myfile_out.rst') as fout:
    for _ in range(3):
        fout.write(next(fin))
    copy = False
    for line in fin:
        if copy:
            fout.write(line)
        elif line.startswith('| **Introduced**'):
            copy = True
            fout.write(line)

Применение этого фрагмента к иерархии файлов и перемещение выводаВернуться к введенному имени оставлено в качестве упражнения для читателя.

1 голос
/ 11 июня 2019

Вы можете использовать 2 группы захвата и сопоставлять то, что находится между ними, используя повторяющийся шаблон, который проверяет, не каждая ли строка начинается с шаблона в качестве последней строки, используя отрицательный прогноз (?!

Затем в замене используйте эти 2 группы, в python, используя re.sub, эта замена будет r'\1\2'.

(\bTitle\n=+\n)(?:\n(?!\| \*\*).*)*(\n\| \*\*Introduced\*\* : Version.*)

Объяснение

  • (\bTitle\n=+\n) Захват группы 1, совпадение с названием, новой строкой, 1+ раз a + и новой строкой
  • (?: Группа без захвата
    • \n(?!\| \*\*).* Совпадение с новой строкой и утверждение того, что прямо справа, не является | ** с использованием отрицательного взгляда. Затем сопоставьте 0+ раз любому символу кроме символа новой строки
  • )* Закрыть группу без захвата и повторить 0+ раз
  • (\n\| \*\*Introduced\*\* : Version.*) Захватить группу 2, сопоставить новую строку и шаблон, соответствующий последней строке

Regex demo

0 голосов
/ 11 июня 2019

sed используется, но требует безумных навыков для многострочной обработки, как вам нужно.Вот альтернатива этому проверенному и истинному * nix текстовому языку обработки, awk; -)

**cleanup.awk**
#!/bin/awk -f
{
  # print "dbg:$0="$0
}
/^$/{
  print $0
  inside_unneeded=1;
}
{
  if ($0 ~ /^\| \*\*Introduced\*\*/) {
    print $0
    inside_unneeded=0
  }
  else if (! inside_unneeded) {
    print $0
  }

Вам нужно будет

chmod 755 cleanup.awk

и запустить его как

cleanup.awk file > file.new && /bin/rm file

Если вы можете позволить себе резервное копирование (рекомендуется), тогда выполните && mv file file.sav && mv file.new file.ИЛИ вы можете перенаправить в другой каталог, и тогда вам не придется заниматься обработкой &&, т.е.cleanup.awk file > /alt/path/for/new/data/file.

будет выводить

Title
=====

| **Introduced** : VersionXXX

Возможно, существует способ значительно уменьшить размер этого сценария, используя awk сокращенную логику, но я оставляю его в дешифруемом состоянии для общегообщедоступный, знаком с логикой типа if/else if/else.

Все блоки (код между { ... }. выполняются для каждой строки ввода, в то время как блок, начинающийся с /^$/, обрабатывается только для пустых строк. Если выв этих пустых строках должен быть пробел, вместо этого вам понадобится /^[ <tab>]*$/{ (и не вводите <tab>, вставьте обычный tab символ с клавиатуры).

IHTH.

0 голосов
/ 11 июня 2019

Это выражение использует три группы захвата, и наша нежелательная часть находится во второй, которую мы можем просто заменить ($1$3).

(.+\s*=====\s*)([\s\S]*)(\|\s+\*\*Introduced\*\* : .+)

Демо

Тест

# coding=utf8
# the above tag defines encoding for this document and is for Python 2.x compatibility

import re

regex = r"(.+\s*=====\s*)([\s\S]*)(\|\s+\*\*Introduced\*\* : .+)"

test_str = ("Title\n"
    "=====\n\n"
    "| |image0| `link <link1.html>`__ |image1| ::\n"
    "  `link2 <link2.html>`__ ::\n"
    "  `link3 <link3.html>`__\n"
    "| **Introduced** : VersionXXX")

subst = "\\1\\3"

# You can manually specify the number of replacements by changing the 4th argument
result = re.sub(regex, subst, test_str, 0, re.MULTILINE)

if result:
    print (result)

# Note: for Python 2.7 compatibility, use ur"" to prefix the regex and u"" to prefix the test string and substitution.
...