Python 如何使用awk替换所有组合中的不同文本块?

Python 如何使用awk替换所有组合中的不同文本块?,python,regex,bash,perl,awk,Python,Regex,Bash,Perl,Awk,我正在尝试替换这样的线条块: 一组线由编号较小的波纹管组成 当一行具有“=”时,该行块可以替换以“=”命名的块 让我们看一个例子,这个输入: 01 hello 02 stack 02 overflow 04 hi 02 friends = overflow 03 this 03 is 03 my = is 03 life 02 lol 02 im 02 jokin

我正在尝试替换这样的线条块:

  • 一组线由编号较小的波纹管组成
  • 当一行具有“=”时,该行块可以替换以“=”命名的块
让我们看一个例子,这个输入:

01 hello
    02 stack
    02 overflow
        04 hi
    02 friends = overflow
        03 this
        03 is 
        03 my = is
        03 life
    02 lol
    02 im
    02 joking = im
        03 filler
将生成以下输出(每个hello块是数组的一个元素):

我这样试过:

#!/bin/bash

awk '{

    if ($0~/=/){
      level=$1
      oc=1
    }else if (oc && $1<=level){
        oc=0
    }

    if (!oc){
        print
    }

}' input.txt
#/bin/bash
awk'{
如果($0~/=/){
级别=$1
oc=1

}else if(oc&&$1这里有一个python脚本,用于读取cobol输入文件并打印出所有可能的已定义和重新定义变量组合:

#!/usr/bin/python
"""Read cobol file and print all possible redefines."""
import sys
from itertools import product

def readfile(fname):
    """Read cobol file & return a master list of lines and namecount of redefined lines."""
    master = []
    namecount = {}
    with open(fname) as f:
        for line in f:
            line = line.rstrip(' .\t\n')
            if not line:
                continue
            words = line.split()
            n = int(words[0])
            if '=' in words or 'REDEFINES' in words:
                name = words[3]
            else:
                name = words[1]
            master.append((n, name, line))
            namecount[name] = namecount.get(name, 0) + 1
    # py2.7: namecount = {key: val for key, val in namecount.items() if val > 1}
    namecount = dict((key, val) for key, val in namecount.items() if val > 1)

    return master, namecount

def compute(master, skip=None):
    """Return new cobol file given master and skip parameters."""
    if skip is None:
        skip = {}
    seen = {}
    skip_to = None
    output = ''
    for n, name, line in master:
        if skip_to and n > skip_to:
            continue
        seen[name] = seen.get(name, 0) + 1
        if seen[name] != skip.get(name, 1):
            skip_to = n
            continue
        skip_to = None
        output += line + '\n' 
    return output

def find_all(master, namecount):
    """Return list of all possible output files given master and namecount."""
    keys = namecount.keys()
    values = [namecount[k] for k in keys]
    out = []
    for combo in product(*[range(1, v + 1) for v in values]):
        skip = dict(zip(keys, combo))
        new = compute(master, skip=skip)
        if new not in out:
            out.append(new)
    return out

def main(argv):
    """Process command line arguments and print results."""
    fname = argv[-1]
    master, namecount = readfile(fname)
    out = find_all(master, namecount)
    print('\n'.join(out))

if __name__ == '__main__':
    main(sys.argv)
如果上述脚本保存在名为
cobol.py
的文件中,则If可以作为以下方式运行:

python cobol.py name_of_input_file
定义和重定义的各种可能组合将显示在标准输出上

此脚本在python2(2.6+)或python3下运行

解释 该代码使用三个功能:

  • readfile
    读取输入文件并返回两个变量,这些变量汇总了其中的结构

  • compute
    获取两个参数,并从中计算输出块

  • find_all
    确定所有可能的输出块,使用
    compute
    创建它们,然后将它们作为列表返回

让我们更详细地了解每个函数:

  • readfile
  • readfile
    将输入文件名作为参数,并返回一个列表,
    master
    ,以及一个字典,
    namecont
    。对于输入文件中的每一个非空行,list
    master
    都有一个元组,其中包含(1)级别号,(2)定义或重新定义的名称,以及(2)原始行本身。对于示例输入文件,
    readfile
    master
    返回此值:

    [(1, 'hello', '01 hello'),
     (2, 'stack', '    02 stack'),
     (2, 'overflow', '    02 overflow'),
     (4, 'hi', '        04 hi'),
     (2, 'overflow', '    02 friends = overflow'),
     (3, 'this', '        03 this'),
     (3, 'is', '        03 is'),
     (3, 'is', '        03 my = is'),
     (3, 'life', '        03 life'),
     (2, 'lol', '    02 lol'),
     (2, 'im', '    02 im'),
     (2, 'im', '    02 joking = im'),
     (3, 'filler', '        03 filler')]
    
    readfile
    还返回字典
    namecount
    ,该字典为每个重新定义的名称都有一个条目,并有一个该名称的定义/重新定义数量的计数。对于示例输入文件,
    namecount
    具有以下值:

    {'im': 2, 'is': 2, 'overflow': 2}
    
    这表示
    im
    is
    、和
    overflow
    各有两个可能的值

    readfile
    当然是为了在当前版本的问题中使用输入文件格式而设计的。在可能的情况下,它也被设计为使用该问题以前版本的格式。例如,变量重新定义无论是否用等号表示都可以接受(当前版本)或与以前版本一样使用单词
    REFDEFINES
    。这是为了使此脚本尽可能灵活

  • compute
  • 函数
    compute
    用于生成每个输出块。它使用两个参数。第一个参数是
    master
    ,它直接来自
    readfile
    。第二个参数是
    skip
    ,它来自
    readfile
    返回的
    namecount
    字典。例如,
    nameco>unt
    字典指出,
    im
    有两种可能的定义。这说明了如何使用
    compute
    为每种定义生成输出块:

    In [14]: print compute(master, skip={'im':1, 'is':1, 'overflow':1})
    01 hello
        02 stack
        02 overflow
            04 hi
        02 lol
        02 im
    
    In [15]: print compute(master, skip={'im':2, 'is':1, 'overflow':1})
    01 hello
        02 stack
        02 overflow
            04 hi
        02 lol
        02 joking = im
            03 filler
    
    请注意,上面对
    compute
    的第一次调用生成了使用
    im
    的第一个定义的块,第二次调用生成了使用第二个定义的块

  • find_all
  • 有了以上两个函数,很明显最后一步就是生成所有不同的定义组合并打印出来。这就是函数
    find\u all
    所做的。使用
    master
    namecontcount
    返回的
    readfile
    ,它系统地运行所有可用的数据库le定义和调用的组合
    compute
    ,为每个定义和调用创建一个块。它收集以这种方式创建的所有唯一块并返回它们

    find_all
    返回的输出是一个字符串列表。每个字符串都是对应于定义/重定义组合的块。使用问题的示例输入,这显示了
    find_all
    返回的内容:

    In [16]: find_all(master, namecount)
    Out[16]: 
    ['01 hello\n    02 stack\n    02 overflow\n        04 hi\n    02 lol\n    02 im\n',
     '01 hello\n    02 stack\n    02 friends = overflow\n        03 this\n        03 is\n        03 life\n    02 lol\n    02 im\n',
     '01 hello\n    02 stack\n    02 overflow\n        04 hi\n    02 lol\n    02 joking = im\n        03 filler\n',
     '01 hello\n    02 stack\n    02 friends = overflow\n        03 this\n        03 is\n        03 life\n    02 lol\n    02 joking = im\n        03 filler\n',
     '01 hello\n    02 stack\n    02 friends = overflow\n        03 this\n        03 my = is\n        03 life\n    02 lol\n    02 im\n',
     '01 hello\n    02 stack\n    02 friends = overflow\n        03 this\n        03 my = is\n        03 life\n    02 lol\n    02 joking = im\n        03 filler\n']
    
    作为一个例子,让我们以
    find_all
    返回的第四个字符串为例,为了获得更好的格式,我们将
    打印它:

    In [18]: print find_all(master, namecount)[3]
    01 hello
        02 stack
        02 friends = overflow
            03 this
            03 is
            03 life
        02 lol
        02 joking = im
            03 filler
    
    在完整的脚本中,
    find_all
    的输出组合在一起并打印到标准输出,如下所示:

    out = find_all(master, namecount)              
    print('\n'.join(out))
    
    这样,输出将显示所有可能的块

    问题早期版本的答案 原问题的答案 说明: 此程序具有以下变量:

    #!/usr/bin/python
    """Read cobol file and print all possible redefines."""
    import sys
    from itertools import product
    
    def readfile(fname):
        """Read cobol file & return a master list of lines and namecount of redefined lines."""
        master = []
        namecount = {}
        with open(fname) as f:
            for line in f:
                line = line.rstrip(' .\t\n')
                if not line:
                    continue
                words = line.split()
                n = int(words[0])
                if '=' in words or 'REDEFINES' in words:
                    name = words[3]
                else:
                    name = words[1]
                master.append((n, name, line))
                namecount[name] = namecount.get(name, 0) + 1
        # py2.7: namecount = {key: val for key, val in namecount.items() if val > 1}
        namecount = dict((key, val) for key, val in namecount.items() if val > 1)
    
        return master, namecount
    
    def compute(master, skip=None):
        """Return new cobol file given master and skip parameters."""
        if skip is None:
            skip = {}
        seen = {}
        skip_to = None
        output = ''
        for n, name, line in master:
            if skip_to and n > skip_to:
                continue
            seen[name] = seen.get(name, 0) + 1
            if seen[name] != skip.get(name, 1):
                skip_to = n
                continue
            skip_to = None
            output += line + '\n' 
        return output
    
    def find_all(master, namecount):
        """Return list of all possible output files given master and namecount."""
        keys = namecount.keys()
        values = [namecount[k] for k in keys]
        out = []
        for combo in product(*[range(1, v + 1) for v in values]):
            skip = dict(zip(keys, combo))
            new = compute(master, skip=skip)
            if new not in out:
                out.append(new)
        return out
    
    def main(argv):
        """Process command line arguments and print results."""
        fname = argv[-1]
        master, namecount = readfile(fname)
        out = find_all(master, namecount)
        print('\n'.join(out))
    
    if __name__ == '__main__':
        main(sys.argv)
    
    • f
      是一个标志,在第一次重新定义之前为零,之后为一

    • s
      包含第一次重新定义之前的所有文本

    • t
      包含当前重定义的文本

    • c
      是一个计数器,用于确定输出名称的名称

    该守则的运作如下:

    out = find_all(master, namecount)              
    print('\n'.join(out))
    
  • f==0&&!/REDEFINES/{s=s“\n”$0;next}

    在遇到第一个重定义之前,文本保存在变量
    s
    中,我们跳过其余命令并跳转到
    下一行

  • /重新定义/{f=1;打印ST>(“输出”++c.txt”);t=“”}

    每次遇到重定义行时,我们将标志
    f
    设置为1,并将序言部分
    s
    与当前重定义部分一起打印到名为
    outputn.txt
    的文件中,其中n由计数器
    c
    的值替换

    因为我们在一个新的重定义部分的开头,所以变量
    t
    被设置为空

  • {
    
    awk '/REDEFINES/{f=1;print substr(s,2) t>("output" ++c ".txt");t=""} f==0 {s=s"\n"$0;next} {t=t"\n"$0} END{print substr(s,2) t>("output" ++c ".txt")}' input
    
    awk 'f==1 && pre==$1 && !/REDEFINES/{tail=tail "\n" $0} /REDEFINES/{pre=$1;f=1;t[++c]="\n"$0} f==0 {head=head"\n"$0;next} pre!=$1{t[c]=t[c]"\n"$0} END{for (i=0;i<=c;i++) {print head t[i] tail>("output" (i+1) ".txt")}}' file