2

昨日、perl スクリプトで行き詰まりました。単純化してみましょう。文字列があるとします (たとえば、ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD など)。まず、"E" が来るすべての位置で文字列を分割し、次に、ユーザーが希望する場所で文字列を分割します。ただし、条件として、プログラムは E の後に P が続くサイトで切断してはなりません。たとえば、このシーケンスには 6 つの Es があるため、7 つのフラグメントを取得する必要がありますが、2 つの Es の後に P が続くため、5 つのフラグメントのみが取得されます。出力のフラグメント。

2 番目のケースについて助けが必要です。ユーザーがシーケンス内の E の 5 番目と 10 番目の位置でこのシーケンスをカットしたくないと仮定すると、プログラムがこれら 2 つのサイトのみをスキップできるようにする対応するスクリプトは何になるでしょうか? 最初のケースのスクリプトは次のとおりです。

my $otext = 'ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD';

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

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

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

print "@output";

助けてください!

4

2 に答える 2

4

「P」が続く場所を除いて「E」で分割するには、否定先読みアサーションを使用する必要があります。

perldoc perlre「ルックアラウンド アサーション」セクションから:

  • (?!pattern)
    ゼロ幅の負の先読みアサーション。
    たとえば/foo(?!bar)/、「bar」が後に続かない「foo」の出現に一致します。
my $otext = 'ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD'; 
#                E    E    EP    E    EP    E
my @output=split(/E(?!P)/, $otext); 
use Data::Dumper; print Data::Dumper->Dump([\@output]);"

$VAR1 = [
          'ABCD',
          'ABCD',
          'ABCDEPABCD',
          'ABCDEPABCD',
          'ABCD'
        ];

さて、オカレンス #2 と #4 をカットしないために、次の 2 つのことを行うことができます。

  1. 特定の出現で自動的に一致しない、本当に凝った正規表現を作成します。完全を期すために、他の誰かに答えを試みてもらいます。

  2. 正しい断片をつなぎ合わせるだけです。

    私はそれを行うための良い慣用的な方法を思い付くにはあまりにも頭が死んでいますが、単純で汚い方法は次のいずれかです:

      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); # 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'
              ];
    
于 2012-08-11T12:55:35.083 に答える
0

以下は、2 つの事実を悪用する汚いトリックです。

  • 通常のテキスト文字列には null バイトが含まれることはありません (null バイトが何であるかわからない場合は、プログラマーとして次のことを行う必要があります: http://en.wikipedia.org/wiki/Null_character、および nb。数字の 0 または文字の 0)。
  • perl 文字列をそこに置くと、null バイトを含むことができますが、これにより一部の perl 内部関数が台無しになる可能性があるため、注意してください。

「気をつけて」はあくまでも注意点です。とにかく、アイデアは、ブレークしたくないポイントでヌルバイトを置き換えることです:

my $s = "ABCDEABCDEABCDEPABCDEABCDEPABCDEABCD";

my @nobreak = (4,9);

foreach (@nobreak) {
    substr($s, $_, 1) = "\0";
}

"\0""\t"タブのようなヌルバイトを表すエスケープシーケンスです。繰り返しますが、これは文字 0 ではありません。4 と 9 を使用したのは、それらの位置に E があったからです。文字列を印刷すると、次のようになります。

ABCDABCDABCDEPABCDEABCDEPABCDEABCD

null バイトは表示されませんが、存在するため、後で元に戻します。最初の分割:

my @a = split(/E(?!P)/, $s);

次に、ゼロ バイトを元に戻します。

$_ =~ s/\0/E/g foreach (@a);

ここで印刷する@aと、次のようになります。

ABCDEABCDEABCDEPABCD
ABCDEPABCD
ABCD

これはまさにあなたが望むものです。split は区切り文字 (この場合は E) を削除することに注意してください。それらを保持するつもりなら、後で再びそれらを元に戻すことができます. 区切り文字がより動的な正規表現からのものである場合、少し複雑になります。こちらを参照してください。

http://perlmeme.org/howtos/perlfunc/split_function.html

「例 9. 区切り文字を保持する」

位置が E ではない可能性がある場合は@nobreak、それらを交換するときにそれらを追跡して、正しい文字に再度置き換えることを確認する必要があります。

于 2012-08-11T13:31:32.260 に答える