模式匹配的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 是要编辑的字符串,使用 agensub 也 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' 视为插入、删除或替换。因此,比模式字符串少一个或多一个字符的匹配被考虑在内,而此处的所有其他答案要求匹配具有相同数量的字符