Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Windows Perl";“内存不足”;使用大型文本文件_Windows_String_Perl_Memory - Fatal编程技术网

Windows Perl";“内存不足”;使用大型文本文件

Windows Perl";“内存不足”;使用大型文本文件,windows,string,perl,memory,Windows,String,Perl,Memory,在最新版本的草莓Perl for Windows下,我对以下代码有一个问题:我想读入目录中的所有文本文件并处理它们的内容。我目前看不到一种逐行处理它们的方法,因为我想对文件内容所做的一些更改是跨换行的。处理过程主要涉及删除大块文件(在我下面的示例代码中,这只是一行,但理想情况下,我会运行两个类似的正则表达式,每个正则表达式都会从文件中删除内容) 我在大量文件(>10000)上运行此脚本,它总是在一个大于400 MB的特定文件上出现“内存不足”消息。问题是,当我编写一个只处理一个文件的程序时,代码

在最新版本的草莓Perl for Windows下,我对以下代码有一个问题:我想读入目录中的所有文本文件并处理它们的内容。我目前看不到一种逐行处理它们的方法,因为我想对文件内容所做的一些更改是跨换行的。处理过程主要涉及删除大块文件(在我下面的示例代码中,这只是一行,但理想情况下,我会运行两个类似的正则表达式,每个正则表达式都会从文件中删除内容)

我在大量文件(>10000)上运行此脚本,它总是在一个大于400 MB的特定文件上出现“内存不足”消息。问题是,当我编写一个只处理一个文件的程序时,代码运行良好

这台机器有8GB内存,所以我认为物理内存不是问题

我通读了其他关于记忆问题的帖子,但没有找到任何有助于我实现目标的东西

有人能建议我需要修改什么才能使程序正常工作吗,比如说,提高内存效率,或者以某种方式回避这个问题

use strict;
use warnings;
use Path::Iterator::Rule;
use utf8;

use open ':std', ':encoding(utf-8)';

