模式匹配的awk解决方案,允许一个ambiguity/mismatch
awk solution for pattern matching and allowing one ambiguity/mismatch
我想计算文档中的字符串数。
如果输入是:
GGTGGTGGTAT
GGTAGTGGTAT
GGTGGTGGTAT
GGTAATGGTAT
然后我搜索 GGTGGTGGT 我想找到 3 个匹配项。允许有一处歧义。
使用 egrep 看起来像这样,输出为 3。
egrep -c "GGTGGTGGT|.GTGGTGGT|G.TGGTGGT|GG.GGTGGT|GGT.GTGGT|GGTG.TGGT|GGTGG.GGT|GGTGGT.GT|GGTGGTG.T|GGTGGTGG." input
必要的 awk 模式与您的 egrep 解决方案相同:
awk '/GGTGGTGGT|.GTGGTGGT|G.TGGTGGT|GG.GGTGGT|GGT.GTGGT|GGTG.TGGT|GGTGG.GGT|GGTGGT.GT|GGTGGTG.T|GGTGGTGG./{print [=10=]}' input
下面是使用 bash 生成正则表达式的方法:
$ patt=(GGTGGTGGT)
$ for ((i=0; i<${#patt[0]}; i++)); do
patt+=( "${patt[0]:0:i}.${patt[0]:i+1}" )
done
$ regex=$(IFS='|'; echo "${patt[*]}")
$ echo "$regex"
GGTGGTGGT|.GTGGTGGT|G.TGGTGGT|GG.GGTGGT|GGT.GTGGT|GGTG.TGGT|GGTGG.GGT|GGTGGT.GT|GGTGGTG.T|GGTGGTGG.
然后:
awk -v regex="$regex" '[=11=] ~ regex' file
或仅使用 awk:
awk -v srch=GGTGGTGGT '
BEGIN {
regex = srch
for (i=1; i<=length(srch); i++)
regex = regex "|" substr(srch,1,i-1) "." substr(srch, i+1)
}
[=12=] ~ regex
' << END
GGTGGTGGTAT
GGTAGTGGTAT
GGTGGTGGTAT
GGTAATGGTAT
END
GGTGGTGGTAT
GGTAGTGGTAT
GGTGGTGGTAT
这个 awk 可执行脚本将创建要匹配的模式,然后测试每一行以计算匹配数:
#!/usr/bin/awk -f
BEGIN { createPatternArray( pattern, a ) }
{
for( k in a ) { if( [=10=] ~ k ) { total++; break } }
}
END { print total }
function createPatternArray( pattern, a, pLen, i ) {
a[pattern]
pLen = length( pattern )
for(i=1; i<=pLen; i++) {
a[substr(pattern,1,i-1) "." substr(pattern,i+1)]
}
# for( k in a ) { print k }
}
如果它被放置在像 awko
这样的文件中(并使其可执行),那么 运行 它在数据上就像:
awko -v pattern=GGTGGTGGT data
3
createPatternArray
函数使数组中的条目类似于:
.GTGGTGGT
G.TGGTGGT
GG.GGTGGT
GGT.GTGGT
GGTG.TGGT
GGTGG.GGT
GGTGGT.GT
GGTGGTG.T
GGTGGTGG.
GGTGGTGGT
对于每一行,都会根据数组中的条目测试行的前缀。如果有匹配项,递增 totals
然后中断(否则有多个匹配项)。在 END
处打印 total
.
这是使用 (G)awk 和 gensub 函数的方法
awk -va="GGTGGTGGT" '
{for(i=1;i<=length(a);i++)if([=10=]~gensub(/./,".",i,a)){print;next}}' file
输出
GGTGGTGGTAT
GGTAGTGGTAT
GGTGGTGGTAT
工作原理
-va="GGTGGTGGT"
将变量 a 设置为引号中的值(任意)
{for(i=1;i<=length(a);i++)
创建一个从 1 到变量长度的循环 a.The 长度是字符串中的字符数。
if([=14=]~gensub(/./,".",i,a))
我先解释一下gensub
。
前两个参数用文字 .
交换 .
(任何字符)。第三个参数是参数 1 的匹配项。当我们搜索单个字符时,这将只遍历字符串,用 .
替换每个字符。最后的 arg 是要编辑的字符串,使用 a
。 gensub
也 returns 字符串而不是编辑原始字符串。
[=15=]~
表示整行包含 ~
之后的任何内容
这些都包含在一个 if 中,当两者都被评估时将导致
[=16=]~.GTGGTGGT
[=16=]~G.TGGTGGT
[=16=]~GG.GGTGGT
[=16=]~GGT.GTGGT
[=16=]~GGTG.TGGT
[=16=]~GGTGG.GGT
[=16=]~GGTGGT.GT
[=16=]~GGTGGTG.T
[=16=]~GGTGGTGG.
'
{print;next}
如果其中任何一个匹配,则打印该行并跳过所有进一步的指令并处理下一行。
资源
https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html
你真正想要的是agrep
,它代表近似grep。它工作得非常好,有时甚至比普通的 grep 更快。
你可以找到原文here。
安装就像下载 tar 球一样简单,运行 tar -xf <file>
,在生成的文件夹中导航,然后 运行 make
或当前(可能更臃肿)版本here
在您的情况下,您只需:
agrep -1 GGTGGTGGT <file>
-#
是您允许的不匹配数。原版最多支持8个不匹配
请务必注意,agrep 将 'mismatch' 视为插入、删除或替换。因此,比模式字符串少一个或多一个字符的匹配被考虑在内,而此处的所有其他答案要求匹配具有相同数量的字符。
我想计算文档中的字符串数。
如果输入是:
GGTGGTGGTAT
GGTAGTGGTAT
GGTGGTGGTAT
GGTAATGGTAT
然后我搜索 GGTGGTGGT 我想找到 3 个匹配项。允许有一处歧义。
使用 egrep 看起来像这样,输出为 3。
egrep -c "GGTGGTGGT|.GTGGTGGT|G.TGGTGGT|GG.GGTGGT|GGT.GTGGT|GGTG.TGGT|GGTGG.GGT|GGTGGT.GT|GGTGGTG.T|GGTGGTGG." input
必要的 awk 模式与您的 egrep 解决方案相同:
awk '/GGTGGTGGT|.GTGGTGGT|G.TGGTGGT|GG.GGTGGT|GGT.GTGGT|GGTG.TGGT|GGTGG.GGT|GGTGGT.GT|GGTGGTG.T|GGTGGTGG./{print [=10=]}' input
下面是使用 bash 生成正则表达式的方法:
$ patt=(GGTGGTGGT)
$ for ((i=0; i<${#patt[0]}; i++)); do
patt+=( "${patt[0]:0:i}.${patt[0]:i+1}" )
done
$ regex=$(IFS='|'; echo "${patt[*]}")
$ echo "$regex"
GGTGGTGGT|.GTGGTGGT|G.TGGTGGT|GG.GGTGGT|GGT.GTGGT|GGTG.TGGT|GGTGG.GGT|GGTGGT.GT|GGTGGTG.T|GGTGGTGG.
然后:
awk -v regex="$regex" '[=11=] ~ regex' file
或仅使用 awk:
awk -v srch=GGTGGTGGT '
BEGIN {
regex = srch
for (i=1; i<=length(srch); i++)
regex = regex "|" substr(srch,1,i-1) "." substr(srch, i+1)
}
[=12=] ~ regex
' << END
GGTGGTGGTAT
GGTAGTGGTAT
GGTGGTGGTAT
GGTAATGGTAT
END
GGTGGTGGTAT
GGTAGTGGTAT
GGTGGTGGTAT
这个 awk 可执行脚本将创建要匹配的模式,然后测试每一行以计算匹配数:
#!/usr/bin/awk -f
BEGIN { createPatternArray( pattern, a ) }
{
for( k in a ) { if( [=10=] ~ k ) { total++; break } }
}
END { print total }
function createPatternArray( pattern, a, pLen, i ) {
a[pattern]
pLen = length( pattern )
for(i=1; i<=pLen; i++) {
a[substr(pattern,1,i-1) "." substr(pattern,i+1)]
}
# for( k in a ) { print k }
}
如果它被放置在像 awko
这样的文件中(并使其可执行),那么 运行 它在数据上就像:
awko -v pattern=GGTGGTGGT data
3
createPatternArray
函数使数组中的条目类似于:
.GTGGTGGT
G.TGGTGGT
GG.GGTGGT
GGT.GTGGT
GGTG.TGGT
GGTGG.GGT
GGTGGT.GT
GGTGGTG.T
GGTGGTGG.
GGTGGTGGT
对于每一行,都会根据数组中的条目测试行的前缀。如果有匹配项,递增 totals
然后中断(否则有多个匹配项)。在 END
处打印 total
.
这是使用 (G)awk 和 gensub 函数的方法
awk -va="GGTGGTGGT" '
{for(i=1;i<=length(a);i++)if([=10=]~gensub(/./,".",i,a)){print;next}}' file
输出
GGTGGTGGTAT
GGTAGTGGTAT
GGTGGTGGTAT
工作原理
-va="GGTGGTGGT"
将变量 a 设置为引号中的值(任意)
{for(i=1;i<=length(a);i++)
创建一个从 1 到变量长度的循环 a.The 长度是字符串中的字符数。
if([=14=]~gensub(/./,".",i,a))
我先解释一下gensub
。
前两个参数用文字 .
交换 .
(任何字符)。第三个参数是参数 1 的匹配项。当我们搜索单个字符时,这将只遍历字符串,用 .
替换每个字符。最后的 arg 是要编辑的字符串,使用 a
。 gensub
也 returns 字符串而不是编辑原始字符串。
[=15=]~
表示整行包含 ~
这些都包含在一个 if 中,当两者都被评估时将导致
[=16=]~.GTGGTGGT
[=16=]~G.TGGTGGT
[=16=]~GG.GGTGGT
[=16=]~GGT.GTGGT
[=16=]~GGTG.TGGT
[=16=]~GGTGG.GGT
[=16=]~GGTGGT.GT
[=16=]~GGTGGTG.T
[=16=]~GGTGGTGG.
'
{print;next}
如果其中任何一个匹配,则打印该行并跳过所有进一步的指令并处理下一行。
资源
https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html
你真正想要的是agrep
,它代表近似grep。它工作得非常好,有时甚至比普通的 grep 更快。
你可以找到原文here。
安装就像下载 tar 球一样简单,运行 tar -xf <file>
,在生成的文件夹中导航,然后 运行 make
或当前(可能更臃肿)版本here
在您的情况下,您只需:
agrep -1 GGTGGTGGT <file>
-#
是您允许的不匹配数。原版最多支持8个不匹配
请务必注意,agrep 将 'mismatch' 视为插入、删除或替换。因此,比模式字符串少一个或多一个字符的匹配被考虑在内,而此处的所有其他答案要求匹配具有相同数量的字符。