有没有办法删除不是全部,而是只删除嵌套的括号?

lok*_*kus 9 sed awk text-processing

说我有一个这样的字符串

[[["q", "0"], "R"], "L"], ["q", [["1", "["], "]"]], [["q", ["2", "L"]], "R"], ["q", ["3", ["R", "L"]]]
Run Code Online (Sandbox Code Playgroud)

我想从中删除所有嵌套的括号

["q", "0", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]
Run Code Online (Sandbox Code Playgroud)

我了解如何编写算法来通过推入和弹出堆栈或仅递增和递减计数器来实现此目的,但我很好奇是否有一种方法可以仅使用sed或 之类的基本工具来做到这一点awk

Qua*_*odo 12

bracket.awk

BEGIN{quote=1}
{
    for(i=1;i<=length;i++){
        ch=substr($0,i,1)
        pr=1
        if(ch=="\""){quote=!quote}
        else if(ch=="[" && quote){brk++;pr=brk<2}
        else if(ch=="]" && quote){brk--;pr=brk<1}
        if(pr){printf "%s",ch}
    }
    print ""
}
Run Code Online (Sandbox Code Playgroud)
$ awk -f bracket.awk file
["q", "0", "R", "L"], ["q", "1", "[", "]"], ["q", "2", "L", "R"], ["q", "3", "R", "L"]
Run Code Online (Sandbox Code Playgroud)

背后的想法:

初始化quote=1。按字符读取文件。每当找到引用时,反转quote变量(如果1,则变为0,反之亦然)。

然后,quote根据brk计数器,仅当设置为 1 并且不打印多余的括号时才计算括号。

print ""语句只是添加一个换行符,因为printf上面没有这样做。


Sté*_*las 8

perl

perl -pe '
   s{([^]["]+|"[^"]*")|\[(?0)*\]}
    {$1 // "[". ($& =~ s/("[^"]*"|[^]["]+)|./$1/gr) . "]"}ge'
Run Code Online (Sandbox Code Playgroud)

这利用了perl的递归正则表达式。

外部s{regex}{replacement-code}ge将输入标记为:

  • [,]"
  • 带引号的字符串
  • 一个[...]组(在正则表达式中使用递归来查找匹配]

然后,我们更换令牌本身,如果它在第一两类($1),如果没有与令牌非引用[]使用在内部替换相同tokenising技术去除。

要处理引号内的转义引号和反斜杠(如"foo\"bar\\"),请替换[^"](?:[^\\"]|\\.).

sed

如果您sed支持使用扩展正则表达式而不是基本正则表达式的-Eor-r选项,您可以使用循环来完成,首先替换最里面的s:[...]

LC_ALL=C sed -E '
  :1
  s/^(("[^"]*"|[^"])*\[("[^"]*"|[^]"])*)\[(("[^"]*"|[^]["])*)\]/\1\4/
  t1'
Run Code Online (Sandbox Code Playgroud)

LC_ALL=C用于加速并使其等效于在perl将字节解释为字符时也忽略用户的语言环境的那个)。

POSIXly,它应该仍然可行,例如:

LC_ALL=C sed '
  :1
  s/^\(\(\("[^"]*"\)*[^"]*\)*\[\(\("[^"]*"\)*[^]"]*\)*\)\[\(\(\("[^"]*"\)*[^]["]*\)*\)\]/\1\6/
  t1'
Run Code Online (Sandbox Code Playgroud)

这里使用\(\(a\)*\(b\)*\)*代替(a|b)*作为基本正则表达式没有交替运算符(某些sed实现的 BRE具有\|此功能,但这不是 POSIX/便携式)。