这是在Python 2中编码转义字符而不破坏Unicode的正确方法



我想我对Python的unicode字符串太着迷了。我正在尝试将转义字符编码为Unicode字符串,而不使用转义的实际Unicode字符。我得到这个:

In [14]: a = u"Examplen"
In [15]: b = u"Примерn"
In [16]: print a
Example

In [17]: print b
Пример

In [18]: print a.encode('unicode_escape')
Examplen
In [19]: print b.encode('unicode_escape')
u041fu0440u0438u043cu0435u0440n

当我迫切需要的时候(很明显,英文示例可以随心所欲):

In [18]: print a.encode('unicode_escape')
Examplen
In [19]: print b.encode('unicode_escape')
Примерn

除了转到Python 3之外,我该怎么办?

PS:正如下面所指出的,我实际上是在寻求逃离控制角色。我是否需要的不仅仅是这些,还有待观察。

Backflash在unicode数据中间转义ascii控制字符绝对是一件很有用的事情。但这不仅仅是对它们进行转义,当您想要返回实际的字符数据时,还可以适当地取消对它们的捕获。

应该有一种方法可以在python stdlib中实现这一点,但没有。我提交了一份错误报告:http://bugs.python.org/issue18679

但与此同时,这里有一个关于使用翻译和技巧的工作:

tm = dict((k, repr(chr(k))[1:-1]) for k in range(32))
tm[0] = r''
tm[7] = r'a'
tm[8] = r'b'
tm[11] = r'v'
tm[12] = r'f'
tm[ord('\')] = '\\'
b = u"Примерn"
c = b.translate(tm)
print(c) ## results in: Примерn

所有非反斜杠单字母控制字符都将使用\x##序列进行转义,但如果您需要对这些字符进行不同的处理,您的翻译矩阵可以做到这一点。这种方法没有损耗,所以它对我有效

但把它拿出来也很麻烦,因为你不能用translate把字符序列翻译回单个字符。

d = c.encode('latin1', 'backslashreplace').decode('unicode_escape')
print(d) ## result in Пример with trailing newline character

实际上,您必须使用latin1对映射到字节的字符进行单独编码,同时对latin1不知道的反斜杠转义unicode字符进行编码,以便unicodeescape编解码器能够以正确的方式处理所有内容的重新组装。

更新

所以我有一个案例,我需要它来处理python2.7和python3.3。以下是我所做的(埋在_compat.py模块中):

if isinstance(b"", str):                                                        
    byte_types = (str, bytes, bytearray)                                        
    text_types = (unicode, )                                                    
    def uton(x): return x.encode('utf-8', 'surrogateescape')                    
    def ntob(x): return x                                                       
    def ntou(x): return x.decode('utf-8', 'surrogateescape')                    
    def bton(x): return x
else:                                                                           
    byte_types = (bytes, bytearray)                                             
    text_types = (str, )                                                        
    def uton(x): return x                                                       
    def ntob(x): return x.encode('utf-8', 'surrogateescape')                    
    def ntou(x): return x                                                       
    def bton(x): return x.decode('utf-8', 'surrogateescape')    
escape_tm = dict((k, ntou(repr(chr(k))[1:-1])) for k in range(32))              
escape_tm[0] = u''                                                            
escape_tm[7] = u'a'                                                            
escape_tm[8] = u'b'                                                            
escape_tm[11] = u'v'                                                           
escape_tm[12] = u'f'                                                           
escape_tm[ord('\')] = u'\\'
def escape_control(s):                                                          
    if isinstance(s, text_types):                                               
        return s.translate(escape_tm)
    else:
        return s.decode('utf-8', 'surrogateescape').translate(escape_tm).encode('utf-8', 'surrogateescape')
def unescape_control(s):                                                        
    if isinstance(s, text_types):                                               
        return s.encode('latin1', 'backslashreplace').decode('unicode_escape')
    else:                                                                       
        return s.decode('utf-8', 'surrogateescape').encode('latin1', 'backslashreplace').decode('unicode_escape').encode('utf-8', 'surrogateescape')

首先让我们更正术语。您要做的是将"控制字符"替换为等效的"转义序列"。

我还没有找到任何内置的方法来实现这一点,也没有人发布过。幸运的是,这不是一个难以编写的函数。

control_chars = [unichr(c) for c in range(0x20)] # you may extend this as required
def control_escape(s):
    chars = []
    for c in s:
        if c in control_chars:
            chars.append(c.encode('unicode_escape'))
        else:
            chars.append(c)
    return u''.join(chars)

或者可读性稍差的单行版本:

def control_escape2(s):
    return u''.join([c.encode('unicode_escape') if c in control_chars else c for c in s])

方法.encode返回一个字节字符串(在Python 2中为str类型),因此它不能返回unicode字符。

但由于序列很少,您可以很容易地手动.replace它们。看见http://docs.python.org/reference/lexical_analysis.html#string-完整列表的文字。

.encode('unicode_escape')返回一个字节字符串。您可能希望直接转义Unicode字符串中的控制字符:

# coding: utf8
import re
def esc(m):
    return u'\x{:02x}'.format(ord(m.group(0)))
s = u'rtb马克n'
# Match control characters 0-31.
# Use DOTALL option to match end-of-line control characters as well.
print re.sub(ur'(?s)[x00-x1f]',esc,s)

输出:

x0dx09x08马克x0a

请注意,0-31之外还有其他Unicode控制字符,因此您可能需要更类似的字符:

# coding: utf8
import re
import unicodedata as ud
def esc(m):
    c = m.group(0)
    if ud.category(c).startswith('C'):
        return u'\u{:04x}'.format(ord(c))
    return c
s = u'rMarktb马克n'
# Match ALL characters so the replacement function
# can test the category.  Not very efficient if the string is long.
print re.sub(ur'(?s).',esc,s)

输出:

u000dMarku0009u0008马克u000a

您可能需要对被视为控制字符的内容进行更精细的控制。有许多类别。您可以构建一个与特定类型匹配的正则表达式:

import sys
import re
import unicodedata as ud
# Generate a regular expression that matches any Cc category Unicode character.
Cc_CODES = u'(?s)[' + re.escape(u''.join(unichr(n) for n in range(sys.maxunicode+1) if ud.category(unichr(n)) == 'Cc')) + u']'

相关内容

最新更新