2016-04-11 18 views
3

の正規表現、特にそのmatch.start()match.end()の方法をよく理解するために苦労しています。このコードで遊んでいる間正規表現match.start()とmatch.end()Pythonの例

は(hereを見つけた):

Token = collections.namedtuple('Token', ['typ', 'value', 'line', 'column']) 

def tokenize(code): 
    keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'} 
    token_specification = [ 
     ('NUMBER', r'\d+(\.\d*)?'), # Integer or decimal number 
     ('ASSIGN', r':='),   # Assignment operator 
     ('END',  r';'),   # Statement terminator 
     ('ID',  r'[A-Za-z]+'), # Identifiers 
     ('OP',  r'[+\-*/]'),  # Arithmetic operators 
     ('NEWLINE', r'\n'),   # Line endings 
     ('SKIP', r'[ \t]+'),  # Skip over spaces and tabs 
     ('MISMATCH',r'.'),   # Any other character 
    ] 
    tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification) 
    line_num = 1 
    line_start = 0 
    for mo in re.finditer(tok_regex, code): 
     kind = mo.lastgroup 
     value = mo.group(kind) 
     if kind == 'NEWLINE': 
      line_start = mo.end() 
      line_num += 1 
     elif kind == 'SKIP': 
      pass 
     elif kind == 'MISMATCH': 
      raise RuntimeError('%r unexpected on line %d' % (value, line_num)) 
     else: 
      if kind == 'ID' and value in keywords: 
       kind = value 
      column = mo.start() - line_start 
      yield Token(kind, value, line_num, column) 

statements = ''' 
    IF quantity THEN 
     total := total + price * quantity; 
     tax := price * 0.05; 
    ENDIF; 
''' 

for token in tokenize(statements): 
    print(token) 

mo.end()mo.start()を使用して行と列を計算するときに、私が使用してロジックを理解することはできません。 たとえばの場合、NEWLINEとを出力するとTokenの出力も得られる場合は、列のインデックスが完全に崩れてしまいます。 mo.end()で列のインデックスを再計算して、この例のようにこのケースに対応しましたが、失敗しました。 アイデア、サンプルコード、説明が素晴らしいです。

+1

あなたは[ドキュメント]に表示された場合(https://docs.python.org/3/library/ re.html#re.match.start)いくつかの例があります(私はどちらもあまり理解していません)。 – Karim

+0

ありがとう、私はそれらを見たが、説明に言及するその事例を実装するために賢明になりませんでした:/ –

+0

私はあなたが提供したコードが正しく動作するようにドキュメントに一致するようにトークンを更新しました:それがあなたの部分の意図的な省略であるかどうか私に知らせてください。 –

答えて

1

あなたの基準を満たしていると私は思っています。試したことを投稿できれば、問題の原因をよりよく理解できます。

import collections 
import re 
Token = collections.namedtuple('Token', ['typ', 'value', 'line', 'column']) 

def tokenize(code): 
    keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'} 
    token_specification = [ 
     ('NUMBER', r'\d+(\.\d*)?'), # Integer or decimal number 
     ('ASSIGN', r':='),   # Assignment operator 
     ('END',  r';'),   # Statement terminator 
     ('ID',  r'[A-Za-z]+'), # Identifiers 
     ('OP',  r'[+\-*/]'),  # Arithmetic operators 
     ('NEWLINE', r'\n'),   # Line endings 
     ('SKIP', r'[ \t]+'),  # Skip over spaces and tabs 
     ('MISMATCH',r'.'),   # Any other character 
    ] 
    tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification) 
    line_num = 1 
    line_start = 0 
    for mo in re.finditer(tok_regex, code): 
     kind = mo.lastgroup 
     value = mo.group(kind) 
     column = (mo.start() - line_start) + 1 

     if kind == 'MISMATCH': 
      raise RuntimeError('%r unexpected on line %d' % (value, line_num)) 
     else: 
      if kind == 'ID' and value in keywords: 
       kind = value 
      yield Token(kind, value, line_num, column) 
      if kind == 'NEWLINE': 
       line_start = mo.end() 
       line_num += 1 


statements = ''' 
    IF quantity THEN 
     total := total + price * quantity; 
     tax := price * 0.05; 
    ENDIF; 
''' 

for token in tokenize(statements): 
    print(token) 

出力:string[mo.start():mo.end()]はあなたにマッチした文字列を返すように

