正则表达式:删除偶数行

3

我需要帮助构建一个正则表达式,可以从纯文本文件中删除偶数行。

给定以下输入:

line1
line2
line3
line4
line5
line6

它将输出以下内容:

line1
line3
line5

谢谢!


1
你真的需要使用正则表达式吗?如果可能的话,这似乎相当困难。例如,在awk或sed中处理它非常简单。 - Martin Wickman
2
那不是“奇数”,那是“偶数”。 - bart
我们需要更多信息。是否涉及编程语言?您为什么认为正则表达式在这里是正确的选择? - Jay Bazuzi
当我还是个技工时,还没有成为程序员,我们常说“用正确的工具做正确的工作”。你确定正则表达式是这个任务的正确工具吗?最后,你如何定义一行?CR、LF、CR & LF,还是使用ReadLine?文件可能只有一行,但有几千个字符。 - Mauro
1
@bart:只有你的大脑从零开始索引,对吧? :) - Jørn Schou-Rode
6个回答

9
实际上,你不需要使用正则表达式来实现这个功能。使用你喜欢的编程语言迭代文件,使用计数器并进行模运算即可。例如,在 Unix 系统中可以使用 awk。
$ awk 'NR%2==1' file
line1
line3
line5

偶数行:

$ awk 'NR%2==0' file
line2
line4
line6

4

好的,如果你在搜索和替换所有匹配项时

^(.*)\r?\n.*

在“^匹配行首模式”和“.不匹配换行符模式”的情况下进行替换。

\1

那么你会丢失每一行的偶数行。

例如,在C#中:

resultString = Regex.Replace(subjectString, @"^(.*)\r?\n.*", "$1", RegexOptions.Multiline);

或者在 Python 中:
result = re.sub(r"(?m)^(.*)\r?\n.*", r"\1", subject)

你还应该考虑到行数为奇数的情况。 - Gumbo
一开始我也是这么想的,但我们想保留奇数行,不是吗? :) 顺便说一下,恭喜你当选为版主 - 我刚刚注意到了钻石标志(而且我投了你的票 ;)) - Tim Pietzcker

2

首先,我完全同意这不是正则表达式应该做的事情的共识。

以下是Java演示:

public class Test {

    public static String voodoo(String lines) {
        return lines.replaceAll("\\G(.*\r?\n).*(?:\r?\n|$)", "$1");
    }

    public static void main(String[] args) {
        System.out.println("a)\n"+voodoo("1\n2\n3\n4\n5\n6"));
        System.out.println("b)\n"+voodoo("1\r\n2\n3\r\n4\n5\n6\n7"));
        System.out.println("c)\n"+voodoo("1"));
    }
}

输出:

a)
1
3
5

b)
1
3
5
7

c)
1

正则表达式的简要解释:

\G       # match the end of the previous match
(        # start capture group 1
  .*     #   match any character except line breaks and repeat it zero or more times
  \r?    #   match the character '\r' and match it once or none at all
  \n     #   match the character '\n'
)        # end capture group 1
.*       # match any character except line breaks and repeat it zero or more times
(?:      # start non-capture group 1 
  \r?    #   match the character '\r' and match it once or none at all
  \n     #   match the character '\n'
  |      #   OR
  $      #   match the end of the input
)        # end non-capture group 1

\G从字符串的开头开始。每对行(第二行是可选的,以防最后不均匀的行)都会用该对中的第一行替换。

但再次强调:使用普通编程语言(如果可以称之为“正常”:)是正确的方法。

编辑

正如Tim所建议的那样,这也可以工作:

replaceAll("(?m)^(.*)\r?\n.*", "$1")

1
String result = subject.replaceAll("(?m)^(.*)\r?\n.*", "$1"); 不应该也能起到同样的作用吗?在匹配之后,正则表达式引擎会自动到达下一行的开头。 - Tim Pietzcker
当然可以!这在我经常遇到的情况下是很常见的:我总是试图用一种过于复杂的方式来解决问题! - Bart Kiers

2

我在Sublime Text的“正则表达式查找替换”模式下使用捕获组(.*) --> $1,以删除每隔一行的换行符,并在值之间放置一个制表符。

replace (.*)\n(.*)\n
with $1\t$2\n

对于这个具体的问题,问答者可以将其更改为:
replace (.*)\n(.*)\n
with $1\n

0

这个程序将从文本文件中删除偶数行:

grep '[13579]$' textfile > textfilewithoddlines

并输出以下内容:

行1

行3

行5


1
是的,无论数字有多大,它是否为奇数只由最后一位数字($)决定,该数字必须是以下5个数字之一。 - bart
我所说的不可扩展是指数据可能不是字面上的“line1”,“line2”。它可以是任何东西。因此,搜索以数字结尾的模式并不可扩展。 - ghostdog74
1
它非常具有可扩展性。Ghostdog 的意思是它不是“通用”的。不幸的是,规范(问题)没有说明需要什么样的通用性,因此我们只能猜测。 - Jay Bazuzi
@ghostdog74:正如OP所说,他想使用正则表达式,我认为他想要以奇数/偶数数字结尾的行。否则,就像许多人所说的那样,不会使用正则表达式。sed -n '2,$n;p' textfile可能更适合。 - emil

0
也许你正在使用命令行。在 PowerShell 中: $x = 0; gc .\foo.txt | ? { $x++; $x % 2 -eq 0 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接