【问题标题】:Removing breadcrumbs from restructured text files in pycharm从pycharm中的重组文本文件中删除面包屑
【发布时间】:2019-10-26 03:08:04
【问题描述】:

我有大约 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

【问题讨论】:

  • 你能分享你对上述文件的预期输出吗
  • 完成。谢谢。
  • 标题是否总是用至少 3 个 = 下划线,并且您要保存的第一行是否总是以 | **Introduced** 开头?
  • 第 2 行总是有与第 1 行中的字符相等数量的 =。是的,最后一行总是以这种方式开始。 (此模式的最后一行,顺便说一句,不在整个文件中。)
  • @Jason。明白了。

标签: python regex sed pycharm restructuredtext


【解决方案1】:

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

如果您有能力保留备份(推荐),请使用&amp;&amp; mv file file.sav &amp;&amp; mv file.new file。或者您可以重定向到不同的目录,然后不必处理任何&amp;&amp; 处理,即。 cleanup.awk file &gt; /alt/path/for/new/data/file

将产生输出

Title
=====

| **Introduced** : VersionXXX

可能有一种方法可以使用awk 速记逻辑来显着减小此脚本的大小,但我将其置于熟悉if/else if/else 类型逻辑的普通公众可辨认的状态。

所有块({ ... }. 之间的代码。为每一行输入执行,而以/^$/ 开头的块仅针对空行处理。如果这些空行上有空格,则需要@改为 987654334@(不要输入 &lt;tab&gt;,从键盘插入一个普通的 tab 字符)。

IHTH。

【讨论】:

  • 谢谢你,把它保存起来以备将来使用。
  • 必须修复一个不完整的变量名。现在已修复(我认为 ;-) )。祝你好运。
【解决方案2】:

由于您正在寻找大多数固定模式,我会使用没有正则表达式的 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)

将此 sn-p 应用于文件层次结构并将输出移回输入名称留作读者练习。

【讨论】:

  • 这对我的应用程序最有意义。非常感谢你。为了避免写出单独的文件(包含在我的问题末尾),我确实做了一些改动
  • 在销毁原始文件之前将输出完全写入通常更安全。
  • 很公平,但在这种情况下,它都在一个 git repo 中,所以如果出现任何故障,我可以将更改恢复到我的分支。
  • 有道理。
【解决方案3】:

您可以使用 2 个捕获组并通过使用重复模式来匹配中间的内容,该模式检查每行是否不以模式开头作为最后一行,使用负前瞻(?!

然后在替换中使用这两个组,在 python 中使用re.sub,替换将是r'\1\2'

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

说明

  • (\bTitle\n=+\n) 捕获组 1,匹配 Title,换行,1+ 次 + 和换行
  • (?:非捕获组
    • \n(?!\| \*\*).* 匹配换行符并断言右边的内容不是 | ** 使用负前瞻。然后匹配除换行符以外的任何字符 0+ 次
  • )*关闭非捕获组并重复0+次
  • (\n\| \*\*Introduced\*\* : Version.*)捕获组2,匹配换行符和匹配最后一行的模式

Regex demo

【讨论】:

    【解决方案4】:

    由于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
    

    【讨论】:

    • 我对 sed 为解决这个问题所做的努力表示了纠正!好节目!
    【解决方案5】:

    这个表达式使用了三个捕获组,我们不需要的部分在第二个,我们可以简单地替换它($1$3)。

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

    Demo

    测试

    # 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.
    

    【讨论】:

    • 谢谢!会后不久将进行测试。需要明确的是,标题本身是一种模式,并且根据页面的不同长度是可变的。 = 行总是与标题字符串等长。
    • 谢谢你。我采用了 Mad Scientist 的简单性,但我很欣赏正则表达式的洞察力。存放起来以备后用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    相关资源
    最近更新 更多