Perl - 如何删除匹配的行和文件中匹配项周围的“x”行

时间:2013-11-22 15:51:18

标签: perl file-io

我有一个由我的脚本附加的sitemap.xml文件(该脚本从我的动态网站创建静态页面,并将静态页面网址添加到站点地图。)

但是,有一种情况我需要脚本在不再需要$ SomeID页面时删除(5)行。

以下是我想在$ SomeID(例如12345)匹配时删除的站点地图部分的示例:

     <url>
        <loc>http://mydomain.com/directory/some-page-name-34-098765.htm</loc>
        <changefreq>daily</changefreq>
        <priority>0.5</priority>
        </url>
#I want to delete from here
     <url> 
        <loc>http://mydomain.com/directory/some-page-name-340-12345.htm</loc>
        <changefreq>daily</changefreq>
        <priority>0.5</priority>
        </url>
##to here (when 12345 is matched (done below))
     <url>
        <loc>http://mydomain.com/directory/some-page-name-445-45673.htm</loc>
        <changefreq>daily</changefreq>
        <priority>0.5</priority>
        </url>

现在,我可以找到该行及其编号:

#!/usr/bin/perl -w
use CGI;
use CGI::Carp qw(fatalsToBrowser warningsToBrowser);
print CGI::header();
use strict;
use warnings;
use LWP::Simple qw(!head);
use Fcntl; #needed for the verbose file handler calls like O_CREAT
use File::Slurp;
use File::Basename;
use DBI;
use Tie::File;

ABOVE是我过去因各种原因使用的模块示例。

不需要在下面运行,但只是从另一个脚本粘贴。

my $SiteMapFile = 'sitemap.xml';
my $fh;
my $SomeID='12345'; 
my $cnt = '1';
my @SiteMap;


    open($fh, "<", $SiteMapFile ) || die "Can't open $SiteMapFile: $!";
    my @maplines = <$fh>;
    foreach(@maplines){
    if ($_ =~ $SomeID){
    print qq~Match in sitemap Sitemap Line $_<br />~;
    push(@SiteMap, $_);
    print qq~Found match at line number $cnt<br />;
    }
    $cnt++;
    }
    close $fh;

我将匹配线推入阵列,我想下一步。

有没有办法可以通过'行号删除一行?

那么,我可以删除该行,它之前的行和它之后的3行没有创建临时文件/使用两个文件?

我在这里查看了一些类似的帖子,但没有任何问题可以解决我的问题,或者将其解释为我的理解点。

我几乎是个菜鸟,但耐心等待。 (请原谅我上面的基本编码技巧?) 我放弃了,我在这里!

...谢谢

(如果需要,我可以使用像Tie :: File这样的模块)

3 个答案:

答案 0 :(得分:1)

嗯,最简单的是,如果您在找到邪恶的Id时将$cnt存储在数组中,请说该数组名为@badLines。这样你就可以获得需要删除它们的所有行号。然后你需要一个循环:

my $index=0;
foreach(@badLines)
{
    splice (@maplines, $_-1-($index*5), 5); # Remove 5 lines starting one before the offending one.
    $index++;
}

http://perldoc.perl.org/functions/splice.html

然后您只需将@maplines打印回文件即可。

编辑:我忘了每当拼接完成时,indize如何向下移动。上面的编辑应该处理,但看起来不太好看。当然可以尝试以相反的顺序迭代数组以超越索引shiftig问题。

答案 1 :(得分:1)

这会将标记内的所有数据保存在临时变量中。当遇到另一个时,它会检查temp变量是否包含someID。如果没有,则它会推送@SiteMap数组中的行。最后,您只需要将@SiteMap的内容打印到新文件中。它并不完全是你提出的问题,但使用open标签更有意义,而不是计算行数。

my $SiteMapFile = 'sitemap.xml';
my $fh;
my $SomeID='12345'; 
my $cnt = 1;
my @SiteMap;


open($fh, "<", $SiteMapFile ) || die "Can't open $SiteMapFile: $!";
my @maplines = <$fh>;
my $tmprow;
foreach my $line(@maplines){
  if ($line =~ /<url>/){
    push @SiteMap, $tmprow if ($tmprow !~ /$SomeID/);
    $tmprow = $line;
  } else {
    $tmprow .= $line."\n";
  }
  $cnt++;
}
close $fh;
#dont forget to check the last element
push @SiteMap, $tmprow if ($tmprow !~ /$SomeID/);

print join("\n", @SiteMap);

答案 2 :(得分:1)

将XML固定为字符串非常麻烦和困难。它最初看起来比某些模块更容易,但是一旦你超越了学习曲线,解析器/编写器方法在每个级别上都会更好。

这种方法可以提供更大的灵活性,并保证您最终获得有效的XML。文档:XML::LibXMLXML::LibXML::XPathContext

use XML::LibXML;
use XML::LibXML::XPathContext;

my $doc = XML::LibXML->new->parse_fh(\*DATA);
my $xc  = XML::LibXML::XPathContext->new($doc);
$xc->registerNs( sitemap => $doc->documentElement->namespaceURI );

for my $loc ( $xc->findnodes('//sitemap:loc') )
{
    # Up to you to make the match/check sane and robust.
    $loc->parentNode->unbindNode
        if $loc->textContent =~ /\b 12345 \b/x;
}

print $doc->toString(1);

__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<urlset xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
-- snipped your URLs for space --
</urlset>