使用Python 3.x统计匹配特定正则表达式模式的行数

1 投票
4 回答
2502 浏览
提问于 2025-04-18 14:12

我有一个UTF8格式的源文件(没有BOM,使用Windows换行符),内容大致如下:

~someunicodetext_someunicodetext_someunicodetext~
some_more_unicode_text_some_more_unicode_text

~someunicodetext_someunicodetext_someunicodetext~
some_more_unicode_text_some_more_unicode_text
&&even_more_text_here

~someunicodetext_someunicodetext_someunicodetext~
some_more_unicode_text_some_more_unicode_text

~someunicodetext_someunicodetext_someunicodetext~

这个文件里有三种类型的行(如果算上空行就是四种)。我的目标是用Python的正则表达式来统计每种非空类型的行数。我必须使用基于正则表达式的解决方案,并且要用Python 3.x,因为我想理解它是怎么工作的。

我的Python脚本大概是这样的:

import re, codecs
pattern = re.compile(r'some_expression_here')
count = 0
with codecs.open("some_input_file", "r", "UTF8") as inputFile:
    inputFile=inputFile.read()
    lines = re.findall(pattern, inputFile)
    for match in lines:
        count +=1
print (count)

我遇到的真正问题是正则表达式的写法。
~.*~似乎可以匹配我上面例子中的第1、4、8行(如果从1开始算的话)。
&&.*可以匹配第6行。
但是我不知道怎么去统计那些没有标记的行,也就是第2、5、9行。
在Notepad++中,这个表达式^(?!(~.*~)|(&&.*)).*或者简单的^(?!~|&).*对我有效(虽然不完全正确),但我在Python中尝试复制这个却失败了……

编辑
inputFile.read()读取文件的方式和我预期的不一样(你好,Windows换行符)。这可能重要,也可能不重要。它的输出看起来是这样的:

~someunicodetext_someunicodetext_someunicodetext~

some_more_unicode_text_some_more_unicode_text



~someunicodetext_someunicodetext_someunicodetext~

some_more_unicode_text_some_more_unicode_text

&&even_more_text_here

4 个回答

0

“非标记”行可以理解为那些既不是普通行,也不以 ~ 开头,或者不以 & 开头的行。

所以下面这个正则表达式可以用:

^[^&\s].*

解释一下:^ 表示从行的开头开始匹配,[^...] 表示一个不在括号里的字符,&\s 表示字符 & 或者空白字符(也就是说,不是这两种),.* 表示后面可以跟任何内容。

(我加了 \s 是以防万一,因为你说你在处理换行时遇到问题。我不确定这是否必要)

另外,逐行读取文件会更好。你会得到:

import re, codecs
pattern = re.compile(r'^[^&\s].*')
with codecs.open("some_input_file", "r", "UTF8") as inputFile:
    count = sum( 1 for line in inputFile if re.search(pattern, line) )
print (count)
0

这是我的回答。我还不太确定我处理Windows换行符的方式是否正确,但这个方法似乎有效。

我其实希望有人能解释一下我的问题出在哪里,以及为什么它会这样工作,不过算了。

这个方法的作用是,我们匹配每一行,这些行前面有~EOL标记,并且以另一个EOL结束。同时,我们确保不匹配那些有两个或更多连续EOL的行。

所以,这个方法只匹配那些紧挨着带有~标记的行的下一行。

import re, codecs

regex = re.compile(r'(?!~(\r\n){2,})~\r\n.*\r\n', re.MULTILINE)
count = 0

with codecs.open('input_file', 'r', 'UTF8') as inputFile:
    inputFile=inputFile.read()
    lines = re.findall(regex, inputFile)
    for match in lines:
        count +=1
print (count)
0

你可以试试这个模式 ^\w.*,并加上 `re.MULTILINE` 这个标志。

在 Python 2 中,还应该使用 re.UNICODE 这个标志。

下面是一个完整的例子:

import re, codecs

with codecs.open("input.txt", "r", "UTF8") as inputFile:
    data = inputFile.read()
pattern = re.compile(r'^\w.*', flags=re.MULTILINE)
lines = re.findall(pattern, data)

>>> data   #  note windows line termination
'~someunicodetext_someunicodetext_someunicodetext~\r\nsome_more_unicode_text_some_more_unicode_text\r\n   \t\r\n~someunicodetext_someunicodetext_someunicodetext~\r\nsome_more_unicode_text_some_more_unicode_text\r\n&&even_more_text_here\r\n\r\n~someunicodetext_someunicodetext_someunicodetext~\r\nsome_more_unicode_text_some_more_unicode_text\r\n\r\n~someunicodetext_someunicodetext_someunicodetext~\r\n'

>>> print(lines)
['some_more_unicode_text_some_more_unicode_text\r', 'some_more_unicode_text_some_more_unicode_text\r', 'some_more_unicode_text_some_more_unicode_text\r']

>>> print(len(lines))
3

这样,这个正则表达式就能匹配到“没有标记”的非空行,正好符合要求。

1
    x="~someunicodetext_someunicodetext_someunicodetext~ \n   \n \nsome_more_unicode_text_some_more_unicode_text \n"
    pattern=re.compile(r"(\S+)")
    print len(pattern.findall(x))

这个代码会计算所有行的数量,但不包括空行。所以空白行不会被算在内。希望这对你有帮助。

撰写回答