2016-06-13 18 views

Trả lời

4

Sử dụng tokenize library để tìm kiếm token.OP tokens, trong đó phần tử thứ hai là ;*. Thay thế các mã thông báo này bằng token.NEWLINE token.

Bạn cần phải điều chỉnh bù lệch mã thông báo và tạo thụt lề phù hợp quá; do đó, sau NEWLINE bạn cần phải điều chỉnh số dòng (tăng bởi số tiền bạn tăng cho mỗi NEWLINE bạn chèn) và dòng 'tiếp theo' (phần còn lại của dòng hiện tại) sẽ phải có các chỉ số được điều chỉnh để khớp với thụt đầu dòng hiện tại cấp:

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

Lưu ý rằng tôi không bận tâm sửa giá trị line; nó chỉ mang tính thông tin, dữ liệu được đọc từ tập tin này không thực sự được sử dụng khi bỏ thông báo.

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 

và hơi phức tạp hơn:

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

* Các Python 3 phiên bản của tokenize đầu ra thông tin mới hơn TokenInfo tên các bộ, trong đó có một phụ exact_type thuộc tính có thể được sử dụng thay vì thực hiện so khớp văn bản: tok.exact_type == tokenize.SEMI. Tuy nhiên, tôi đã giữ phần trên tương thích với Python 2 và 3.

1

Dưới đây là một giải pháp pyparsing - xem chú thích trong mã bên dưới:

from pyparsing import Literal, restOfLine, quotedString, pythonStyleComment, line 

SEMI = Literal(';') 
patt = SEMI + restOfLine 
patt.ignore(quotedString) 
patt.ignore(pythonStyleComment) 

def split_at(s, locs): 
    """ 
    break up s into pieces, given list of break locations 
    """ 
    current = 0 
    ret = [] 
    for loc in locs: 
     ret.append(s[current:loc].lstrip()) 
     current = loc+1 
    ret.append(s[current:].lstrip()) 
    return ret 

def split_on_semicolon(s,l,tokens): 
    """ 
    parse time callback, when finding first unquoted ';' on a line 
    """ 
    current_line = line(l,s) 
    line_body = current_line.lstrip() 
    indent = current_line.index(line_body) 
    indent = current_line[:indent] 

    # may be more than one ';' on this line, find them all 
    # (the second token contains everything after the ';') 
    remainder = tokens[1] 
    if remainder.strip(): 
     all_semis = [s for _,s,_ in SEMI.scanString(remainder)] 

     # break line into pieces 
     pieces = split_at(remainder, all_semis) 

     # rejoin pieces, with leading indents 
     return '\n'+'\n'.join(indent+piece for piece in pieces) 
    else: 
     return '' 

patt.addParseAction(split_on_semicolon) 

sample = """ 
def main(): 
    this_semi_does_nothing(); 
    neither_does_this_but_there_are_spaces_afterward(); 
    a = "a;b"; return a # this is a comment; it has a semicolon! 

def b(): 
    if False: 
     z=1000;b("; in quotes"); c=200;return z 
    return ';' 

class Foo(object): 
    def bar(self): 
     '''a docstring; with a semicolon''' 
     a = 10; b = 11; c = 12 

     # this comment; has several; semicolons 
     if self.spam: 
      x = 12; return x # so; does; this; one 
     x = 15;;; y += x; return y 

    def baz(self): 
     return self.bar 
""" 
print(patt.transformString(sample)) 

Cung cấp:

def main(): 
    this_semi_does_nothing() 
    neither_does_this_but_there_are_spaces_afterward() 
    a = "a;b" 
    return a # this is a comment; it has a semicolon! 

def b(): 
    if False: 
     z=1000 
     b("; in quotes") 
     c=200 
     return z 
    return ';' 

class Foo(object): 
    def bar(self): 
     '''a docstring; with a semicolon''' 
     a = 10 
     b = 11 
     c = 12 

     # this comment; has several; semicolons 
     if self.spam: 
      x = 12 
      return x # so; does; this; one 
     x = 15 
     y += x 


     return y 

    def baz(self): 
     return self.bar 
+0

Edited để thêm một số ';' s trong nhận xét và một số trường hợp bệnh lý có lặp đi lặp lại '; 'S. – PaulMcG

Các vấn đề liên quan