4

结论:不可能覆盖或禁用 Python 的内置转义序列处理,因此,您可以跳过使用原始前缀说明符。我深入研究了 Python 的内部结构来解决这个问题。因此,如果有人尝试将在复杂字符串(如正则表达式)上工作的对象设计为某种框架的一部分,请确保在文档字符串中指定对象的字符串参数__init__() 必须包含r前缀!




原始问题:我发现强制 Python 不“更改”有关用户输入字符串的任何内容有点困难,其中可能包含正则表达式或转义的十六进制序列。我已经尝试过各种原始字符串组合.encode('string-escape')(及其解码对应项),但我找不到正确的方法。

给定文档 IPv6 地址的转义十六进制表示2001:0db8:85a3:0000:0000:8a2e:0370:7334,使用.encode()这个小脚本(称为x.py):

#!/usr/bin/env python

class foo(object):
    __slots__ = ("_bar",)
    def __init__(self, input):
        if input is not None:
            self._bar = input.encode('string-escape')
        else:
            self._bar = "qux?"

    def _get_bar(self): return self._bar
    bar = property(_get_bar)
#

x = foo("\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x8a\x2e\x03\x70\x73\x34")
print x.bar


执行时将产生以下输出:

$ ./x.py
 \x01\r\xb8\x85\xa3\x00\x00\x00\x00\x8a.\x03ps4


请注意已\x20转换为 ASCII 空格字符以及其他一些字符。这基本上是正确的,因为 Python 处理了转义的十六进制序列并将它们转换为可打印的 ASCII 值。


foo()如果将初始化程序视为原始字符串(并.encode()删除调用),则可以解决此问题,如下所示:

x = foo(r"\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x8a\x2e\x03\x70\x73\x34")


但是,我的最终目标是创建一种可以使用的框架,并且我想对最终用户隐藏这些“实现细节”。如果他们foo()以转义的十六进制形式(没有原始说明符)调用上述 IPv6 地址并立即将其打印回来,他们应该在不知道或使用原始说明符的情况下准确地返回他们输入的内容。所以我需要找到一种方法来让foo'__init__()做任何必要的处理来实现它。



编辑:根据这个 SO question,这似乎是 Python 的一个缺陷,因为它总是执行某种转义序列处理。似乎没有任何设施可以完全关闭转义序列处理,即使是暂时的。糟透了。我想我将不得不研究子类化str来创建类似的东西rawstr,智能地确定 Python 在字符串中处理的转义序列,并将它们转换回原来的格式。这不会很有趣...


Edit2:另一个例子,给定下面的示例正则表达式:

"^.{0}\xcb\x00\x71[\x00-\xff]"


如果我将其分配给 var 或将其传递给函数而不使用 raw 说明符,则将\x71转换为 letter q。即使我添加.encode('string-escape').replace('\\', '\\\\'),转义序列仍然被处理。因此导致此输出:

"^.{0}\xcb\x00q[\x00-\xff]"


我怎样才能在不使用原始说明符的情况下再次停止这种情况?是否有某种方法可以“关闭”转义序列处理或在事实q转回之后“恢复”它\x71?有没有办法在转义序列处理发生之前处理字符串并转义反斜杠?

4

2 回答 2

2

我认为您对 Python 字符串文字(源代码表示)、内存中的 Python 字符串对象以及如何打印这些对象(它们可以在输出中以什么格式表示)之间的区别有一个可以理解的混淆。

如果您将文件中的一些字节读入字节串,您可以按原样将它们写回。

r""仅存在于源代码中,在运行时没有这样的东西,即相等r"\x""\\x"它们甚至可能是内存中完全相同的字符串对象。

要查看输入没有损坏,您可以将每个字节打印为整数:

print " ".join(map(ord, raw_input("input something")))

或者只是按原样回显(可能存在差异,但与您的"string-escape"问题无关):

print raw_input("input something")

身份功能:

def identity(obj):
    return obj

如果您对字符串不执行任何操作,那么您的用户将收到完全相同的对象。您可以在文档中提供您认为将输入字符串表示为 Python 文字的简洁易读方式的示例。如果您发现使用二进制字符串感到困惑,"\x20\x01"那么您可以接受 ascii 十六进制表示:("2001"您可以使用 binascii.hexlify/unhexlify 将一个字符串转换为另一个字符串)。


正则表达式的情况更复杂,因为有两种语言:

  1. Python 根据其字符串文字语法解释转义序列
  2. 正则表达式引擎将字符串对象解释为也有自己的转义序列的正则表达式模式
于 2012-12-30T04:58:28.490 回答
0

我认为您将不得不走加入路线。

这是一个例子:

>>> m = {chr(c): '\\x{0}'.format(hex(c)[2:].zfill(2)) for c in xrange(0,256)}
>>>
>>> x = "\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x8a\x2e\x03\x70\x73\x34"
>>> print ''.join(map(m.get, x))
\x20\x01\x0d\xb8\x85\xa3\x00\x00\x00\x00\x8a\x2e\x03\x70\x73\x34

我不完全确定你为什么需要它。如果您的代码需要与其他代码交互,我建议您同意定义的格式,并坚持下去。

于 2012-12-30T01:58:04.673 回答