【发布时间】:2010-09-09 23:22:04
【问题描述】:
我有一个以//#... 开头的字符串到换行符。我已经找到了 ..#([^\n]*) 的正则表达式。
我的问题是如果以下条件匹配,你如何从文件中删除这一行
【问题讨论】:
我有一个以//#... 开头的字符串到换行符。我已经找到了 ..#([^\n]*) 的正则表达式。
我的问题是如果以下条件匹配,你如何从文件中删除这一行
【问题讨论】:
您的正则表达式在几个方面选择不当:
您没有专门匹配两个斜杠,而是使用.. 来匹配两个可以是任何字符的字符,大概是因为当您还使用斜杠作为分隔符时,您不知道如何匹配斜杠。 (实际上,点匹配几乎任何东西,正如我们将在 #3 中看到的那样。)
在斜杠分隔的正则表达式文字// 中,您可以简单地通过用反斜杠保护斜杠来匹配斜杠,例如。 /\/\//。然而,更好的变体是使用较长形式的正则表达式文字m//,您可以在其中选择分隔符,例如。 m!!。由于您使用斜线以外的其他内容进行分隔,因此您可以在不转义的情况下编写它们:m!//!。见perldoc perlop。
它没有锚定到字符串的开头,所以它会匹配任何地方。使用前面的^ start-of-string 断言。
你写了[^\n] 来匹配“除换行符之外的任何字符”,但有一种更简单的写法,那就是. 通配符。它正是这样做的——匹配除换行符以外的任何字符。
您正在使用括号对匹配的一部分进行分组,但该组既没有量化(您没有指定它可以匹配任何其他次数而不是一次),您也没有兴趣保留它。所以括号是多余的。
总而言之,这就是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 只有在成功关闭后才会替换原始文件。
【讨论】:
过滤掉文件中与某个正则表达式匹配的所有行:
perl -n -i.orig -e 'print unless /^#/' file1 file2 file3
-i 开关后的“.orig”创建具有给定扩展名 (.orig) 的文件的备份。如果不需要备份,可以跳过它(只需使用 -i)。
-n 开关使 perl 为文件中的每一行执行您的指令(-e ' ... ')。该行存储在 $_ 中(这也是许多指令的默认参数,在这种情况下:打印和正则表达式匹配)。
最后,-e 开关的参数是“打印该行,除非它与行首的 # 字符匹配。
附言。还有一个 -p 开关,其行为类似于 -n,除了总是打印行(有利于搜索和替换)
【讨论】:
正如其他人所指出的,如果最终目标只是删除以//# 开头的行,出于性能原因,您最好使用grep 或sed:
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
这允许 ^ 匹配字符串的开头和换行符之后的。您会认为有一种方法可以使用正则表达式修饰符 /g、/m 和 /s 去除或匹配匹配 m{^//#.*$} 的行,以防您将文件转换为字符串但您没有不想复制它(乞求为什么它首先被啜饮成一个字符串。)它应该是可能的,但已经晚了,我没有看到答案。但是,一种“简单”的做法是:
my $cooked = join qq{\n}, (grep { ! m{^//} } (split m{\n}, $raw));
即使这会创建一个副本,而不是对原始字符串 $raw 进行就地编辑。
【讨论】:
你真的不需要 perl。
sed '/^\/\/#/d' inputfile > outputfile
我
【讨论】:
逐行读取文件,仅将这些行写入与正则表达式不匹配的新文件。 你不能只删除一行。
【讨论】:
它是从行首开始还是可以出现在任何地方?如果前 s/old/new 是您想要的。如果是后者,我将不得不弄清楚这一点。我怀疑可以以某种方式使用反向引用。
【讨论】:
我认为您的正则表达式不正确。
首先,您需要以 ^ 开头,否则它将匹配该模式的任何位置。
其次,.. 应该是\/\/,否则它将匹配任意两个字符。
^\/\/#[^\n]* 可能就是你想要的。
然后按照 EricSchaefer 所说的去做,逐行读取文件,只写入不匹配的行。
--
bmb
【讨论】:
尝试以下方法:
perl -ne 'print unless m{^//#}' input.txt > output.txt
如果您使用的是 windows,则需要双引号而不是单引号。
你可以用 grep 做同样的事情
grep -v -e '^//#' input.txt > output.txt
【讨论】:
遍历文件中的每一行,如果匹配模式则跳过该行:
我的 $fh = new FileHandle '文件名' 或死“无法打开文件 - $!”; 而(我的 $line = $fh->getline){ 接下来如果 $line =~ m{^//#}; 打印$行; } 关闭 $fh;这将打印文件中的所有行,以“//#”开头的行除外。
【讨论】: