【问题标题】:Removing symbols from a large unicode text file从大型 unicode 文本文件中删除符号
【发布时间】:2014-11-21 13:33:04
【问题描述】:

我有一个文本文件,其中包含大小约为 2GB 的 Unicode 文本。我尝试使用以下代码删除所有符号

import re
symbols = re.compile(r'[{} &+( )" =!.?.:.. / |  » © : >< #  «  ,] 1 2 3 4 5 6 7 8 9 _ - + ; [ ]  %',flags=re.UNICODE)

with open('/home/corpus/All12.txt','a') as t:
    with open('/home/corpus/All11.txt', 'r') as n:
        data = n.readline()          
        data = symbols.sub(" ", data)          
        t.write(data)

一个用于测试代码的小文件:

:621   

"

    :621       "
    :621               :1                ;"
     _            "         :594            :25   4   8   0        :23          "സര്‍ക്കാര്‍ജീവനക്കാരുടെ ശമ്പളം അറിയാന്‍ ഭാര്യമാര്‍ക്ക് അവകാശമുണ്ട്വിവരാവകാശകമ്മീഷന്‍    
    :621            :4   0   3   0  ;"
     _           "         :551             :16        :3  " 

     :12     :70                ;"                  "             "     =""                   "               "     =""                     "            "     ="" +    


     _                       "         :541             :26       :30   45   5   35  " 
 ='                  'ന്യൂഡല്‍ഹി: സര്‍ക്കാര്‍ജീവനക്കാരായ ഭര്‍ത്താക്കന്മാരുടെ ശമ്പളം 

愿望输出是ന്യൂഡല്‍ഹി സര്‍ക്കാര്‍ജീവനക്കാരായ ഭര്‍ത്താക്കന്മാരുടെ ശമ്പളം。 代码不起作用。它停止了我的电脑。

我可以不用正则表达式来解决这个问题吗?

【问题讨论】:

  • 您确定代码不起作用吗?也许只是需要很长时间,这可能是因为您正在阅读一个 2GB 的文本文件。尝试在循环中添加print。
  • 尝试分块读取文件stackoverflow.com/questions/519633/…
  • @DanielGibbs 我敢肯定。跑了一段时间后就安静了。这里没有循环
  • 我建议你制作一个小样本文件来测试,否则你会发现很难隔离问题。考虑将脚本拆分为三个函数(读入数据、处理数据、写出数据),以便您可以单独测试每个函数。
  • 您的 2 GiB 文件中有多少行?如果只有几个(或一个......),一次迭代一行对您没有多大帮助。

标签: python regex python-3.x unicode


【解决方案1】:

