【问题标题】:match string between columns using perl使用perl在列之间匹配字符串
【发布时间】:2014-08-05 02:08:43
【问题描述】:

我想将 A 列中的字符串与 B 列中每一行的字符串进行比较,并打印第三列突出显示差异。

Column A                      Column B
uuaaugcuaauugugauaggggu       uuaaugcuaauugugauaggggu
uuaaugcuaauugugauagggguu      uuaaugcuaauugugauaggggu
uuaaugcuaauugugauagggguuu     uuaaugcuaauugugauaggggu

期望的结果:

Column A                      Column B                Column C
uuaaugcuaauugugauaggggu       uuaaugcuaauugugauaggggu ********************
uuaaugcuaauugugauagggguu      uuaaugcuaauugugauaggggu ********************u
uuaaugcuaauugugauagggguuu     uuaaugcuaauugugauaggggu ********************uu

我有一个可能有效的示例脚本,但我该如何对数据框中的每一行执行此操作?

use strict;
use warnings;
my $string1 = 'AAABBBBBCCCCCDDDDD';
my $string2 = 'AEABBBBBCCECCDDDDD';
my $result = '';
for(0 .. length($string1)) {
    my $char = substr($string2, $_, 1);
    if($char ne substr($string1, $_, 1)) {
        $result .= "**$char**";
    } else {
        $result .= $char;
    }
}
print $result;

【问题讨论】:

  • 有一个难以想象的问题“突出差异的第三列”。如果前两列是ABCABD,那么“区别”将是第一列在第三个位置有C,而第一列有D。除非第二个字符串总是以第一个字符串的内容开头,否则很难用一个字符串来表达这种差异,你应该说出你需要的内容。
  • 假设第一列包含uuaaug,第二列包含gggacc,第三列希望看到什么?

标签: perl string-matching


【解决方案1】:

使用暴力破解和substr

use strict;
use warnings;

while (<DATA>) {
    my ($str1, $str2) = split;
    my $len = length $str1 < length $str2 ? length $str1 : length $str2;
    for my $i (0..$len-1) {
        my $c1 = substr $str1, $i, 1;
        my $c2 = substr $str2, $i, 1;
        if ($c1 eq $c2) {
            substr $str1, $i, 1, '*';
            substr $str2, $i, 1, '*';
        }
    }
    printf "%-30s %s\n", $str1, $str2;
}

__DATA__
Column_A                      Column_B
uuaaugcuaauugugauaggggu       uuaaugcuaauugugauaggggu
uuaaugcuaauugugauagggguu      uuaaugcuaauugugauaggggu
uuaaugcuaauugugauagggguuu     uuaaugcuaauugugauaggggu
AAABBBBBCCCCCDDDDD            AEABBBBBCCECCDDDDD

输出:

*******A                       *******B
***********************        ***********************
***********************u       ***********************
***********************uu      ***********************
*A********C*******             *E********E*******

使用 XOR 的替代方法

也可以使用^ 来查找两个字符串之间的交集。

下面的执行与上面相同:

while (<DATA>) {
    my ($str1, $str2) = split;

    my $intersection = $str1 ^ $str2;
    while ($intersection =~ /(\0+)/g) {
        my $len = length $1;
        my $pos = pos($intersection) - $len;
        substr $str1, $pos, $len, '*' x $len;
        substr $str2, $pos, $len, '*' x $len;
    }

    printf "%-30s %s\n", $str1, $str2;
}

【讨论】:

  • 嗯。我收到此错误:无法通过包“UUAAUGCUAAUUGUGAUAGGGGUU”定位对象方法“UUAAUGCUAAUUGUGAUAGGGGU”(也许您忘记加载“UUAAUGCUAAUUGUGAUAGGGGUU”?)在 isomir_test.csv 第 1 行
  • @user3741035:嗯?你加载UUAAUGCUAAUUGUGAUAGGGGUU了吗?!
  • @user3741035:对不起——我在开玩笑。我希望“!”会告诉你的。我看不到任何可能被误解为方法调用的内容,我认为您一定是在尝试执行 CSV 文件,就好像它是 Perl 脚本一样?
  • 谢谢!事实上,我确实执行了 csv 而不是脚本 :))
【解决方案2】:

我忍不住用正则表达式提供了修改后的米勒解决方案

   use strict;
   use warnings;

   while (<DATA>) {
    my $masked_str1 ="";
    my $masked_str2 ="";
    my ($str1, $str2) = split;

    my $intersection = $str1 ^ $str2;
    while ($intersection =~ /(\x00+)/g) {

        my $mask = $intersection;
        $mask =~ s/\x00/1/g;
        $mask =~ s/[^1]/0/g;

        while ( $mask =~ /\G(.)/gc ) { # traverse the mask
           my $bit = $1;
           if ( $str1 =~ /\G(.)/gc ) { # traverse the string1 to be masked
                $masked_str1 .= $bit ? '_' : $1;
           }
           if ( $str2 =~ /\G(.)/gc ) { # traverse the string2 to be masked
                $masked_str2 .= $bit ? '_' : $1;
           }
        }

    }
    print "=" x 80;
    printf "\n%-30s %s\n", $str2, $str1; # Minimum length 30 char, left-justified
    printf "%-30s %s\n", $str1, $str2;  
    printf "%-30s %s\n\n", $masked_str1, $masked_str2;  


}

【讨论】:

  • @user3741035 可读性比速度更重要,所以选择你最喜欢的。但是,此方法比我的 substr 解决方案慢 3 倍,比我的替代 xor 解决方案慢 10 倍:benchmarks
  • 对我来说只是一个正则表达式练习。感谢您的基准测试。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-15
  • 2013-04-26
  • 2019-04-02
相关资源
最近更新 更多