【问题标题】:How to replace delimited spans with uniquely-numbered marked spans?如何用唯一编号的标记跨度替换定界跨度?
【发布时间】:2017-07-27 12:10:37
【问题描述】:

我有一个文件,其中包含由 <BD> begin<ED> end delimiters 分隔的文本,并且允许嵌套。我希望更改这些分隔符以唯一地指示它们之间的每个文本范围。这些分隔符可以是任意字符串。例如:

%{                         # 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>

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

例如,文件包含分隔的文本范围,包括嵌套范围:

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

注意:不是在寻找表示嵌套级别的编号;我正在寻找每个匹配的 &lt;BM&gt;i&lt;BM&gt;...&lt;EM&gt;i&lt;EM&gt; 文本跨度以唯一整数标记,从 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
  • 扫描&lt;BD&gt; 并将N 推入堆栈。将&lt;BD&gt; 替换为&lt;BM&gt;$N&lt;BM&gt;。递增N
  • 扫描&lt;ED&gt;并替换为&lt;EM&gt;&lt;pop stack&gt;&lt;EM&gt;
  • 最后,回显$N

我在想 Bash 脚本中的一些 awk 可能会派上用场。我认为这超出了 sed 的能力。我也对 python 或任何可以用 Bash 脚本编写的解决方案持开放态度,仅限于使用 CentOS 7 Minimal iso 中可用的软件包。不幸的是,这意味着不能考虑 perl

【问题讨论】:

  • H }% I 而不是 H %} I 输入错误?
  • @JoseRicardoBustosM。是的,这是一个错字……已修复!谢谢。

标签: python bash awk sed centos7


【解决方案1】:

如果,你可以使用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)

允许 and 被转义?也就是说,如果这些分隔符前面有反斜杠,则不视为分隔符

和转义为 \%{\}% 例如

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

【讨论】:

  • 看起来不错......这个解决方案可以通过两种方式进行调整:(1)允许&lt;BD&gt;&lt;ED&gt;被转义?也就是说,如果这些分隔符前面有反斜杠,则它们不会被视为分隔符。并且,(2)如果检测到不正确的嵌套,该脚本可以返回错误代码吗?例如:%{ A }% }% 第二个&lt;ED&gt; 没有&lt;BD&gt;
  • 我也在结尾看到 &lt;EM&gt;&lt;EM&gt; 并且输入文件的最后一个换行符没有被发出。
  • 是的,这两个要求都是可能的
  • 谢谢...如果您有时间用这些“额外信用”项目更新解决方案,我认为这不仅对我有益,而且其他人也能看到错误检测是如何可能的。非常感谢!
  • 难以置信!我尝试了一些变体来使用反斜杠,但还没有完全发挥作用。可以像定义-v ESC='#'-v ESC='\\' 那样进行任意转义吗?另外,我在脚本正文中看到RT=="}%" 可以替换为RT==ED
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
相关资源
最近更新 更多