将两个不同模式之间的数字相乘并打印整个文件

将两个不同模式之间的数字相乘并打印整个文件

我想用乘数替换模式之间的数字并打印所有行。该文件是 newick 格式的树文件,仅包含一行。我的目标是 之前)和之后的所有数字:。我想将两个符号之间的所有数字乘以 100。

文件:

((((A_8:0.000846,(A_5:0.002449,(A_1:1e-06,((A_4:1e-06,((A_7:1e-06,A_6:0.001061)0.714000:1e-06,A_3:1e-06)0.314500:1e-06)0.358667:1e-06,A_2:1e-06)0.361000:1e-06)0.434800:1e-06)0.683500:0.001619)0.888571:0.001931,A_9:0.00069)0.688471:0.000691,...

对我来说最简单的方法似乎是通过首先用新行替换所有“:”符号来分割文件。所以我的所有目标数字现在都在单独的行中并出现在).然后,我使用下面的 awk 脚本将目标数字乘以 100,但无法保留没有目标数字的行。

脚本:

sed 's/:/\n/g' df9.tree | awk -F")" '{OFS=")"} $2=$2*100 {print $0}'
sed 's/:/\n/g' df9.tree | awk '$NF ~/)/ {$NF *=100}1'

在这种情况下,如何将后面的数字相乘)并打印整个文件?或者是否有其他更简单的方法来直接查找:和之间的数字),将它们乘以 100 并打印整个文件?

更新:预期输出

((((A_8:0.000846,(A_5:0.002449,(A_1:1e-06,((A_4:1e-06,((A_7:1e-06,A_6:0.001061)71.4000:1e-06,A_3:1e-06)31.4500:1e-06)35.8667:1e-06,A_2:1e-06)36.1000:1e-06)43.4800:1e-06)68.3500:0.001619)88.8571:0.001931,A_9:0.00069)68.8471:0.000691,...)

答案1

awk 'BEGIN {OFS=FS=":"; ORS=RS=")"} NR>1 {$1=sprintf("%.4f", $1 * 100)}1' df9.tree

如果您接受将 RS 记录和 FS 字段分开,则所需的数字将始终位于第一个记录之后的第一个字段中。

答案2

$ perl -pe 's/\)([-0-9.]+):/sprintf ")%.4f:", $1 * 100/eg' df9.tree
((((A_8:0.000846,(A_5:0.002449,(A_1:1e-06,((A:1e-06,((A_7:1e-06,A:0.001061)71.4000:1e-06,A:1e-06)31.4500:1e-06)35.8667:1e-06,A:1e-06)36.1000:1e-06)43.4800:1e-06)68.3500:0.001619)88.8571:0.001931,A:0.00069)68.8471:0.000691,...

)替换紧随一个字符并以:数字乘以 100 的字符结尾的所有数字(定义为一个或多个数字、句点或减号字符的序列) 。

例如)0.714000:更改为)71.4000:

它使用perl的/e正则表达式评估修饰符在运算符的RHS中执行perl代码s///。查看man perlop并搜索s\/PATTERN详细信息。sprintf用于将数字格式化为小数点后 4 位。


)如果和之间的数字:可以采用普通十进制表示法(“0.714000”)或“C float”式科学记数法(“1e-06”),则正则表达式需要稍微复杂一点才能匹配所有可能的变化:

$ perl -pe 's/\)(([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?):/sprintf ")%.4f:", $1 * 100/eg' df9.tree
((((A_8:0.000846,(A_5:0.002449,(A_1:1e-06,((A_4:1e-06,((A_7:1e-06,A_6:0.001061)71.4000:1e-06,A_3:1e-06)31.4500:1e-06)35.8667:1e-06,A_2:1e-06)36.1000:1e-06)43.4800:1e-06)68.3500:0.001619)88.8571:0.001931,A_9:0.00069)68.8471:0.000691,...)

以下也可能有效,但可能有一些数字不匹配:

perl -pe 's/\)([-0-9.eE+]+):/sprintf ")%.4f:", $1 * 100/eg'

答案3

使用 perl's s///e,您可以使用评估步骤来确定匹配是否是数字,并进行相应的替换:

perl -MScalar::Util=looks_like_number -pe '
s{\)\K.*?(?=:)}{ looks_like_number($&) ? $&*100 : $& }ge' file

使用 GNU awk,使用正则表达式作为记录分隔符:

gawk 'prevRT==")" && RT==":" && $0+0 == $0 {$0 *= 100} {ORS = prevRT = RT}
1' RS='[):]' file

这通过比较来测试记录的数字性$0+0 == $0

答案4

POSIX sed与桌面计算器和 bash shell 结合使用,dc我们可以获得如图所示的结果。首先,我们计算要合成的字符串的形式,然后通过将其推送到 bash 来合成它。

echo 'echo "'"$(sed -e '
  s#)\([^:)]\{1,\}\):#)$(echo "4k100 \1*1/p"|dc):#g' < file)"'"'|sh

或者,我们可以评估已预先填充命令的变量以生成输出。

var='echo "'$(sed -e '
  s#)\([^:)]\{1,\}\):#)$(echo "4k100 \1*1/p"|dc):#g' < inp)\"

eval "$var"

使用 4 参数split函数来GNU awk跟踪字段和分隔符。

awk '{
  split($0, a, /[:)]/, s)
  for (i=1; i in a; i++)
    print (s[i-1] s[i] == "):" ? sprintf("%.4f",a[i]*100):a[i]) s[i]
  $0=RS
}1' ORS= file

相关内容