如何从文件中删除所有非标准字符?

时间:2014-10-13 23:12:52

标签: python

几个星期前我在bash中遇到了这个same problem,但现在我想在python中找到解决方案。

我的输入如下:

^MCopying non-tried blocks... Pass 1 (forwards)^M^[[A^[[A^[[Arescued:         0 B,  errsize:       0 B,  current rate:        0 B/s
   ipos:         0 B,   errors:       0,    average rate:        0 B/s
   opos:         0 B, run time:       1 s,  successful read:       1 s ago
^MFinished

我想删除每个^M控制字符和每个^[[A序列,以实现以下所需的输出;

rescued:         0 B,  errsize:       0 B,  current rate:        0 B/s
   ipos:         0 B,   errors:       0,    average rate:        0 B/s
   opos:         0 B, run time:       1 s,  successful read:       1 s ago
Finished

到目前为止,我已尝试过:

def main(input=None):
    f = open(os.path.abspath(input),'r')
    file = f.read()
    f.close()
    filter(lambda x: x in string.printable, file)
    open('output', 'w').write(file)

但执行cat -v仍会显示所有非标准字符。

使用itertools.ifilter会产生相同的结果。

3 个答案:

答案 0 :(得分:1)

如果你要做的是删除回车(^M'\r'用Python术语)并完成ANSI or VT100 or whatever-you-have control sequences,过滤string.printable不会做你想要什么。 (你也做错了,正如Warren Weckesser's answer解释的那样 - filter不会就地修改字符串,它会返回一个新的字符串 - 并且会使它过度复杂化,但鉴于它不是正确的逻辑,谁在乎?)


如果查看string.printable,您会看到它包含回车符:

>>> '\r' in string.printable
True

因此,剥离不可打印的字符不会删除回车。


如果你看一下你的控制序列是什么样的,比如^[[A(在Python术语中是'\x1b[A'),它们以Escape字符开头,然后是一系列可打印的字符:

>>> [c.isprintable() for c in '\x1b[A']
[False, True, True]

因此,当您删除不可打印的字符时,将会远离转义字符,留下[A

因此,您需要编写或查找一些解析控制序列的代码,以便您可以检测它们并将其删除。这意味着您需要知道您尝试检测和删除的控制序列类型。

IIRC,VT100和过时的ANSI X3.64的规则非常简单,如下所示:

  • 逃脱(^[,又名\x1b
  • 可选[,后跟一系列“私有”字符,后跟零个或多个以分号分隔的整数序列,后跟零个或多个“中间”字节(来自ASCII 32-47)...我认为可能更容易匹配为[后跟ASCII 32-63中的任何字符串,除了58,而不是试图完全正确。
  • “命令”(来自ASCII 64-126)。

因此,像r'\x1b\[[ -9;-?]*[@-~]'这样的正则表达式应该处理它。但是,由于我不知道您的数据是VT100,ANSI X3.64,还是“当我运行某些程序时发生在术语中的任何事情”,我无法告诉您这是否适合您。我可以告诉你的是,这条规则适用于你给出的一个例子,^[[A

答案 1 :(得分:1)

如果您实际上并未尝试删除所有控制序列,只是来自该特定输入的特定^M^[[A序列,您可以通过两种更简单的方式执行此操作。

首先,只需替换这些序列:

text = text.replace('\r', '').replace('\x1b[A', '')

或者,第二个 - 这似乎更复杂,但它可以让你照顾你还没有达到的另一部分(删除前两个^M之间的所有可打印的东西) - 你可以只是在“获救”之前移除所有内容,然后在“完成”之前删除角色:

# partition on the first 'rescued', drop the prefix, re-join the rest
text = ''.join(text.partition('rescued')[1:])
# partition on the last 'Finished', drop the last char of the prefix, re-join
bits = text.partition('Finished')
text = ''.join(bits[0][:-1], bits[1], bits[2])

或者,使用正则表达式:

text = ''.join(re.search(r'(rescued.*?)\r(Finished.*)', text, re.DOTALL).groups())

(rescued.*?)匹配从rescued到最后但不包括下一个\r的所有内容,然后(Finished.*)匹配从Finished到结尾的所有内容(我不确定这是什么,或者换行);将这两个捕获组合在一起,你就得到了你想要的东西。

答案 2 :(得分:-1)

您必须在变量中获取filter结果。

无论如何,我会使用简单的RegEx方法。

import re, os

with open(os.path.abspath(input), 'r') as f:
    match = re.search("rescued:.*Finished", f.read(), re.MULTILINE|re.DOTALL)
    if match:
        data = match.group(0).replace("^M","")
        open('output', 'w').write(data)