使用Python 3.x统计匹配特定正则表达式模式的行数
我有一个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 个回答
“非标记”行可以理解为那些既不是普通行,也不以 ~
开头,或者不以 &
开头的行。
所以下面这个正则表达式可以用:
^[^&\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)
这是我的回答。我还不太确定我处理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)
你可以试试这个模式 ^\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
这样,这个正则表达式就能匹配到“没有标记”的非空行,正好符合要求。
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))
这个代码会计算所有行的数量,但不包括空行。所以空白行不会被算在内。希望这对你有帮助。