Utilizzare il tokenize
library cercare token.OP
tokens, dove il secondo elemento è un ;
*. Sostituisci questi token con un token.NEWLINE
token.
Avresti bisogno di regolare i tuoi offset di token e generare indentazione di corrispondenza troppo però; quindi dopo un NEWLINE
devi regolare i numeri di riga (incrementa di un offset incrementato per ogni NEWLINE
che inserisci) e la riga 'next' (resto della riga corrente) dovrebbe avere gli indici aggiustati per corrispondere all'indentazione corrente livello:
import tokenize
TokenInfo = getattr(tokenize, 'TokenInfo', lambda *a: a) # Python 3 compat
def semicolon_to_newline(tokens):
line_offset = 0
last_indent = None
col_offset = None # None or an integer
for ttype, tstr, (slno, scol), (elno, ecol), line in tokens:
slno, elno = slno + line_offset, elno + line_offset
if ttype in (tokenize.INDENT, tokenize.DEDENT):
last_indent = ecol # block is indented to this column
elif ttype == tokenize.OP and tstr == ';':
# swap out semicolon with a newline
ttype = tokenize.NEWLINE
tstr = '\n'
line_offset += 1
if col_offset is not None:
scol, ecol = scol - col_offset, ecol - col_offset
col_offset = 0 # next tokens should start at the current indent
elif col_offset is not None:
if not col_offset:
# adjust column by starting column of next token
col_offset = scol - last_indent
scol, ecol = scol - col_offset, ecol - col_offset
if ttype == tokenize.NEWLINE:
col_offset = None
yield TokenInfo(
ttype, tstr, (slno, scol), (elno, ecol), line)
with open(sourcefile, 'r') as source, open(destination, 'w') as dest:
generator = tokenize.generate_tokens(source.readline)
dest.write(tokenize.untokenize(semicolon_to_newline(generator)))
Nota che non si preoccupano di correggere il valore line
; è solo informativo, i dati che sono stati letti dal file non vengono effettivamente utilizzati durante la tokenizzazione.
Demo:
>>> from io import StringIO
>>> source = StringIO('''\
... def main():
... a = "a;b"; return a
... ''')
>>> generator = tokenize.generate_tokens(source.readline)
>>> result = tokenize.untokenize(semicolon_to_newline(generator))
>>> print(result)
def main():
a = "a;b"
return a
e un po 'più complesso:
>>> source = StringIO('''\
... class Foo(object):
... def bar(self):
... a = 10; b = 11; c = 12
... if self.spam:
... x = 12; return x
... x = 15; return y
...
... def baz(self):
... return self.bar;
... # note, nothing after the semicolon
... ''')
>>> generator = tokenize.generate_tokens(source.readline)
>>> result = tokenize.untokenize(semicolon_to_newline(generator))
>>> print(result)
class Foo(object):
def bar(self):
a = 10
b = 11
c = 12
if self.spam:
x = 12
return x
x = 15
return y
def baz(self):
return self.bar
# note, nothing after the semicolon
>>> print(result.replace(' ', '.'))
class.Foo(object):
....def.bar(self):
........a.=.10
........b.=.11
........c.=.12
........if.self.spam:
............x.=.12
............return.x
........x.=.15
........return.y
....def.baz(self):
........return.self.bar
........
........#.note,.nothing.after.the.semicolon
* La Python 3 versione di tokenize
uscite più informativi TokenInfo
tuple di nome, che hanno un attributo aggiuntivo exact_type
che può essere utilizzato al posto di una corrispondenza testuale: tok.exact_type == tokenize.SEMI
. Ho mantenuto il precedente compatibile con Python 2 e 3 comunque.
A cura di aggiungere un po ';' s nei commenti, e alcuni casi patologici con ripetuti '; 'S. – PaulMcG