Python:re.compile和re.sub

时间:2013-08-27 04:43:19

标签: python compilation compare

问题第1部分

我收到了这个文件f1:

<something @37>
<name>George Washington</name> 
<a23c>Joe Taylor</a23c>
</something @37>

我想重新编译它看起来像这个f1 :(带空格)

George Washington Joe Taylor

我试过这段代码,但有点删除了所有内容:

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)

我的猜测是re.compile行,但我不太清楚如何处理它。我不应该使用第三方扩展。有什么想法吗?

问题第2部分

我有一个不同的问题,比较我从Alfe得到这个代码的2个文件:

from collections import Counter

def test():
    with open('f1.txt') as f:
        contentsI = f.read()
    with open('f2.txt') as f:
        contentsO = f.read()

    tokensI = Counter(value for value in contentsI.split()
                        if value not in [])
    tokensO = Counter(value for value in contentsO.split()
                        if value not in [])
    return not (tokensI - tokensO) and not (set(tokensO) - set(tokensI))

是否可以在'if value not in []'部分中实现re.compile和re.sub?

4 个答案:

答案 0 :(得分:22)

我将解释您的代码会发生什么:

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 string 类型的对象 text
请注意,我使用粗体字符文字来表达对象,并使用text来表示此对象的名称== IDENTIFIER。
作为指令for unwanted in text:的结果,标识符unwanted被连续分配给 text 对象引用的每个字符。

此外,re.compile('<.*>')创建了一个RegexObject类型的对象(我个人称之为编译的)正则表达式或只是正则表达式,{{1}只是正则表达式) 您将此已编译的正则表达式对象分配给标识符<.*>:这是一个非常糟糕的做法,因为match已经是正则表达式对象的方法的名称,并且是您创建的正则对象的名称特别是,那么你可以毫无错误地写match match.match也是 re 模块功能的名称 使用此名称满足您的特殊需求非常令人困惑。你必须避免这种情况。

使用match作为文件f1的文件处理程序的名称存在同样的缺陷。 file已经是语言中使用的标识符,您必须避免使用它。

好。现在定义了这个命名不佳的匹配对象,指令file将替换文本中正则表达式匹配找到的所有内容替换fixed_doc = match.sub(r' ',text)
请注意,编写r' '而不仅仅是r' '完全是多余的,因为' '中绝对没有需要转义的内容。每当他们必须在正则表达式问题中写一个字符串时,一些焦虑的人都会写一些原始字符串。

由于其符号格式' ',其中点符号表示&#34;贪婪地吃掉位于<.+><之间的每个字符,除非它是换行符#34 ; ,匹配在文本中捕获的每个行都是最后一行>。 由于名称>没有出现在此指令中,因此对于文本的每个字符,它是一个接一个地执行的操作。也就是说:没什么有趣的 要分析程序的执行,您应该在代码中添加一些打印指令,以便了解发生的情况。例如,如果您执行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 的方法sub()调用函数r来执行替换。
如果匹配带有开始标记(必须在文本中通过其对应的结束标记,通过构造正则表达式),则ripl()将返回ripl()
如果匹配带有结束标记,则''仅在检测到文本中的此结束标记与先前开始标记的对应结束标记时才返回ripl()。每次检测到开始标记并进行匹配时,都可以通过在列表 li 中记录每个相应结束标记的范围来实现这一点。

录制列表 li 被定义为默认参数,以便它始终与每次调用函数''时使用的列表相同(请参阅对undertsand的默认参数的功能,因为它是微妙的。) 由于将ripl()定义为接收默认参数的参数,列表对象 li 将保留在分析多个文本时记录的所有跨度,以防连续分析多个文本。为了避免列表 li 保留过去文本匹配的跨度,有必要将列表设置为空。我编写了函数,以便使用默认参数li定义第一个参数:在正则表达式None方法中使用它之前,允许在没有参数的情况下调用ripl()
然后,必须考虑在使用它之前写sub()

如果要删除文本的换行符以获得您在问题中显示的精确结果,则必须将代码修改为:

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)

您可以使用Beautiful Soup轻松完成此操作:

from bs4 import BeautifulSoup
file = open('f1.txt')
fixed = open('fnew.txt','w')

#now for some soup

soup = BeautifulSoup(file)

fixed.write(str(soup.get_text()).replace('\n',' '))

上述行的输出将为:

George Washington Joe Taylor

(Atleast这适用于你给我的样本)

对不起,我不明白第2部分,祝你好运!

答案 2 :(得分:0)

想出第一部分是缺少'?'

match = re.compile('<.*?>')

诀窍。


无论如何仍然不确定第二个问题。 :/

答案 3 :(得分:0)

对于第1部分,请尝试以下代码段。但是请考虑使用像Moe Jan

建议的像beautifulsoup这样的库
import re
import os
def main():
    f = open('sample_file.txt')
    fixed = open('fnew.txt','w')



    #pattern = re.compile(r'(?P<start_tag>\<.+?\>)(?P<content>.*?)(?P<end_tag>\</.+?\>)')
    pattern = re.compile(r'(?P<start><.+?>)(?P<content>.*?)(</.+?>)')
    output_text = []
    for text in f:
        match = pattern.match(text)
        if match is not None:
            output_text.append(match.group('content'))

    fixed_content = ' '.join(output_text)


    fixed.write(fixed_content)
    f.close()
    fixed.close()

if __name__ == '__main__':
    main()

第2部分

我对你的要求并不完全清楚 - 不过我的猜测是你想做if re.sub(value) not in []之类的事情。但请注意,在初始化re.compile实例之前,您只需要调用Counter一次。如果你澄清问题的第二部分会更好。

实际上,我建议您使用内置的Python diff模块来查找两个文件之间的区别。使用这种方法比使用自己的diff算法更好,因为diff逻辑经过了很好的测试和广泛使用,并且不容易受到虚假换行符,制表符和空格字符的影响而导致的逻辑或编程错误。