我有一个以//#...
开头的字符串,直到换行符结束。我已经找出了匹配它的正则表达式,就是..#([^\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 '...')。该行存储在$_中(这也是许多指令的默认参数,在本例中是print和regex匹配)。
最后,-e开关的参数表示“打印该行,除非该行以#字符开头。
PS. 还有一个-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
我喜欢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
遍历文件中的每一行,如果匹配模式,则跳过该行:
my $fh = new FileHandle 'filename' or die "无法打开文件 - $!";
while (my $line = $fh->getline) { next if $line =~ m{^//#}; print $line; } close $fh;
这将打印文件中除以“//#”开头的行之外的所有行。