0

我是这个网站的新手。这是一个困扰我超过 2 小时的问题。我有一个字符串(newick 格式的系统发育树),它看起来像:

((A:14,B:43):22,C:76,(D:54,(E:87,F:28):17):35);

树可能有多个级别,用括号表示。现在我想在顶级数字(分支长度)中添加一个数字,比如 10。这里只有三个顶级数字:22、76、35。转换后的字符串应如下所示:

((A:14,B:43):32,C:86,(D:54,(E:87,F:28):17):45);

我已经尽力想出一个合适的正则表达式,但最终承认了我的局限性。怎样才能真正做到?

4

4 回答 4

1

虽然我会选择解析整个树,但仅使用正则表达式可以解决问题:

use strict; use warnings; use feature qw(say);
my $string = "((A:14,B:43):22,C:76,(D:54,(E:87,F:28):17):35)";
$string =~ s/^\(//;
$string =~ s/\)$//;
$string =~ s{
    \G ((?&PRELEM)) : (\d+) (,|$)
    (?(DEFINE)
        (?<SUBLIST> [(] (?&ELEM)(?:,(?&ELEM))* [)] )
        (?<ELEM> (?&PRELEM) : \d+ )
        (?<PRELEM> (?:[A-Z]|(?&SUBLIST)) )
    )
 }{"$1:".($2+10).$3}gex;
 say "($string)";

打印((A:14,B:43):32,C:86,(D:54,(E:87,F:28):17):45)

我定义了一个自顶向下递归解析的小语法,请根据需要进行调整。在顶层,我们有无趣的 Pre-Elements,我们将其存储在$1它们可以是单个字母或用括号括起来的树。在 a 之后是:我们想要增加的数字,存储在$2. 它后面是字符串或逗号的结尾。我们迭代匹配,从最后一个匹配的左侧开始(由/g选项和\G断言表示)。当我们构建替换字符串时会发生添加(我们正在使用该/e选项)。

于 2012-07-31T14:50:44.053 回答
1
s/(?:^\(|(\((?:(?>[^()]*)|(?1))*\)))\K|:\K([0-9]+)/$2?$2+10:""/ge

匹配您要跳过的内容或以 : 开头的数字。

您要跳过的内容要么是前导括号,要么是(任何平衡的括号集(平衡括号正则表达式几乎从字面上取自perlre)。

在替换中,如果要修改的数字匹配,则添加十,否则不匹配。

但是你最好不要聪明,而是去解析、修改和重新序列化你的树。

于 2012-07-31T14:51:31.683 回答
1

这需要一个递归正则表达式来匹配嵌套的括号。

首先定义一个“键”,它可以是一串大写字母,也可以是括号之间的任意数量的键:值对。

然后找到后跟冒号和十进制数的所有键,并对数字进行算术运算。

use strict;
use warnings;

my $str = '((A:14,B:43):22,C:76,(D:54,(E:87,F:28):17):35)';

my $key = qr/ (?<key> [A-Z]+ | \( (?&key) : \d+ (?: , (?&key) : \d+ )* \)  ) /x;

$str =~ s/$key : \K ( \d+ ) /$2 + 10/xge;

print $str;

输出

((A:14,B:43):32,C:86,(D:54,(E:87,F:28):17):45)
于 2012-07-31T15:56:26.600 回答
0

首先,我要感谢ysth在这个帖子中非常有趣的帖子。从这篇文章中,我了解了如何以及为什么要应用\Keep 修饰符。

我添加了另一个\K(到第一个子表达式)并使用了++原子组的新符号:

my $r = qr{
  (?:
     (?: ^ \(\K )
     |
     (
       \( (?: [^()]++ | (?1) )* \)
     )\K
  )
  |
  :\K (\d+)
}x;

输出字符串现在与输入字符串完全匹配 - 除了增加的值:

$t =~ s/$r/$2?$2+10:''/ge;

input:  ((A:14,B:43):22,C:76,(D:54,(E:87,F:28):17):35)
output: ((A:14,B:43):32,C:86,(D:54,(E:87,F:28):17):45)
于 2012-07-31T17:44:45.347 回答