我编写了一个从邮件文件夹中提取附件的程序( 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个字符,但在看到第一个“=”时停止 sign(表示base64中的文件结尾)。
/tmp/testfile.txt有70180行,第一行有35088行 表示我们想要捕获的字符串(base64-encode) 第一个文件)。
我们现在在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为空。
因此,我认为我误解了你的做法。救命?
答案 0 :(得分:2)
由于您可以通过静态字符串拆分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上有很多的邮件模块,所以找到合适的邮件模块有点困难。