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
上面没有这样做。
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
支持使用扩展正则表达式而不是基本正则表达式的-E
or-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/便携式)。