在正则表达式的 Perl 教程 中,有一个带有
/g
修饰符的示例:
$dna = "ATCGTTGAATGCAAATGACATGAC";
while ($dna =~ /(\w\w\w)*?TGA/g) { # note the minimal *?
print "Got a TGA stop codon at position ", pos $dna, "\n";
}
我将其翻译成 Common Lisp,如下所示:
(let ((dna "ATCGTTGAATGCAAATGACATGAC"))
(ppcre:do-matches (m-s m-e ; match-start match-end
"(\\w\\w\\w)*?TGA"
dna
nil
:start 0 :end (length dna))
(format t "~&;; Got a TGA stop codon at position ~d"
m-e)))
其行为类似于 Perl 示例:
;; Got a TGA stop codon at position 18
;; Got a TGA stop codon at position 23
NIL
来自 Perl 教程:
18号位不错,但23号位是假的。发生什么事了?
答案是我们的正则表达式运行良好,直到我们通过 最后一场真正的比赛。那么正则表达式将无法匹配 同步 TGA 并开始向前推进一个字符位置 一次,不是我们想要的。
解决方案是使用 \G 将匹配锚定到密码子 对齐...
CL-PPCRE的文档说
(当前)不支持以下 Perl 功能:... \G 代表 Perl 的 pos() 因为我们没有它。
但是我当然想要这些功能,至少涵盖大部分由
cl-ppcre
完成的教程。所以,我试图弄清楚会发生什么,为什么正则表达式会“开始向前推进一个字符位置”,这是我没想到的,所以我仍然没有真正获得正则表达式的功能。 (但这就是我对我的期望。现在,我尝试找到适合我的 Common Lisp 翻译。然后我将深入研究创建具有所需行为的正则表达式的艺术,以解决各个问题。)
但是,首先我用
ppcre:do-scans
看它:
(let ((dna "ATCGTTGAATGCAAATGACATGAC"))
(ppcre:do-scans (m-s m-e
r-s r-e ; register-start register-end
"(\\w\\w\\w)*?TGA"
dna
nil
:start 0 :end (length dna))
(format t "~&;; ~a ~a ~a ~a" m-s m-e r-s r-e)))
输出:
;; 0 18 #(12) #(15) ; end pos 18
;; 20 23 #(NIL) #(NIL) ; <<<< Why NIL? why start pos 20?
NIL
虽然我很天真,但我首先尝试为我使用这个:
(let ((dna "ATCGTTGAATGCAAATGACATGAC"))
(ppcre:do-scans (m-s m-e
r-s r-e
"(\\w\\w\\w)*?TGA"
dna
nil
:start 0 :end (length dna))
(if (null (elt r-s 0))
(return)
(format t "~&;; Got a TGA stop codon at position ~d"
m-e))))
这完成了这项工作。
;; Got a TGA stop codon at position 18
NIL
但没想到这会是一个通用的解决方案。
在“DNA”中添加三胞胎又改变了图景(当然是弗兰肯斯坦):
(let ((dna "ATCGTTGAATGCAAATGACATGACTGCTGAGTTATGAAATGCATC"))
(ppcre:do-scans (m-s m-e
r-s r-e
"(\\w\\w\\w)*?TGA"
dna
nil
:start 0 :end (length dna))
(format t "~&;; ~a ~a ~a ~a" m-s m-e r-s r-e)))
结果:
;; 0 18 #(12) #(15)
;; 18 30 #(24) #(27) <<<<<< end 30
;; 31 37 #(31) #(34) <<<<<< start 31 results in a bogus
NIL
可以肯定的是(并希望找到“向前迈出一个字符”的必要性的提示),我将其想象为这样:
;; 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17|18
;; A T C G T T G A A T G C A A A T G A|
;; | | | | | |
;; v
;; 18 19 20 21 22 23 24 25 26|27 28 29|30<
;; C A T G A C T G C T G A| G
;; | | | | ∧
;; v
;; 31 32 33 34 35 36 37 38 39 40 41 42 43 44
;; T T A T G A| A A T G C A T C
;; | | | | |
;; |-------|--------|
;; bogus
既然我想,也许我可以从更长的时间中获得更多信息 顺序,我又试了一次:
(let ((dna "ATCGTTGAATGCAAATGACATGACTGCTGAGTTATGAAATGCATCTGCTGAATCAAACTGAAATGAATC"))
(ppcre:do-scans (m-s m-e
r-s r-e
"(\\w\\w\\w)*?TGA"
dna
nil
:start 0 :end (length dna))
(format t "~&;; ~a ~a ~a ~a" m-s m-e r-s r-e)))
结果是:
;; 0 18 #(12) #(15)
;; 18 30 #(24) #(27) end pos 30
;; 30 51 #(45) #(48) <<<<<<< oops, start pos 30 and the match is correct
;; 51 66 #(60) #(63) <<<<<<< again correct.
NIL
(再次,我想真正确定并完成我的可视化。但我毫不怀疑你不想再看到它。)
这张图片与更长的“DNA”保持不变
"ATCGTTGAATGCAAATGACATGACTGCTGAGTTATGAAATGCATCTGCTGAATCAAACTGAAATGAATCAAATGCTGACCC"
输出
;; 0 18 #(12) #(15)
;; 18 30 #(24) #(27)
;; 30 51 #(45) #(48)
;; 51 66 #(60) #(63)
;; 66 78 #(72) #(75)
NIL
而且我觉得:对应的效果还是模仿的
\G
锚点就是告诉ppcre:do-scans
,在这种情况下
搜索它总是应以它在中的方式进行
最后两个案例的字符串很长。
但我现在不知道该怎么做或如何告诉它
ppcre:do-scans
和它的亲戚。 (我还没有找到为什么我们需要用"(\\w\\w\\w)*?TGA"
“开始一次向前迈出一个角色位置”的模式。)
并且:如果我从 pos 18 -> pos 20 和 pos 30 -> pos 31 开始,然后继续 pos 30 -> pos 30、pos 51 -> pos 51,我会得到 pos 567 -> pos 566 的长度吗?位置 1038 -> 位置 1036 等等?
你能帮我吗?
所以,我试图弄清楚会发生什么,为什么正则表达式会“开始向前推进一个字符位置”,这是我没想到的,所以我仍然没有真正理解正则表达式的功能。
首先,让我们在您的程序中添加一些内容:
$dna = "ATCGTTGAATGCAAATGACATGAC";
while ($dna =~ /(\w\w\w)*?TGA/g) { # note the minimal *?
print "Matched found at [$-[0],$+[0]): `$&`.\n";
}
Matched found at [0,18): `ATCGTTGAATGCAAATGA`.
Matched found at [20,23): `TGA`.
(
$+[0]
与pos($dna)
相同。)
在 Perl 中,默认情况下不锚定正则表达式。我的意思是,如果在第一个位置找不到匹配项,它将尝试从第二个位置开始找到匹配项。它会迭代地执行此操作,直到找到匹配项,或者已尝试过每个后续位置。
例如,即使字符串不以
b
开头,以下内容也会返回 true(在标量上下文中计算时)。
"abc" =~ /b/ # true
查看您的程序,尝试以 18 号位置进行比赛,但失败了。于是它尝试了19号位置,但也失败了。但它确实成功地在第 20 位找到了匹配。
那么你能做些什么呢?我不知道。我不熟悉 List 或 CL-PPCRE。
知道每个模式都以
\G(?s:.)*?\K
为前缀可能会很有用。
/\G(?s:.)*?\Kb/ # Same as /b/
/\G(?s:.)*?\K(\w\w\w)*?TGA/g # Same as /(\w\w\w)*?TGA/g