在不杀死Unicode的情况下,在Python 2中编码转义字符的正确方法是什么?

时间:2012-03-19 21:55:53

标签: python unicode encoding

我觉得我对Python的unicode字符串感到很疯狂。我正在尝试在Unicode字符串中编码转义字符,而不用转义实际的Unicode字符。我明白了:

In [14]: a = u"Example\n"

In [15]: b = u"Пример\n"

In [16]: print a
Example


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


In [18]: print a.encode('unicode_escape')
Example\n

In [19]: print b.encode('unicode_escape')
\u041f\u0440\u0438\u043c\u0435\u0440\n

虽然我迫切需要(英语示例可以按照我的意愿运行,显然):

In [18]: print a.encode('unicode_escape')
Example\n

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

如果不移植到Python 3,我该怎么办?

PS:正如下面所指出的,我实际上是在试图逃避控制角色。我是否需要更多不仅仅需要看到它们。

4 个答案:

答案 0 :(得分:3)

首先让我们更正术语。你要做的是替换"控制角色"使用等效的"转义序列"。

我还没有找到任何内置的方法来做到这一点,而且还没有人发布过。幸运的是,写作并不是一个难题。

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])

答案 1 :(得分:2)

在unicode数据中间转义ascii控制字符的反斜杠绝对是一个有用的尝试。但它不仅仅是逃避它们,当你想要实际的角色数据时,它正确地解决它们。

应该有一种方法可以在python stdlib中执行此操作,但没有。我提交了一份错误报告:http://bugs.python.org/issue18679

但与此同时,这是一个使用翻译和hackery的工作:

tm = dict((k, repr(chr(k))[1:-1]) for k in range(32))
tm[0] = r'\0'
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 ##序列进行转义,但如果您需要使用不同的东西,您的翻译矩阵可以执行此操作。这种方法虽然没有损失,但它对我有用。

但是将其退出也很麻烦,因为你不能只使用翻译将字符序列翻译成单个字符。

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

你实际上必须使用latin1对映射到字节的字符进行编码,而反斜杠转义latin1不知道的unicode字符,以便unicode_escape编解码器能够以正确的方式处理所有内容。

<强>更新

所以我有一个案例,我需要在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'\0'                                                            
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')

答案 2 :(得分:1)

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

但由于只有少数\ - 序列,您可以轻松地.replace手动。 有关完整列表,请参阅http://docs.python.org/reference/lexical_analysis.html#string-literals

答案 3 :(得分:0)

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

# coding: utf8
import re

def esc(m):
    return u'\\x{:02x}'.format(ord(m.group(0)))

s = u'\r\t\b马克\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)

输出:

\x0d\x09\x08马克\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'\rMark\t\b马克\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)

输出:

\u000dMark\u0009\u0008马克\u000a

您可能希望更好地控制被视为控制角色的内容。有许多categories。您可以使用以下命令构建与特定类型匹配的正则表达式:

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']'