我有一个文件,该文件的文本范围由<BD>开始和<ED>结束定界符分隔,并允许嵌套。我希望更改这些定界符,以唯一地指示它们之间的每个文本范围。这些定界符可以是任意字符串。例如:

%{                         # Begin delimiter <BD>
}%                         # End delimiter <ED>


我正在寻找用唯一编号的标记替换定界符:

<BM><UniqueNumber><BM>     # <BD> is replaced by <BM>i<BM>
<EM><UniqueNumber><EM>     # <ED> is replaced by <EM>i<EM>


<BM><EM>是任意长度的字符串,可以是二进制的,并且不存在于正在处理的文件中。例如,在大多数文本文件中,可以将$'\x01'用作<BM>,将$'\x02'用作<EM>

例如,文件包含定界的文本范围,包括嵌套范围:

A %{ B
C %{ D
E }% F %{ G }% H }% I
J %{ K }% L


字母A..L可以是任何文本。转换产生:

A <BM>0<BM> B
C <BM>1<BM> D
E <EM>1<EM> F <BM>2<BM> G <EM>2<EM> H <EM>0<EM> I
J <BM>3<BM> K <EM>3<EM> L


注意:我不是在寻找表示嵌套级别的编号。我正在寻找每个匹配的<BM>i<BM>...<EM>i<EM>文本范围,这些范围要用从0向上计数的唯一整数进行标记。

而且,我希望能够存储为标记0..N-1生成的最大数量N。我在想象Bash函数:

ChangeMarkup()
{
   local InputFile="$1"
   local OutputFile="$2"
   local BD="$3"   # Begin delimiter
   local ED="$4"   # End delimiter
   local BM="$5"   # Begin unique numbered marker
   local EM="$6"   # End unique numbered marker
   local -i N=0
   # ... convert InputFile to OutputFile, incrementing N for each span
   echo "$N"       # Echo the number of spans
}

# Example invocation:
NSpans=$(ChangeMarkup infile outfile '%{' '}%' $'\x01' $'\x02')


我认为解决方案将是:


初始化N=0
扫描<BD>并将N推入堆栈。将<BD>替换为<BM>$N<BM>。递增N
扫描<ED>并替换为<EM><pop stack><EM>
最后,回显$N


我认为Bash脚本中的某些awk可能可以解决。我认为这超出了sed的能力。我也欢迎使用python或可以用Bash脚本编写的任何解决方案,仅限于使用CentOS 7 Minimal iso中可用的软件包。不幸的是,这意味着不能考虑perl。

最佳答案

如果可以,可以使用gnu-awkRT special variable

awk -v BD='%{' -v ED='}%' -v BM='<BM>' -v EM='<EM>' '
    BEGIN{i=c=-1; RS=BD"|"ED}
    RT==BD {++i; ++c; d[i]=c; tag=BM}
    RT==ED {tag=EM}
    {printf "%s%s%s%s",$0,tag,d[i],tag}
    RT==ED{--i; if(i==-1) tag=""}
' file


你得到,

A <BM>0<BM> B
C <BM>1<BM> D
E <EM>1<EM> F <BM>2<BM> G <EM>2<EM> H <EM>0<EM> I
J <BM>3<BM> K <EM>3<EM> L


编辑:要求(2)


  如果检测到不正确的嵌套,脚本可以返回错误代码吗?例如:%{A}%}%秒没有


awk -v BD='%{' -v ED='}%' -v BM='<BM>' -v EM='<EM>' '
    BEGIN{i=c=-1; RS=BD"|"ED}
    RT==BD {++i; ++c; d[i]=c; tag=BM}
    RT==ED {tag=EM}
    {
        if(i<0 && tag!=""){
            print "Error <ED> without opener" > "/dev/stderr"
            exit 1
        }
        printf "%s%s%s%s",$0,tag,d[i],tag
    }
    RT==ED{--i; if(i==-1) tag=""}
    END{
        if(i!=-1){
            print "Error <BD> without closer" > "/dev/stderr"
            exit 1
        }
    }
' file


编辑:要求(1)


  允许和逃脱?也就是说,如果这些定界符前面有反斜杠,则不会将它们视为定界符


 和转义的例如是\%{\}%

awk -v BD='%{' -v ED='}%' -v BM='<BM>' -v EM='<EM>' '
    BEGIN{i=c=-1; RS="\\\\"BD"|\\\\"ED"|"BD"|"ED}
    RT==BD {++i; ++c; d[i]=c; tag=BM}
    RT==ED {tag=EM}
    RT~/^\\/{printf "%s%s",$0,RT; next}
    {
        if(i<0 && tag!=""){
            print "Error <ED> without opener" > "/dev/stderr"
            exit 1
        }
        printf "%s%s%s%s",$0,tag,d[i],tag
    }
    RT==ED{--i; if(i==-1) tag=""}
    END{
        if(i!=-1){
            print "Error <BD> without closer" > "/dev/stderr"
            exit 1
        }
    }
' file


与输入文件

A %{ B
C %{ D
E }% F %{ G }% H }% I
J %{ K }% L\%{ M\}%O


你得到,

A <BM>0<BM> B
C <BM>1<BM> D
E <EM>1<EM> F <BM>2<BM> G <EM>2<EM> H <EM>0<EM> I
J <BM>3<BM> K <EM>3<EM> L\%{ M\}%O

关于python - 如何用唯一编号的标记跨度替换定界跨度?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42645692/

10-12 06:49