我编写了一个程序来从邮件文件夹( GITHUB)中提取附件,但由于 Perl 对正则表达式匹配的 32767 行限制,它失败了。我的程序将每个邮件消息加载为单个字符串,然后尝试将每个 base64 编码文件匹配为单个字符串。
要复制问题,首先执行以下操作:
(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
这将创建一个 5403516 字节的文件,其中包含两个文件的 base64 编码,它们之间有一个三重换行缓冲区。生产中的情况稍微复杂一些,但这个更简单的案例说明了问题所在。
我们的目标是提取第一个文件的 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 作为不同的单行变量,并且 $5 和 $6 是空的。
因此,我想我误解了你的方法。帮助?