2

メール フォルダから添付ファイルを抽出するプログラム ( GITHUB ) を作成しましたが、Perl の 32767 行の正規表現の制限により失敗します。私のプログラムは、各メール メッセージを 1 つの文字列として読み込み、base64 でエンコードされた各ファイルを 1 つの文字列として照合しようとします。

問題を再現するには、まず次のようにします。

(dd if=/dev/urandom bs=2000 count=1000 | base64 ; echo "\n\n\n" ; dd if=/dev/urandom bs=2000 count=1000 | base64 ) >! /tmp/testfile.txt 

これにより、2 つのファイルの base64 エンコードを含む 5403516 バイトのファイルが 1 つ作成され、その間に 3 つの改行バッファーがあります。本番環境の状況はもう少し複雑ですが、この単純なケースが問題を示しています。

私たちの目標は、最初のファイルの base64 エンコーディングを抽出することです。つまり、50 文字以上で base64 文字のみを含む連続するすべての行ですが、最初の「=」記号 (base64 でファイルの終わりを示す) が表示されると停止します。

/tmp/testfile.txt には 70180 行あり、最初の 35088 行はキャプチャする文字列 (最初のファイルの base64 エンコード) を表しています。

Perl で次のことを行います。

# next 4 lines: read the entire file into a single variable 
undef $/; 
open(A,"/tmp/testfile.txt"); 
$all = <A>; 
close(A); 

# the output of base64 consists of these characters (plus "=" and 
# "\n", but those two are special cases) 
my($chars) = "[a-zA-Z0-9\+\/]"; 

# we declare a subroutine for testing 
sub foo {print STDERR length($_[0]),"\n";} 

# this is what I tried to do originally 
$all=~s/(\n($chars{50,}\=*\n)+)($chars+\=*\n)/foo("$1$3")/seg; 

上記は、「2523137」、「178467」、「2523137」、「178544」を STDERR に返します。

つまり、最初のファイルの最初の 2523137 文字をキャプチャし、最初のファイルの次の 178467 文字をキャプチャするのではなく、最初のファイルの 2701604 文字をすべてキャプチャします。2523137 は約 77*32767 であることに注意してください (また、/tmp/testfile.txt の各行の長さは 77 文字です)。

@ikegami、私が正しく理解していれば、あなたのアプローチは次のとおりです。

$all=~s/((\n($chars{50,}\=*\n){0,20000})+)($chars+\=*\n)//seg; 

つまり、一度に 20000 行をキャプチャします (32767 行の制限を回避します) が、20000 行の複数の束をキャプチャします。これは正しいです?

結果は複数の変数で出力されるため、結果を foo() に渡しませんでしたが、代わりに次のように結果を STDERR に出力しました。

print STDERR "1 is $1\n"; 
print STDERR "2 is $2\n"; 
print STDERR "3 is $3\n"; 
print STDERR "4 is $4\n"; 
print STDERR "5 is $5\n"; 
print STDERR "6 is $6\n"; 

これにより、$1 と $2 が同一の 15085 行変数として生成され、$3 と $4 が同一でない 1 行変数として生成され、$5 と $6 が空として生成されます。

したがって、私はあなたのアプローチを誤解したと思います。ヘルプ?

4

1 に答える 1

1

base64 ピースを静的文字列で分割できるため、 を使用$/してファイルをより効率的に分割し、各ピースが基準に一致するかどうかを選択できます。

use strict;
use warnings;
use autodie;

my $is_base64 = qr{^[a-zA-Z0-9\+\/]+\n?$}m;

{
    open(my $fh,"/tmp/testfile.txt");
    local $/ = "=\n";

    while(my $base64 = <$fh>) {
        chomp $base64;
        _strip(\$base64);
        next unless $base64 =~ $is_base64;

        print STDERR length $base64, "\n";
    }
}

sub _strip {
    my $ref = shift;
    $$ref =~ s{^\s+}{};
    $$ref =~ s{\s+$}{};

    return;
}

これは、メールボックスを分割する場合にも便利$/です"\n\nFrom "

ただし、モジュールでこれを行う必要があることを示唆するコメントは正しいです。CPAN には多くのメール モジュールがあるため、適切なものを見つけるのは少し難しい場合があります。

于 2013-01-25T05:10:46.393 に答える