如何仅过滤 Bash(linux)或 Python 文件中的可打印字符?

How to filter only printable characters in a file on Bash (linux) or Python?

我想制作一个包含不可打印字符的文件,使其只包含可打印字符。我认为这个问题与 ACSCII control action 有关,但我找不到解决方案,也无法理解以下文件中 .[16D(ASCII 控制操作字符??)的含义。

输入文件的十六进制转储:

00000000: 4845 4c4c 4f20 5448 4953 2049 5320 5448 HELLO THIS IS TH
00000010: 4520 5445 5354 1b5b 3136 4420 2020 2020 E TEST.[16D
00000020: 2020 2020 2020 2020 2020 201b 5b31 3644            .[16D
00000030: 2020

当我在 bashcat 编辑该文件时,我刚得到:"HELLO "。我认为这是因为默认 cat 解释了 ASCII 控制操作,两个 .[16D

为什么两个 .[16D 字符串生成 cat FILE 只是为了打印 "HELLO"?,以及...我怎样才能使该文件只包含可打印字符,即 "HELLO "?

您可以尝试使用此 sed 命令从文件中删除所有不可打印的字符:

sed -i.bak 's/[^[:print:]]//g' file

hexdump 显示 .[16D 中的点实际上是一个转义字符,\x1b.
Esc[nD是一个ANSI escape code删除n个字符。所以 Esc[16D 告诉终端删除 16 个字符,这解释了 cat 输出。

有多种方法可以从文件中删除 ANSI 转义码,使用 Bash 命令(例如使用 sed,如 Anubhava 的回答)或 Python.

但是,在这种情况下,最好通过终端仿真器 运行 文件来解释文件中任何现有的编辑控制序列,这样您就可以得到文件作者想要的结果应用了那些编辑序列。

在 Python 中执行此操作的一种方法是使用 pyte, a Python module that implements a simple VTXXX compatible terminal emulator. You can easily install it using pip, and here are its docs on readthedocs.

这是一个简单的演示程序,可以解释问题中给出的数据。它是为 Python 2 编写的,但很容易适应 Python 3。pyte 是 Unicode 感知的,它的标准 Stream class 需要 Unicode 字符串,但是这个例子使用一个 ByteStream,所以我可以向它传递一个纯字节字符串。

#!/usr/bin/env python

''' pyte VTxxx terminal emulator demo

    Interpret a byte string containing text and ANSI / VTxxx control sequences

    Code adapted from the demo script in the pyte tutorial at
    http://pyte.readthedocs.org/en/latest/tutorial.html#tutorial

    Posted to  

    Written by PM 2Ring 2015.06.02
'''

import pyte


#hex dump of data
#00000000  48 45 4c 4c 4f 20 54 48  49 53 20 49 53 20 54 48  |HELLO THIS IS TH|
#00000010  45 20 54 45 53 54 1b 5b  31 36 44 20 20 20 20 20  |E TEST.[16D     |
#00000020  20 20 20 20 20 20 20 20  20 20 20 1b 5b 31 36 44  |           .[16D|
#00000030  20 20                                             |  |

data = 'HELLO THIS IS THE TEST\x1b[16D                \x1b[16D  '

#Create a default sized screen that tracks changed lines
screen = pyte.DiffScreen(80, 24)
screen.dirty.clear()
stream = pyte.ByteStream()
stream.attach(screen)
stream.feed(data)

#Get index of last line containing text
last = max(screen.dirty)

#Gather lines, stripping trailing whitespace
lines = [screen.display[i].rstrip() for i in range(last + 1)]

print '\n'.join(lines)

输出

HELLO

输出的十六进制转储

00000000  48 45 4c 4c 4f 0a                                 |HELLO.|

我想到的简约解决方案是

import string
printable_string = filter(lambda x: x in string.printable, your_string)
## TODO: substitute your string in the place of "your_string"

如果这仍然无济于事,那么也尝试包括特定的 uni-code [curses.ascii]

查看内置 string 模块。

import string
printable_str = filter(string.printable, string)

对我来说,下面的命令效果很好,使用 strings 开箱即用

head /dev/random | strings -ws ''

详细说明:

head /dev/random : 不太重要,只是创建一些包含随机字符的行,包括可能会占用屏幕的非打印字符。

-w & -s strings 的选项:(man strings 的部分输出)

-w --include-all-whitespace By default tab and space characters are included in the strings that are displayed, but other whitespace characters, such a newlines and carriage returns, are not. The -w option changes this so that all whitespace characters are considered to be part of a string.

-s --output-separator By default, output strings are delimited by a new-line. This option allows you to supply any string to be used as the output record separator. Useful with --include-all-whitespace where strings may contain new-lines internally.

使用 -w-s 选项,通过 strings 传输的数据按原样处理,因此 strings -ws '' 打印可打印字符的序列。