scr*_*345 1 regex perl filemap
更新2:解决了.见下文.
我正在将一个大的txt文件从旧的基于DOS的库程序转换为更有用的格式.我刚开始使用Perl并设法将这样的脚本放在一起:
BEGIN {undef $/; };
open $in, '<', "orig.txt" or die "Can't read old file: $!";
open $out, '>', "mod.txt" or die "Can't write new file: $!";
while( <$in> )
{
$C=s/foo/bar/gm;
print "$C matches replaced.\n"
etc...
print $out $_;
}
close $out;
Run Code Online (Sandbox Code Playgroud)
这是相当快的,但经过一段时间后,我总是得到一个"Out of Memory" - 由于缺少RAM/Swap-Space而导致的错误(我在Win XP上使用2GB的Ram和1.5GB的Swap-File).在看了一下如何处理大文件后,File::Map在我看来这是一个避免这个问题的好方法.但是,我在实施它时遇到了麻烦.这就是我现在所拥有的:
#!perl -w
use strict;
use warnings;
use File::Map qw(map_file);
my $out = 'output.txt';
map_file my $map, 'input.txt', '<';
$map =~ s/foo/bar/gm;
print $out $map;
Run Code Online (Sandbox Code Playgroud)
但是我收到以下错误: Modification of a read-only value attempted at gott.pl line 8.
另外,我在File::Map帮助页面上看到,在我需要使用的非Unix系统上binmode.我怎么做?
基本上,我想要做的是通过File :: Map"加载"文件,然后运行如下代码:
$C=s/foo/bar/gm;
print "$C matches found and replaced.\n"
$C=s/goo/far/gm;
print "$C matches found and replaced.\n"
while(m/complex_condition/gm)
{
$C=s/complex/regex/gm;
$run_counter++;
}
print "$C matches replaced. Script looped $run_counter times.\n";
etc...
Run Code Online (Sandbox Code Playgroud)
我希望我没有忽略太明显的东西,但File::Map帮助页面上给出的示例仅显示如何从映射文件中读取,对吗?
编辑:
为了更好地说明由于内存不足我目前无法完成的任务,我将举例说明:
在http://pastebin.com/6Ehnx6xA上是我们导出的一个库记录(txt格式)的示例.我对+Deskriptoren:从第46行开始的部分感兴趣.这些是以树形层次结构组织的主题分类器.
我要的是要扩大其完整的父节点的链中的每个分类,但只如果没有父节点是不存在之前或有问题的子节点之后.这意味着转身
+Deskriptoren
-foo
-Cultural Revolution
-bar
Run Code Online (Sandbox Code Playgroud)
成
+Deskriptoren
-foo
-History
-Modern History
-PRC
-Cultural Revolution
-bar
Run Code Online (Sandbox Code Playgroud)
目前使用的Regex使用Lookbehind和Lookahead以避免重复重复,因此比以下更复杂s/foo/bar/g;:
s/(?<=\+Deskriptoren:\n)((?:-(?!\QParent-Node\E).+\n)*)-(Child-Node_1|Child-Node_2|...|Child-Node_11)\n((?:-(?!Parent-Node).+\n)*)/${1}-Parent-Node\n-${2}\n${3}/g;
Run Code Online (Sandbox Code Playgroud)
但它的确有效!直到Perl耗尽内存......:/
所以本质上我需要一种方法来对几行上的大文件(80MB)进行操作.处理时间不是问题.这就是我想到File :: Map的原因.另一种选择可能是在几个步骤中处理文件,链接的perl脚本相互调用然后终止,但我想尽可能地将它保存在一个地方.
更新2:
我设法让它与下面的Schwelm代码一起使用.我的脚本现在调用以下子程序,该子程序调用两个嵌套的子程序.示例代码位于:http://pastebin.com/SQd2f8ZZ
仍然不太满意我无法File::Map上班.哦,好吧......我猜这种线路方法无论如何都更有效率.
感谢大家!
当您将$/(输入记录分隔符)设置为undefined时,您正在"啜饮"文件 - 一次读取文件的整个内容(例如,在perlvar中对此进行了讨论).因此,内存不足的问题.
相反,如果可以的话,一次处理一行:
while (my $line = <$in>){
# Do stuff.
}
Run Code Online (Sandbox Code Playgroud)
在文件足够小并且您对文件进行啜食的情况下,不需要while循环.第一次阅读得到了一切:
{
local $/ = undef;
my $file_content = <>;
# Do stuff with the complete file.
}
Run Code Online (Sandbox Code Playgroud)
更新
在看到你的大规模正则表达式后,我会敦促你重新考虑你的策略.解决这个问题作为解析问题:一次处理一行文件,根据需要存储有关解析器状态的信息.这种方法允许您使用简单,易于理解(甚至可测试)的步骤来处理信息.
你当前的策略 - 人们可能称之为沉闷和大规模正则表达式策略 - 很难理解和维护(在3个月内你的正则表达式会立即对你有意义吗?),难以测试和调试,难以调整如果您发现与初步了解数据的意外偏差.此外,正如您所发现的,该策略易受内存限制(因为需要粘贴文件).
StackOverflow上有很多问题说明当有意义的单位跨越多行时如何解析文本.另请参阅这个问题,我向另一个提问者提供了类似的建议.