Как обеспечить, чтобы все строковые литералы были в кодировке Unicode в Python
У меня есть довольно большая база кода Python, чтобы пройти. Есть проблема, когда некоторые строковые литералы являются строками, а другие - юникодом. И это вызывает ошибки. Я пытаюсь преобразовать все в Unicode. Мне было интересно, если есть инструмент, который может конвертировать все литералы в Unicode. Т.е. если бы нашел что-то вроде этого:
print "result code %d" % result['code']
чтобы:
print u"result code %d" % result[u'code']
Если это поможет, я использую PyCharm (в случае, если есть расширение, которое делает это), однако я был бы рад использовать такую же команду. Надеюсь, такой инструмент существует.
2 ответа
Вы можете использовать tokenize.generate_tokens, чтобы разбить строковое представление кода Python на токены. tokenize
также классифицирует токены для вас. Таким образом, вы можете идентифицировать строковые литералы в коде Python.
Тогда нетрудно манипулировать токенами, добавляя 'u'
где желательно:
import tokenize
import token
import io
import collections
class Token(collections.namedtuple('Token', 'num val start end line')):
@property
def name(self):
return token.tok_name[self.num]
def change_str_to_unicode(text):
result = text.splitlines()
# Insert a dummy line into result so indexing result
# matches tokenize's 1-based indexing
result.insert(0, '')
changes = []
for tok in tokenize.generate_tokens(io.BytesIO(text).readline):
tok = Token(*tok)
if tok.name == 'STRING' and not tok.val.startswith('u'):
changes.append(tok.start)
for linenum, s in reversed(changes):
line = result[linenum]
result[linenum] = line[:s] + 'u' + line[s:]
return '\n'.join(result[1:])
text = '''print "result code %d" % result['code']
# doesn't touch 'strings' in comments
'handles multilines' + \
'okay'
u'Unicode is not touched'
'''
print(change_str_to_unicode(text))
доходность
print u"result code %d" % result[u'code']
# doesn't touch 'strings' in comments
u'handles multilines' + u'okay'
u'Unicode is not touched'
Попробуйте это (использует регулярное выражение), и оно короче, чем решение @unutbu.
Но есть отверстие для петли, строки, содержащие #
не будет работать с этим.
import re
scode = '''
print "'Hello World'" # prints 'Hello World'
u'Unicode is unchanged'"""
# so are "comments"'''
x1 = re.compile('''(?P<unicode>u?)(?P<c>'|")(?P<data>.*?)(?P=c)''')
def repl(m):
return "u%(c)s%(data)s%(c)s" % m.groupdict()
fcode = '\n'.join(
[re.sub(x1,repl,i)
if not '#' in i
else re.sub(x1,repl,i[:i.find('#')])+i[i.find('#'):]
for i in scode.splitlines()])
print fcode
Выходы:
print u"'Hello World'" # prints 'Hello World'
u'Unicode is unchanged'
# so are "comments"
За #
У меня есть это (и это дольше, чем решение @unutbu:|)
import re
scode = '''print "'Hello World'" # prints 'Hello World'
u'Unicode is unchanged'
# so are "comments"
'#### Hi' # 'Hi' '''
x1 = re.compile('''(?P<unicode>u?)(?P<c>'|")(?P<data>.*?)(?P=c)''')
def in_string(text,index):
curr,in_l,in_str,level = '',0,False,[]
for c in text[:index+1]:
if c == '"' or c == "'":
if in_str and curr == c:
instr = False
curr = ''
in_l -= 1
else:
instr = True
curr = c
in_l += 1
level.append(in_l)
return bool(level[index])
def repl(m):
return "u%(c)s%(data)s%(c)s" % m.groupdict()
def handle_hashes(i):
if i.count('#') == 1:
n = i.find('#')
else:
n = get_hash_out_of_string(i)
return re.sub(x1,repl,i[:n]) + i[n:]
def get_hash_out_of_string(i):
n = i.find('#')
curr = i[:]
last = (len(i)-1)-''.join(list(reversed(i))).find('#')
while in_string(curr,n) and n < last:
curr = curr[:n]+' '+curr[n+1:]
n = curr.find('#')
return n
fcode = '\n'.join(
[re.sub(x1,repl,i)
if not '#' in i
else handle_hashes(i)
for i in scode.splitlines()])
print fcode
Выход:
print u"'Hello World'" # prints 'Hello World'
u'Unicode is unchanged'
# so are "comments"
u'#### Hi' # 'Hi'