Perl正则表达式匹配和删除

时间:2008-09-17 06:00:51

标签: regex perl

我有一个以//#...开头的字符串到达​​换行符。我已经找出了正则表达式..#([^\n]*)

我的问题是,如果以下条件匹配

,如何从文件中删除此行

9 个答案:

答案 0 :(得分:27)

你的正则表达式在几个方面被严重挑选:

  1. 不是专门匹配两个斜杠,而是使用..匹配两个可能是任何东西的字符,大概是因为当你还使用斜杠时你不知道如何匹配斜杠分隔符。 (实际上,点匹配几乎任何东西,我们将在#3中看到。)

    在斜线分隔的正则表达式文字//中,您可以通过使用反斜杠保护它们来匹配斜杠,例如。 /\/\//。然而,更好的变体是使用较长形式的正则表达式文字m//,您可以在其中选择分隔符,例如。 m!!。由于您使用斜杠以外的其他内容进行分隔,因此您可以在不转义它们的情况下编写它们:m!//!。请参阅perldoc perlop

  2. 它没有锚定到字符串的开头,因此它将匹配任何地方。使用前面的^字符串开头断言。

  3. 你写[^\n]以匹配“除了换行符之外的任何字符”,当有一种更简单的方法来编写它时,它只是.通配符。它确切地说 - 匹配除换行符之外的任何字符。

  4. 您正在使用括号对匹配的一部分进行分组,但该组既没有量化(您没有指定它可以匹配任何其他次数而不是一次),您也不想保留它。所以括号是多余的。

  5. 总而言之,这就是m!^//#.*!。但是在正则表达式的末尾放置一个未捕获的.*(或任何带有*量词的东西)是没有意义的,因为它永远不会改变字符串是否匹配:*很高兴什么都不匹配。

    这样就可以让你m!^//#!

    对于从文件中删除行,正如其他人解释的那样,逐行读取并打印要保留的所有行到另一个文件。如果您不是在更大的程序中执行此操作,请使用perl的命令行开关轻松完成:

    perl -ni.bak -e'print unless m!^//#!' somefile.txt
    

    这里,-n开关使perl围绕您提供的代码循环,它将按顺序读取您在命令行上传递的所有文件。 -i开关(用于“就地”)表示从脚本中收集输出并用它覆盖每个文件的原始内容。 .bak选项的-i参数告诉perl将原始文件的备份保存在以附加.bak的原始文件名命名的文件中。对于所有这些位,请参阅perldoc perlrun

    如果你想在一个更大的程序的上下文中这样做,最安全的方法是打开文件两次,一次用于阅读,另一次用IO::AtomicFile,另一次写入。 IO :: AtomicFile只有在成功关闭后才会替换原始文件。

答案 1 :(得分:4)

过滤掉文件中与某个正则表达式匹配的所有行:

perl -n -i.orig -e 'print unless /^#/' file1 file2 file3

-i开关后的'.orig'创建具有给定扩展名(.orig)的文件备份。如果您不需要备份,可以跳过它(只需使用-i)。

-n开关使perl为文件中的每一行执行指令(-e'...')。该行存储在$ _(这也是许多指令的默认参数,在这种情况下:print和regex匹配)。

最后,-e开关的参数说“打印该行,除非它与行开头的#字符匹配。

PS。还有一个-p开关,其行为类似于-n,除了总是打印线条(适合搜索和替换)

答案 2 :(得分:2)

正如其他人所指出的那样,如果最终目标只是删除以//#开头的行,出于性能原因,您可能最好使用grepsed

grep -v '^\/\/#' filename.txt > filename.stripped.txt

sed '/^\/\/#/d' filename.txt > filename.stripped.txt

sed -i '/^\/\/#/d' filename.txt

如果您更喜欢就地编辑。

请注意,在perl中你的正则表达式将是

m{^//#}

匹配两个斜杠,后跟字符串开头的#。

请注意,您可以使用匹配运算符m{pattern}而不是更熟悉的/pattern/来避免“反斜杠”。尽早训练自己的语法,因为这是避免过度转义的简单方法。您可以像m{^//#}m%^//#%一样有效地撰写m#^//\##,具体取决于您要匹配的内容。力求清晰 - 正则表达式很难解释,没有可避免的反斜杠的刺痛森林,从而导致可读性降低。说真的,m/^\/\/#/看起来像一条鳄鱼,上面有一颗碎齿和阿尔卑斯山的填充物或微小的ASCII画。

您的脚本中可能出现的一个问题是,如果将整个文件分成字符串,换行符和所有文件。要防御这种情况,请在正则表达式上使用/ m(多行)修饰符:

m{^//#}m

这允许^在换行符后在字符串的开头匹配。您会认为有一种方法可以使用正则表达式修饰符m{^//#.*$}/g/m来删除或匹配与/s匹配的行。把文件写成一个字符串,但是你不想复制它(首先要问一下它为什么会被插入一个字符串的问题。)它应该是可能的,但它已经很晚了我没有看到答案。但是,一种“简单”的方法是:

my $cooked = join qq{\n}, (grep { ! m{^//} } (split m{\n}, $raw));

即使在原始字符串$raw上创建副本而不是就地编辑。

答案 3 :(得分:1)

你真的不需要perl。

sed '/^\/\/#/d' inputfile > outputfile

我< 3 sed。

答案 4 :(得分:0)

逐行读取文件,只将这些行写入与正则表达式不匹配的新文件。 你不能只删除一行。

答案 5 :(得分:0)

它是从一条线的开头开始还是可以出现在任何地方?如果前者/旧/新是你想要的。如果是后者,我将不得不弄明白。我怀疑可以以某种方式使用反击。

答案 6 :(得分:0)

我不认为你的正则表达式是正确的。

首先你需要从^开始,否则它会在线上的任何地方匹配这个模式。

其次,..应为\/\/,否则它将匹配任意两个字符。

^\/\/#[^\n]*可能就是你想要的。

然后执行EricSchaefer所说的并逐行读取文件,只写不匹配的行。

-
BMB

答案 7 :(得分:0)

尝试以下方法:

perl -ne 'print unless m{^//#}' input.txt > output.txt

如果您使用的是Windows,则需要双引号而不是单引号。

你可以用grep

做同样的事情
grep -v -e '^//#' input.txt > output.txt

答案 8 :(得分:0)

迭代文件中的每一行,如果匹配模式,则跳过该行:

my $fh = new FileHandle 'filename'
    or die "Failed to open file - $!";

while (my $line = $fh->getline) {
    next if $line =~ m{^//#};
    print $line;
}
close $fh;

这将打印文件中的所有行,但以'//#'。

开头的行除外