我将解释你的代码会发生什么:
import re
file = open('f1.txt')
fixed = open('fnew.txt','w')
text = file.read()
match = re.compile('<.*>')
for unwanted in text:
fixed_doc = match.sub(r' ',text)
fixed.write(fixed_doc)
指令text = file.read() 创建一个名为text 的字符串 类型的文本 对象。
请注意,我使用粗体字符 text 来表示一个 OBJECT,并使用 text 来表示这个对象的名称 == IDENTIFIER。
作为指令for unwanted in text: 的结果,标识符unwanted 被连续分配给text 对象引用的每个字符。
此外,re.compile('<.*>') 创建了一个 RegexObject 类型的对象(我个人称其为 已编译)正则表达式或简称为 regex ,<.*> 只是 正则表达式模式)。
您将此编译的正则表达式对象分配给标识符match:这是一种非常糟糕的做法,因为match 通常已经是正则表达式对象的方法的名称,并且是您特别创建的方法的名称,因此您可以编写match.match 没有错误。
match 也是 re 模块的函数名称。
将此名称用于您的特殊需要是非常令人困惑的。你必须避免这种情况。
使用file 作为文件 f1 的文件处理程序的名称存在相同的缺陷。 file 已经是语言中使用的标识符,你必须避免它。
嗯。现在定义了这个名字不好的 match 对象,指令fixed_doc = match.sub(r' ',text) 将 text 中正则表达式 match 找到的所有出现替换为替换r' '.
请注意,写r' ' 而不仅仅是写' ' 是完全多余的,因为' ' 中绝对没有任何东西需要转义。一些焦虑的人在每次必须在正则表达式问题中编写字符串时都编写原始字符串是一种时尚。
由于它的模式<.+> 中的点符号表示“贪婪地吃掉位于< 和> 之间的每个字符,除非它是换行符”,因此match 是每一行,直到其中的最后一个 >。
由于名称unwanted 没有出现在此指令中,因此对文本的每个字符执行相同的操作,一个接一个。也就是说:没什么有趣的。
要分析程序的执行,您应该在代码中添加一些打印指令,以便了解发生了什么。例如,如果您执行print repr(fixed_doc),您将看到重复打印:' \n \n \n '。正如我所说:没什么有趣的。
您的代码中还有一个默认设置:您打开文件,但不关闭它们。关闭文件是强制性的,否则可能会发生一些奇怪的现象,在我意识到这种需要之前,我个人在我的一些代码中观察到了这种现象。有些人假装这不是强制性的,但这是错误的。
顺便说一句,打开和关闭文件的更好方法是使用with 语句。它可以完成所有工作,您无需担心。
.
所以,现在我可以为您提出第一个问题的代码:
import re
def ripl(mat=None,li = []):
if mat==None:
li[:] = []
return
if mat.group(1):
li.append(mat.span(2))
return ''
elif mat.span() in li:
return ''
else:
return mat.group()
r = re.compile('</[^>]+>'
'|'
'<([^>]+)>(?=.*?(</\\1>))',
re.DOTALL)
text = '''<something @37>
<name>George <wxc>Washington</name>
<a23c>Joe </zazaza>Taylor</a23c>
</something @37>'''
print '1------------------------------------1'
print text
print '2------------------------------------2'
ripl()
print r.sub(ripl,text)
print '3------------------------------------3'
结果
1------------------------------------1
<something @37>
<name>George <wxc>Washington</name>
<a23c>Joe </zazaza>Taylor</a23c>
</something @37>
2------------------------------------2
George <wxc>Washington
Joe </zazaza>Taylor
3------------------------------------3
原理如下:
当正则表达式检测到标签时,
- 如果是结束标签,则匹配
- 如果它是一个开始标签,它仅在文本中某处有相应的结束标签时才匹配
对于每个匹配,正则表达式r 的方法sub() 调用函数ripl() 来执行替换。
如果匹配带有一个开始标签(通过构造正则表达式,必须在文本中的某处加上相应的结束标签),然后ripl() 返回''。
如果匹配与结束标记,ripl() 仅当此结束标记先前在文本中被检测到是先前开始标记的相应结束标记时才返回 ''。这可以通过在列表中记录 li 每次检测到并匹配开始标签时每个相应结束标签的跨度的跨度。
录制列表li被定义为默认参数,以便在每次调用函数ripl()时使用的列表始终相同(请参阅默认参数的功能理解,因为它很微妙)。
由于将li定义为接收默认参数的参数,列表对象li将保留在分析多个文本时记录的所有跨度,以防多个文本被连续分析。为了避免列表 li 保留过去文本匹配的范围,有必要使列表为空。我编写了该函数,以便使用默认参数 None 定义第一个参数:允许在正则表达式的 sub() 方法中使用它之前调用 ripl() 而不使用参数。
然后,在使用它之前必须考虑写ripl()。
.
如果您想删除文本的换行符以获得您在问题中显示的精确结果,则必须将代码修改为:
import re
def ripl(mat=None,li = []):
if mat==None:
li[:] = []
return
if mat.group(1):
return ''
elif mat.group(2):
li.append(mat.span(3))
return ''
elif mat.span() in li:
return ''
else:
return mat.group()
r = re.compile('( *\n *)'
'|'
'</[^>]+>'
'|'
'<([^>]+)>(?=.*?(</\\2>)) *',
re.DOTALL)
text = '''<something @37>
<name>George <wxc>Washington</name>
<a23c>Joe </zazaza>Taylor</a23c>
</something @37>'''
print '1------------------------------------1'
print text
print '2------------------------------------2'
ripl()
print r.sub(ripl,text)
print '3------------------------------------3'
结果
1------------------------------------1
<something @37>
<name>George <wxc>Washington</name>
<a23c>Joe </zazaza>Taylor</a23c>
</something @37>
2------------------------------------2
George <wxc>WashingtonJoe </zazaza>Taylor
3------------------------------------3