my $doc_rule = Path::Iterator::Rule->new;
$doc_rule->name('*.txt'); # only process text files
$doc_rule->max_depth(3); # don't recurse deeper than 3 levels
my $doc_it = $doc_rule->iter("C:\Temp\");
while ( my $file = $doc_it->() ) { # go through all documents found
    print "Stripping $file\n";

    # read in file
    open (FH, "<", $file) or die "Can't open $file for read: $!";
    my @lines;
    while (<FH>) { push (@lines, $_) }; # slurp entire file
    close FH or die "Cannot close $file: $!";

    my $lines = join("", @lines); # put entire file into one string

    $lines =~ s/<DOCUMENT>\n<TYPE>EX-.*?\n<\/DOCUMENT>//gs; #perform the processing

    # write out file
    open (FH, ">", $file) or die "Can't open $file for write: $!";
    print FH $lines; # dump entire file
    close FH or die "Cannot close $file: $!";
}
使用严格;
使用警告;
使用Path::Iterator::Rule;
使用utf8;
使用open':std',':encoding(utf-8)';
我的$doc\u rule=Path::Iterator::rule->new;
$doc_rule->name('*.txt');#仅处理文本文件
$doc_规则->最大深度(3);#不要在超过3级的地方重复出现
my$doc\u it=$doc\u rule->iter(“C:\Temp\”;
而(my$file=$doc_it->()){检查所有找到的文档
打印“剥离$file\n”;
#读入文件

打开(fh,),同时在内存中保存文件的两个完整副本,<代码> @行< /代码>和<代码> $行<代码>。您可以考虑:

open (my $FH, "<", $file) or die "Can't open $file for read: $!";
$FH->input_record_separator(undef); # slurp entire file
my $lines = <$FH>;
close $FH or die "Cannot close $file: $!";

open(my$FH),使用正则表达式处理XML容易出错且效率低下,正如将整个文件以字符串形式表示的代码所示。要处理XML,您应该使用XML解析器。特别是,您需要一个SAX解析器,它一次只处理一部分XML,而不是一个读取整个文件的DOM解析器

我将按原样回答你的问题,因为知道如何逐行工作是有价值的

如果可以避免,请不要将整个文件读入内存。逐行操作。您的任务似乎是出于某种原因从XML文件中删除少量行。介于
\nEX-
之间的所有内容。我们可以通过保留一点状态来逐行执行

use autodie;

open (my $infh, "<", $file);
open (my $outfh, ">", "$file.tmp");

my $in_document = 0;
my $in_type_ex  = 0;
while( my $line = <$infh> ) {
    if( $line =~ m{<DOCUMENT>\n}i ) {
        $in_document = 1;
        next;
    } 
    elsif( $line =~ m{</DOCUMENT>}i ) {
        $in_document = 0;
        next;
    }
    elsif( $line =~ m{<TYPE>EX-}i ) {
        $in_type_ex = 1;
        next;
    }
    elsif( $in_document and $in_type_ex ) {
        next;
    }
    else {
        print $outfh $line;
    }
}

rename "$file.tmp", $file;
使用autodie;
打开(my$infh,“,$file.tmp”);
我的$in_文档=0;
我的$in_type_ex=0;
while(我的$line=){
if($line=~m{\n}i){
$in_文件=1;
下一个
} 
elsif($line=~m{}i){
$in_文件=0;
下一个
}
elsif($line=~m{EX-}i){
$in_type_ex=1;
下一个
}
elsif($in_文件和$in_类型){
下一个
}
否则{
打印$outph$行;
}
}
重命名“$file.tmp”,$file;
使用临时文件可以在构建替换文件时读取该文件


当然,如果XML文档的格式不是如此(我在正则表达式中添加了
/I
标志以允许小写标记),那么这将失败,您应该真正使用SAX XML解析器。

逐行处理文件:

while ( my $file = $doc_it->() ) { # go through all documents found
    print "Stripping $file\n";

    open (my $infh, "<", $file) or die "Can't open $file for read: $!";
    open (my $outfh, ">", $file . ".tmp") or die "Can't open $file.tmp for write: $!";

    while (<$infh>) {
       if ( /<DOCUMENT>/ ) {
           # append the next line to test for TYPE
           $_ .= <$infh>;
           if (/<TYPE>EX-/) {
              # document type is excluded, now loop through 
              # $infh until the closing tag is found.
              while (<$infh>) { last if m|</DOCUMENT>|; }

              # jump back to the <$infh> loop to resume
              # processing on the next line after </DOCUMENT>
              next;
           }
           # if we've made it this far, the document was not excluded
           # fall through to print both lines
       }
       print $outfh $_;
    }

    close $outfh or die "Cannot close $file: $!";
    close $infh or die "Cannot close $file: $!";
    unlink $file;
    rename $file.'.tmp', $file; 
}
while(my$file=$doc_it->()){检查找到的所有文档
打印“剥离$file\n”;

在Windows Server 2013上使用Perl 5.10.1处理一个稍大的(1.2G)文件时,我注意到

foreach my$line(){}
由于内存不足而失败,而

while (my $line = <LOG>) {}
while(my$line=){

在一个简单的脚本中工作,该脚本只运行一些regexp并打印我感兴趣的行。

不确定您是否遇到过这一问题,但这是一个很好的解决方案,可以对堆内存进行分析,并使用内存映射文件,而不是对文件进行SLURP。与其进行多行搜索和替换,为什么不这样做:1)逐行读取文件,直到找到开头分隔符(2)检查后续行是否符合所需条件;如果符合,则在达到结束分隔符之前不要将任何内容打印到输出文件中;如果不符合,则打印它们。您可以尝试使用类似于处理XML数据的方式,您应该使用它而不是解决方案(这就是我将此作为注释发布的原因),但是,值得一提的是,您可以一次一行地读取文件,如
my@lines=;
可能有点过于简化。原始代码中的
*
s
修饰符的影响。实际上,他们应该使用SAX解析器。当然,这是一个有趣的小练习。我已经更新了答案to容纳@tjd的评论。我在回答末尾暗示的解决方案,但懒得写。+1认为是。现在短了一点:)你测试过这个吗?你有一个多余的
next
,但更重要的是,在你的内部
while
循环之后,
$\u
中有什么还很不清楚。看起来你将打印结束
标记,而OP的代码会删除它。是的,它已经测试过了。
next
跳回的顶部ode>while
,避免在成功的类型匹配后打印。但是,如果您看不到,则可能会出现一些注释。这是因为在第一个示例中,在列表上下文中计算,即,整个文件都在列表上下文中。在第二个示例中,在标量上下文中计算,一次只提取一行。
while (my $line = <LOG>) {}