Token(typ='NEWLINE', value='\n', line=1, column=1) 
Token(typ='SKIP', value=' ', line=2, column=1) 
Token(typ='IF', value='IF', line=2, column=5) 
Token(typ='SKIP', value=' ', line=2, column=7) 
Token(typ='ID', value='quantity', line=2, column=8) 
Token(typ='SKIP', value=' ', line=2, column=16) 
Token(typ='THEN', value='THEN', line=2, column=17) 
Token(typ='SKIP', value=' ', line=2, column=21) 
Token(typ='NEWLINE', value='\n', line=2, column=22) 
Token(typ='SKIP', value='  ', line=3, column=1) 
Token(typ='ID', value='total', line=3, column=9) 
Token(typ='SKIP', value=' ', line=3, column=14) 
Token(typ='ASSIGN', value=':=', line=3, column=15) 
Token(typ='SKIP', value=' ', line=3, column=17) 
Token(typ='ID', value='total', line=3, column=18) 
Token(typ='SKIP', value=' ', line=3, column=23) 
Token(typ='OP', value='+', line=3, column=24) 
Token(typ='SKIP', value=' ', line=3, column=25) 
Token(typ='ID', value='price', line=3, column=26) 
Token(typ='SKIP', value=' ', line=3, column=31) 
Token(typ='OP', value='*', line=3, column=32) 
Token(typ='SKIP', value=' ', line=3, column=33) 
Token(typ='ID', value='quantity', line=3, column=34) 
Token(typ='END', value=';', line=3, column=42) 
Token(typ='NEWLINE', value='\n', line=3, column=43) 
Token(typ='SKIP', value='  ', line=4, column=1) 
Token(typ='ID', value='tax', line=4, column=9) 
Token(typ='SKIP', value=' ', line=4, column=12) 
Token(typ='ASSIGN', value=':=', line=4, column=13) 
Token(typ='SKIP', value=' ', line=4, column=15) 
Token(typ='ID', value='price', line=4, column=16) 
Token(typ='SKIP', value=' ', line=4, column=21) 
Token(typ='OP', value='*', line=4, column=22) 
Token(typ='SKIP', value=' ', line=4, column=23) 
Token(typ='NUMBER', value='0.05', line=4, column=24) 
Token(typ='END', value=';', line=4, column=28) 
Token(typ='NEWLINE', value='\n', line=4, column=29) 
Token(typ='SKIP', value=' ', line=5, column=1) 
Token(typ='ENDIF', value='ENDIF', line=5, column=5) 
Token(typ='END', value=';', line=5, column=10) 
Token(typ='NEWLINE', value='\n', line=5, column=11) 
+2

'column' 1がそれよりも小さいのはなぜですか?例えば最初の 'Token'は1列目が0でなくてはなりません。 – Karim

+0

@Karim 'match.start'は' 1'ではなく最初の '' column''に '0'を使います。それはプログラミングではかなり普通です。 –

+0

@カリンを印刷するときに1を加えることができます。元のコードにも当てはまることに気づくでしょう。カラム "0"に何かが表示されることは決してありません。しかし 'IF'は列' 4'の '5'ではなく' 012'に出力されます。例えば、 –

2

mo.startmo.endはあなたの試合の開始と終了インデックスを返します。あなたの例が\nと一致するたびに、現在の行を追跡するline_numが増加し、line_startは現在の行の最初の文字のインデックスを含むように更新されます。これにより、プログラムは、後でトークンが一致したときに列を計算することができます:column = mo.start() - line_start

行と列のトラッキング動作を説明するために、与えられた文字列内のすべての数字を見つける簡単な例を作成しました。それは、出力ラインと開始列をするすべての番号の場合:

import re 

PATTERN = '(?P<NEWLINE>\n)|(?P<NUMBER>\d+)' 
s = '''word he12re 5 there 
mo912re 
another line 17 
''' 

line = 1 
line_start = 0 
for mo in re.finditer(PATTERN, s): 
    if mo.lastgroup == 'NEWLINE': 
     # Found new line, increase line number and change line_start to 
     # contain index of first character on the line 
     line += 1 
     line_start = mo.end() 
    elif mo.lastgroup == 'NUMBER': 
     # Column: index of start of the match - index of first char on line 
     column = mo.start() - line_start 
     print('line {0}: {1} at column {2}'.format(line, mo.group(0), column)) 

出力:

line 1: 12 at column 7 
line 1: 5 at column 12 
line 2: 912 at column 2 
line 3: 17 at column 13