您需要在方括号[] 中插入要替换的每个符号,转义一些特殊符号,例如[] 本身,单引号' 和\。正则表达式是r'[-0-9{}&amp;+()"=!.?:/|»©&gt;&lt;#«,_+;%\[\]@$*\'\\^~\n\t]'。

演示:

>>> st='1234567890-=[]\;,./\'!@#$%^&*()_+{}|":<>?//.,`~ajshgasd'
>>> print re.sub(r'[-0-9{}&+()"=!.?:/|»©><#«,_+;%\[\]@$*\'\\^`~\n\t]','',st)
ajshgasd

存档:

>>> fp=open('file.txt','r')    
>>> for line in fp:
...     if line.strip() == '': continue  # strip() removes leading and trailing spaces
...     print re.sub(r'[-0-9{}&+()"=!.?:/|»©><#«,_+;%\[\]@$*\'\\^`~]','',line).strip(),
... 
    ന്യൂഡല്‍ഹി സര്‍ക്കാര്‍ജീവനക്കാരായ ഭര്‍ത്താക്കന്മാരുടെ ശമ്പളം

要将输出写入文件,请使用以下代码:

of=open('outfile.txt','w')
fp=open('file.txt','r')
for line in fp:
    if line.strip() == '': continue  # strip() removes leading and trailing spaces
    rline = re.sub(r'[-0-9{}&+()"=!.?:/|»©><#«,_+;%\[\]@$*\'\\^`~]','',line).strip()
    if rline == '': continue # skip empty lines
    of.write(rline+'\n')

of.close()
fp.close()

【讨论】:

  • 这段代码删除了符号,但使用 fp.read() 我们试图将整个文件加载到内存中,这对我来说不实用
  • @karu, fp.read() 只是为了向您展示它适用于您提供的示例。您不必将整个文件加载到内存中。您需要使用 fp=open('file.txt','r') 打开文件,然后使用 for line in fp: ... 逐行遍历文件,并在每一行应用 regex。这不会将整个文件加载到内存中,而是一次加载一行。忘记在我的代码中打印fp.read() 和fp.seek(0) 部分并使用其他三个代码行。这对你有用。
  • 好的。让我试试如果文件中有空行会发生什么
  • 您可以通过if line.strip()=='': continue这个条件跳过空行。我将在代码中更新它。
  • 代码正在运行,如何在不删除空间的情况下将其写入文件
【解决方案2】:

str.translate 可以用来代替re.sub。它采用 Unicode 序数到替换对的映射并返回翻译后的字符串。如果替换为None,它将删除字符。 str.maketrans 可用于生成映射。

在 Python 3 中,还记得指定文件的编码。我使用 UTF-8 进行测试:

#!python3
#coding: utf8
symbols = ' {}&+()"=!.?.:../|»©:><#«,123456789_-+;[]%'
D = str.maketrans('','',symbols)
with open('All12.txt','a',encoding='utf8') as t, open('All11.txt','r',encoding='utf8') as n:
    for line in n:
        t.write(line.translate(D))

只需在symbols 中列出您要删除的任何符号。

或者,您可以按字符块读取文件,这比单独读取超过 1000 万行更有效。读取文件,例如,20+ 100MB 块。

#!python3
#coding: utf8
symbols = ' {}&+()"=!.?.:../|»©:><#«,123456789_-+;[]%'
D = str.maketrans('','',symbols)
with open('All12.txt','a',encoding='utf8') as t, open('All11.txt','r',encoding='utf8') as n:
    while True:
        block = n.read(100*1024*1024)
        if not block:
            break
        t.write(block.translate(D))

参考:str.translate,str.maketrans

【讨论】:

  • 使用正则表达式哪个更快?
  • 可能是这个。自己计时!
【解决方案3】:

第一个 [ 和 ] 之间的符号列表后面的 re 对我来说毫无意义。它不会删除符号,但只会删除一个符号,后跟 '1 2 3 4 5 6 7 8 9 _ - + ; []%'。在其他工作中,re.sub 不会做任何事情。但无论如何,你的代码运行在 3.4.2、Win7 上。

import re
symbols = re.compile(r'[{} &+( )" =!.?.:.. / |  » © : >< #  «  ,]'
                     '1 2 3 4 5 6 7 8 9 _ - + ; [ ]  %',flags=re.UNICODE)
text = ('''" :621 " :621 :1 ;" _ " :594 :25 4 8 0 :23'''
        '''"സര്‍ക്കാര്‍ജീവനക്കാരുടെ ശമ്പളം അറിയാന്‍'''
        '''ഭാര്യമാര്‍ക്ക് അവകാശമുണ്ട്വിവരാവകാശകമ്മീഷന്‍\n'''
        ''':621 :4 0 3 0 ;" _ " :551 :16 :3 " ''')
data = symbols.sub(" ", text)          
print(data == text)  # True

PS。 with 语句可以有多个子句(以节省缩进级别)。

with open('/home/corpus/All12.txt','a') as t,\
     open('/home/corpus/All11.txt', 'r') as n:

【讨论】:

    【解决方案4】:
    [{} &+( )" =!.?.:.. / |  » © : >< #  «  , 1 2 3 4 5 6 7 8 9 _ - + ; \[ \]  %]
    

    试试这个。替换为empty string。查看演示。

    http://regex101.com/r/oE6jJ1/18

    import re
    p = re.compile(ur'[{} &+( )" =!.?.:.. / | » © : >< # « , 1 2 3 4 5 6 7 8 9 _ - + ; \[ \] %]', re.IGNORECASE | re.UNICODE)
    test_str = u" :621 \" :621 :1 ;\" _ \" :594 :25 4 8 0 :23 \"സര്‍ക്കാര്‍ജീവനക്കാരുടെ ശമ്പളം അറിയാന്‍ ഭാര്യമാര്‍ക്ക് അവകാശമുണ്ട്വിവരാവകാശകമ്മീഷന്‍\n:621 :4 0 3 0 ;\" _ \" :551 :16 :3"
    subst = u""
    
    result = re.sub(p, subst, test_str)
    

    【讨论】:

      【解决方案5】:

      没有正则表达式的解决方案:

      您可以使用地图功能以及您要删除的一组符号来完成此操作。

      def removeSymbols(text,symbols):
          return "".join(map(lambda x: "" if x in symbols else x,text))
      
      >>> string = '''" :621 \" :621 :1 ;\" _ \" :594 :25 4 8 0 :23 \"സര്‍ക്കാര്‍ജീവനക്കാരുടെ ശമ്പളം അറിയാന്‍ ഭാര്യമാരക് അവകാശമുണ്ട്വിവരാവകാശകമ്മീഷന്‍\n:621 :4 0 3 0 ;\" _ \" :551 :16 :3"'''    
      
      >>> symbols = set('[{} &+( )" =!.?.:.. / |  » © : >< #  «  ,] 1 2 3 4 5 6 7 8 9 _ - + ; [ ]  %')
      
      >>> cleanString = removeSymbols(string,symbols)
      
      >>> print(cleanString)
      
      '" :621 " :621 :1 ;" _ " :594 :25 4 8 0 :23 "സര്\u200dക്കാര്\u200dജീവനക്കാരുടെ ശമ്പളം അറിയാന്\u200d ഭാര്യമാര്\u200dക്ക് അവകാശമുണ്ട്വിവരാവകാശകമ്മീഷന്\u200d\n:621 :4 0 3 0 ;" _ " :551 :16 :3"'
      

      【讨论】:

        【解决方案6】:

        我认为您的正则表达式不正确,因为您可以简化它。 例如,子表达式[{} &amp;+( )" =!.?.:.. / | » © : &gt;&lt; # « ,] 可以简化 在[ !"#&amp;()+,./:&lt;=&gt;?{|}©«»]:每个字符只保留一次。 这是因为[] 用于表示一组字符。 查看 Python 文档中的“正则表达式操作”一章。 见:https://docs.python.org/3.4/library/re.html

        在您的邮件标题中,您写道:“从大型 unicode 文本文件中删除符号”, 所以我认为您有一组要从文件中删除的字符。

        为了简化您的符号集,您可以尝试:

        >>> symbols = "".join(frozenset(r'[{} &+( )" =!.?.:.. / |  » © : >< #  «  ,] 1 2 3 4 5 6 7 8 9 _ - + ; [ ]  %'))
        >>> print(symbols)
        ! #"%&)(+-,/.132547698»:=<?>[];_|©{}«
        

        这样你就可以简单地写了:

        symbols = '! #"%&)(+-,/.132547698»:=<?>[];_|©{}«'
        

        读者注意:这并不明显,但这里的所有字符串都是 unicode 字符串。 我认为,作者使用 Python 3。 对于 Python 2.7 用户,最好的方法是使用“utf8”编码和u"" 语法,这样:

        # -*- coding: utf8 -*-
        symbols = u'! #"%&)(+-,/.132547698»:=<?>[];_|©{}«'
        

        或者,您可以导入 unicode_literals,并删除“u”前缀:

        # -*- coding: utf8 -*-
        from __future__ import unicode_literals
        symbols = '! #"%&)(+-,/.132547698»:=<?>[];_|©{}«'
        

        如果你想写一个匹配一个符号的正则表达式,你必须对字符进行转义 具有特殊含义(例如:“[”应在“\[”中转义)。 最好的方法是使用re.escape函数。

        >>> import re
        >>> symbols = '! #"%&)(+-,/.132547698»:=<?>[];_|©{}«'
        >>> regex = "[{0}]".format(re.escape(symbols))
        >>> print(regex)
        [\!\ \#\"\%\&\)\(\+\-\,\/\.132547698\»\:\=\<\?\>\[\]\;\_\|\©\{\}\«]
        

        试一试:

        import re
        
        symbols = '! #"%&)(+-,/.132547698»:=<?>[];_|©{}«'
        regex = "[{0}]+".format(re.escape(symbols))
        
        example = '''" :621 " :621 :1 ;" _ " :594 :25 4 8 0 :23 "സര്‍ക്കാര്‍ജീവനക്കാരുടെ ശമ്പളം അറിയാന്‍ ഭാര്യമാര്‍ക്ക് അവകാശമുണ്ട്വിവരാവകാശകമ്മീഷന്‍
        :621 :4 0 3 0 ;" _ " :551 :16 :3 "'''
        
        print(re.sub(regex, "", example, re.UNICODE))
        

        注意,零不在符号集中,但空格在,所以结果将是:

        '''0സര്‍ക്കാര്‍ജീവനക്കാരുടെശമ്പളംഅറിയാന്‍ഭാര്യമാര്‍ക്ക്അവകാശമുണ്ട്വിവരാവകാശകമ്മീഷന്‍
        00'''
        

        我认为正确的符号集是:!#"%&amp;)(+-,/.0132547698»:=&lt;?&gt;[];_|©{}«。 然后您可以剥离每一行以删除尾随空格...

        所以这段代码 sn-p 应该适合你:

        import re
        
        symbols = '!#"%&)(+-,/.0132547698»:=<?>[];_|©{}«'
        regex = "[{0}]+".format(re.escape(symbols))
        sub_symbols = re.compile(regex, re.UNICODE).sub
        
        with open('/home/corpus/All12.txt', 'a') as t:
            with open('/home/corpus/All11.txt', 'r') as n:
                data = n.readline()
                data = sub_symbols("", data).strip()
                t.write(data)
        

        【讨论】:

        【解决方案7】:

        您是否考虑过解码 unicode,例如:

        line = line.decode('utf_8')
        

        然后重新编码让我们说...... ascii,同时忽略它不知道的字符,例如:

        line = line.encode('ascii', 'ignore')
        

        不确定这是否更快或更好。正则表达式很慢,但我凭经验不知道这更好。不过这很容易;)

        可能复杂度为 O(2n)(组合),但长正则表达式可能同样糟糕。

        更新:这是错误的,如下所述。

        【讨论】:

        • 这行不通。它将忽略所有不是所需输出的非 ascii 字符。
        • 我们需要正好相反。
        • 抱歉,我一定是看错了。您可以考虑做同样的事情,但编码为仅包含这些字符的编码。考虑到它们显然是一种特定的外语,这似乎很有可能。
        猜你喜欢
        • 2011-08-23
        • 2019-01-17
        • 2014-09-10
        • 2016-09-01
        • 2018-09-03
        • 2020-06-21
        • 1970-01-01
        • 1970-01-01
        • 2018-03-11
        相关资源
        最近更新 更多