如何在Python中去除类似'\xe2'或'\x0c'的转义序列

9 投票
4 回答
15153 浏览
提问于 2025-04-17 16:18

我正在做一个项目(基于内容的搜索),为此我在Ubuntu上使用'pdftotext'这个命令行工具,它可以把PDF里的所有文字写到一个文本文件里。不过,它也会把项目符号写进去,现在当我读取这个文件来给每个单词建立索引时,也会把一些转义序列(比如'\x01')索引进去。我知道这是因为那些项目符号(•)。

我只想要纯文本,所以有没有办法去掉这些转义序列。我做了一些类似这样的操作:

escape_char = re.compile('\+x[0123456789abcdef]*')
re.sub(escape_char, " ", string)

但是这样并没有去掉转义序列。

提前谢谢你们。

4 个回答

1

如果你在处理8位字符值,可以不使用正则表达式,而是提前建立一些简单的表格,然后结合使用str.translate()方法,这样就能非常快速和简单地去掉字符串中不需要的字符。

import random
import string

allords = [i for i in xrange(256)]
allchars = ''.join(chr(i) for i in allords)
printableords = [ord(ch) for ch in string.printable]
deletechars = ''.join(chr(i) for i in xrange(256) if i not in printableords)

test = ''.join(chr(random.choice(allords)) for _ in xrange(10, 40)) # random string
print test.translate(allchars, deletechars)
4

你遇到的主要问题是反斜杠(\)比较复杂。在字符串中,反斜杠可能会被特殊处理;比如说 \t 会变成一个制表符(Tab)。而 \+ 在字符串中并没有特殊含义,所以字符串的内容其实是你预期的那样。接着,正则表达式的编译器看到 \+ 时,它会把它当作普通的 + 字符。通常情况下,+ 是有特殊含义的(表示“前面模式的一个或多个实例”),而反斜杠则是用来取消这种特殊含义的。

解决这个问题的方法就是把反斜杠写成两个,这样就能匹配一个单独的反斜杠。

我把模式放在 r'' 中,这样就变成了“原始字符串”,在这种情况下,Python 会把反斜杠当作普通字符处理。如果不这样做,Python 的字符串解析器会把两个反斜杠变成一个;就像 \t 变成制表符一样,\\ 也会变成一个反斜杠。所以,使用原始字符串可以确保正则表达式编译器看到你想要的内容。

另外,一个更好的模式是:先是反斜杠,然后是一个 x,接着是一个或多个匹配十六进制字符的字符类。我把模式改成了这个。

import re

s = r'+\x01+'
escape_char = re.compile(r'\\x[0123456789abcdef]+')
s = re.sub(escape_char, " ", s)

如果不使用原始字符串,你也可以用普通字符串,但要非常小心反斜杠。在这种情况下,我们需要写四个反斜杠!字符串解析器会把每两个反斜杠变成一个,而我们希望正则表达式编译器看到两个反斜杠。其实用原始字符串更简单!

此外,你原来的模式会匹配零个或多个十六进制数字,而我的模式会匹配一个或多个。但我认为十六进制数字通常会有两个,或者在 Unicode 的情况下可能会有四个。你应该弄清楚可能有多少个,并写出一个确保这个数量的模式。下面是一个匹配 2、3 或 4 个十六进制数字的模式:

escape_char = re.compile(r'\\x[0123456789abcdef]{2,4}')

还有一个模式是匹配恰好两个或恰好四个。我们需要用竖线来表示两个选择,并且需要用括号来分组。我在这里使用了一个不匹配的分组,写成 (?:pattern),而不是简单的 (pattern)(这里的 pattern 是指模式,而不是字面上的“pattern”这个词)。

escape_char = re.compile(r'\\x(?:[0123456789abcdef]{2,2}|[0123456789abcdef]{4,4})')

这里有个示例代码。这个序列后面紧跟着一个 1 字符,而这个模式会把它保留下来。

import re

s = r'+\x011+'
pat = re.compile(r'\\x(?:[0123456789abcdef]{2,2}|[0123456789abcdef]{4,4})')
s = pat.sub("@", s)
print("Result: '%s'" % s)

这段代码的输出是:Result: '+@1+'

注意:以上内容是基于你确实想要匹配一个反斜杠字符后面跟着十六进制字符的前提。如果你实际上是想匹配可能是“可打印”字符的字节值,那么请使用 @nneonneo 的答案,而不是这个。

13

问题在于 \xXX 只是一个控制字符的表示方式,而不是这个字符本身。所以,如果你想要直接匹配 \x,你必须在处理字符串的 repr(表示形式)时才能做到。

你可以使用字符类来去掉不可打印的字符:

re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\xff]', '', text)

举个例子:

>>> re.sub(r'[\x00-\x1f\x7f-\xff]', '', ''.join(map(chr, range(256))))
' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'

撰写回答