我有一个以//#...
换行符开头的字符串。我已经弄清楚了 which is this 的正则表达式..#([^\n]*)
。
我的问题是如果以下条件匹配,您如何从文件中删除此行
您的正则表达式在几个方面选择不当:
不是专门匹配两个斜杠,而是..
用来匹配两个可以是任何字符的字符,大概是因为当您还使用斜杠作为分隔符时,您不知道如何匹配斜杠。(实际上,点几乎可以匹配任何东西,正如我们将在 #3 中看到的那样。)
在斜杠分隔的正则表达式文字中//
,您可以简单地通过用反斜杠保护斜杠来匹配斜杠,例如。/\/\//
. 然而,更好的变体是使用较长形式的正则表达式文字,m//
您可以在其中选择分隔符,例如。m!!
. 由于您使用斜线以外的其他内容进行分隔,因此您可以在不转义的情况下编写它们:m!//!
. 请参阅perldoc perlop。
它没有锚定到字符串的开头,因此它将匹配任何地方。使用^
前面的字符串开头断言。
当有一种更简单的写法时,你写[^\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 开关的参数说“打印该行,除非它与行首的 # 字符匹配。
PS。还有一个 -p 开关,其行为类似于 -n,除了总是打印行(有利于搜索和替换)
正如其他人指出的那样,如果最终目标只是删除以 开头的行//#
,出于性能原因,您最好使用grep
or 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/
. 尽早训练自己使用这种语法,因为它是避免过度转义的简单方法。您可以像orm{^//#}
一样有效地编写,具体取决于您要匹配的内容。力求清晰 - 正则表达式很难在没有可避免的反斜杠的多刺森林破坏可读性的情况下破译。严重的是,它看起来像一条带有缺口的牙齿和填充物的鳄鱼,或者是一幅阿尔卑斯山的小型 ASCII 画。m%^//#%
m#^//\##
m/^\/\/#/
您的脚本中可能出现的一个问题是,如果整个文件被拼凑成一个字符串、换行符等等。为了防止这种情况,请在正则表达式上使用 /m(多行)修饰符:
m{^//#}m
这允许 ^ 在字符串的开头和换行符之后匹配。您会认为有一种方法可以m{^//#.*$}
使用 regex 修饰符/g
,来去除或匹配匹配的行/m
,并且/s
在您将文件 slurp 成字符串但您不想复制它的情况下(乞求问题为什么它首先被啜饮成一个字符串。)这应该是可能的,但已经晚了,我没有看到答案。但是,一种“简单”的方法是:
my $cooked = join qq{\n}, (grep { ! m{^//} } (split m{\n}, $raw));
即使这会创建一个副本而不是对原始字符串进行就地编辑$raw
。
你真的不需要 perl。
sed '/^\/\/#/d' inputfile > outputfile
我<3 sed。
逐行读取文件,仅将这些行写入与正则表达式不匹配的新文件。您不能只删除一行。
它是从一行的开头开始还是可以出现在任何地方?如果前 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;
这将打印文件中的所有行,以“//#”开头的行除外。