在perl中使用替换运算符跳过字符串中的特定位置

在perl中使用替换运算符跳过字符串中的特定位置,perl,skip,string-substitution,Perl,Skip,String Substitution,昨天,我被一个perl脚本卡住了。让我简化一下,假设有一个字符串(比如abcdeabcdeabcdeabcdeabcdeabcd),首先我必须在“E”出现的每个位置断开它,其次,在用户想要的位置断开它。但是,条件是,程序不应该在E后面跟P的位置进行剪切。例如,在这个序列中有6个E,所以应该得到7个片段,但是当2个E后面跟P时,输出中只会得到5个片段 关于第二个案子我需要帮助。假设用户不想在序列中E的第5和第10个位置剪切这个序列,那么对应的脚本应该是什么,让程序只跳过这两个位置?第一个案例的脚本

昨天,我被一个perl脚本卡住了。让我简化一下,假设有一个字符串(比如abcdeabcdeabcdeabcdeabcdeabcd),首先我必须在“E”出现的每个位置断开它,其次,在用户想要的位置断开它。但是,条件是,程序不应该在E后面跟P的位置进行剪切。例如,在这个序列中有6个E,所以应该得到7个片段,但是当2个E后面跟P时,输出中只会得到5个片段

关于第二个案子我需要帮助。假设用户不想在序列中E的第5和第10个位置剪切这个序列,那么对应的脚本应该是什么,让程序只跳过这两个位置?第一个案例的脚本是:

my $otext = 'ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD';

$otext=~ s/([E])/$1=/g; #Main cut rule.

$otext=~ s/=P/P/g;

@output = split( /\=/, $otext);

print "@output";
请帮忙

要在“E”上拆分,除非后面跟“p”,您应该使用否定的前瞻断言

从“环视断言”部分:

  • (?!模式)
    零宽度负前瞻断言。
    例如
    /foo(?!bar)/
    匹配任何不后跟“bar”的“foo”匹配项。

现在,为了避免在发生第2次和第4次时切割,您可以做两件事:

  • 编造一个真正奇特的正则表达式,在给定的情况下自动匹配失败。为了完整起见,我将把这个问题留给其他人来尝试回答

  • 只需将正确的碎片缝合在一起

    我太笨了,想不出一个好的惯用方法,但简单而肮脏的方法是:

      my %no_cuts = map { ($_=>1) } (2,4); # Do not cut in positions 2,4
      my @output_final;
      for(my $i=0; $i < @output; $i++) {
          if ($no_cuts{$i}) {
              $output_final[-1] .= $output[$i];
          } else {
              push @output_final, $output[$i];
          } 
      }
      print Data::Dumper->Dump([\@output_final];
    
      $VAR1 = [
                'ABCD',
                'ABCDABCDEPABCD',
                'ABCDEPABCDABCD'
              ];
    
    my%no_cuts=map{($u=>1)}(2,4)#不要在位置2,4处切割
    我的@output_final;
    对于(我的$i=0;$i<@output;$i++){
    如果($no_{$i}){
    $output_final[-1]。=$output[$i];
    }否则{
    推送@output_final,$output[$i];
    } 
    }
    打印数据::转储程序->转储([\@output\u final];
    $VAR1=[
    "ABCD",,
    “ABCDABCDEPABCD”,
    “ABCDEPABCDABCD”
    ];
    
    或者更简单一点:

      my %no_cuts = map { ($_=>1) } (2,4); # Do not cut in positions 2,4
      for(my $i=0; $i < @output; $i++) {
          $output[$i-1] .= $output[$i]; 
          $output[$i]=undef; # Make the slot empty
      }
      my @output_final = grep {$_} @output; # Skip empty slots
      print Data::Dumper->Dump([\@output_final];
    
      $VAR1 = [
                'ABCD',
                'ABCDABCDEPABCD',
                'ABCDEPABCDABCD'
              ];
    
    my%no_cuts=map{($=>1)}(2,4);#不要在位置2,4进行剪切
    对于(我的$i=0;$i<@output;$i++){
    $output[$i-1]。=$output[$i];
    $output[$i]=undef;#使插槽为空
    }
    my@output\u final=grep{$\u}@output;#跳过空槽
    打印数据::转储程序->转储([\@output\u final];
    $VAR1=[
    "ABCD",,
    “ABCDABCDEPABCD”,
    “ABCDEPABCDABCD”
    ];
    
  • 要在“E”上拆分,除非后面跟“p”,您应该使用否定的前瞻断言

    从“环视断言”部分:

    • (?!模式)
      零宽度负前瞻断言。
      例如
      /foo(?!bar)/
      匹配任何不后跟“bar”的“foo”匹配项。

    现在,为了避免在发生第2次和第4次时切割,您可以做两件事:

  • 编造一个非常奇特的正则表达式,在给定的情况下自动匹配失败。为了完整性起见,我将把它留给其他人尝试回答

  • 只需将正确的碎片缝合在一起

    我太笨了,想不出一个好的惯用方法,但简单而肮脏的方法是:

      my %no_cuts = map { ($_=>1) } (2,4); # Do not cut in positions 2,4
      my @output_final;
      for(my $i=0; $i < @output; $i++) {
          if ($no_cuts{$i}) {
              $output_final[-1] .= $output[$i];
          } else {
              push @output_final, $output[$i];
          } 
      }
      print Data::Dumper->Dump([\@output_final];
    
      $VAR1 = [
                'ABCD',
                'ABCDABCDEPABCD',
                'ABCDEPABCDABCD'
              ];
    
    my%no_cuts=map{($=>1)}(2,4);#不要在位置2,4进行剪切
    我的@output_final;
    对于(我的$i=0;$i<@output;$i++){
    如果($no_{$i}){
    $output_final[-1]。=$output[$i];
    }否则{
    推送@output_final,$output[$i];
    } 
    }
    打印数据::转储程序->转储([\@output\u final];
    $VAR1=[
    "ABCD",,
    “ABCDABCDEPABCD”,
    “ABCDEPABCDABCD”
    ];
    
    或者更简单一点:

      my %no_cuts = map { ($_=>1) } (2,4); # Do not cut in positions 2,4
      for(my $i=0; $i < @output; $i++) {
          $output[$i-1] .= $output[$i]; 
          $output[$i]=undef; # Make the slot empty
      }
      my @output_final = grep {$_} @output; # Skip empty slots
      print Data::Dumper->Dump([\@output_final];
    
      $VAR1 = [
                'ABCD',
                'ABCDABCDEPABCD',
                'ABCDEPABCDABCD'
              ];
    
    my%no_cuts=map{($=>1)}(2,4);#不要在位置2,4进行剪切
    对于(我的$i=0;$i<@output;$i++){
    $output[$i-1]。=$output[$i];
    $output[$i]=undef;#使插槽为空
    }
    my@output\u final=grep{$\u}@output;#跳过空槽
    打印数据::转储程序->转储([\@output\u final];
    $VAR1=[
    "ABCD",,
    “ABCDABCDEPABCD”,
    “ABCDEPABCDABCD”
    ];
    

  • 这是一个利用两个事实的肮脏伎俩:

    • 普通文本字符串从不包含空字节(如果您不知道空字节是什么,作为程序员,您应该:,和nb。它与数字0或字符0不同)
    • 如果将perl字符串放在那里,它们可能包含空字节,但请小心,因为这可能会破坏一些perl内部函数
    “小心”只是需要注意的一点。无论如何,我们的想法是在不需要中断的地方替换空字节:

    my $s = "ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD";
    
    my @nobreak = (4,9);
    
    foreach (@nobreak) {
        substr($s, $_, 1) = "\0";
    }
    
    “\0”
    是一个转义序列,表示一个空字节,如
    “\t”
    是一个制表符。同样,它不是字符0。我使用了4和9,因为在这些位置有E。如果现在打印字符串,它看起来像:

    ABCDABCDABCDEPABCDEABCDEPABCDEABCD
    
    因为不显示空字节,但它们在那里,我们稍后会将它们交换回。首先,拆分:

    my @a = split(/E(?!P)/, $s);
    
    然后将零字节交换回:

    $_ =~ s/\0/E/g foreach (@a);
    
    如果现在打印
    @a
    ,您将获得:

    ABCDEABCDEABCDEPABCD
    ABCDEPABCD
    ABCD
    
    这正是您想要的。请注意,split会删除分隔符(在本例中为E);如果您打算保留这些分隔符,则可以在以后将其重新加上。如果分隔符来自更动态的正则表达式,则会稍微复杂一些,请参阅此处:

    “示例9.保留分隔符”


    如果有可能
    @nobreak
    位置不是E,那么在替换它们时,您还必须跟踪它们,以确保再次替换为正确的字符。

    下面是一个利用两个事实的肮脏伎俩:

    • 普通文本字符串从不包含空字节(如果您不知道空字节是什么,作为程序员,您应该:,和nb。它与数字0或字符0不同)
    • perl字符串c