効率よく一致複数のregexes Python
-
02-07-2019 - |
質問
語彙分析装置は非常に簡単に書けるだいregexes.今日書かせていただき、分析装置のPythonでは、出を行った:
import re
import sys
class Token(object):
""" A simple Token structure.
Contains the token type, value and position.
"""
def __init__(self, type, val, pos):
self.type = type
self.val = val
self.pos = pos
def __str__(self):
return '%s(%s) at %s' % (self.type, self.val, self.pos)
class LexerError(Exception):
""" Lexer error exception.
pos:
Position in the input line where the error occurred.
"""
def __init__(self, pos):
self.pos = pos
class Lexer(object):
""" A simple regex-based lexer/tokenizer.
See below for an example of usage.
"""
def __init__(self, rules, skip_whitespace=True):
""" Create a lexer.
rules:
A list of rules. Each rule is a `regex, type`
pair, where `regex` is the regular expression used
to recognize the token and `type` is the type
of the token to return when it's recognized.
skip_whitespace:
If True, whitespace (\s+) will be skipped and not
reported by the lexer. Otherwise, you have to
specify your rules for whitespace, or it will be
flagged as an error.
"""
self.rules = []
for regex, type in rules:
self.rules.append((re.compile(regex), type))
self.skip_whitespace = skip_whitespace
self.re_ws_skip = re.compile('\S')
def input(self, buf):
""" Initialize the lexer with a buffer as input.
"""
self.buf = buf
self.pos = 0
def token(self):
""" Return the next token (a Token object) found in the
input buffer. None is returned if the end of the
buffer was reached.
In case of a lexing error (the current chunk of the
buffer matches no rule), a LexerError is raised with
the position of the error.
"""
if self.pos >= len(self.buf):
return None
else:
if self.skip_whitespace:
m = self.re_ws_skip.search(self.buf[self.pos:])
if m:
self.pos += m.start()
else:
return None
for token_regex, token_type in self.rules:
m = token_regex.match(self.buf[self.pos:])
if m:
value = self.buf[self.pos + m.start():self.pos + m.end()]
tok = Token(token_type, value, self.pos)
self.pos += m.end()
return tok
# if we're here, no rule matched
raise LexerError(self.pos)
def tokens(self):
""" Returns an iterator to the tokens found in the buffer.
"""
while 1:
tok = self.token()
if tok is None: break
yield tok
if __name__ == '__main__':
rules = [
('\d+', 'NUMBER'),
('[a-zA-Z_]\w+', 'IDENTIFIER'),
('\+', 'PLUS'),
('\-', 'MINUS'),
('\*', 'MULTIPLY'),
('\/', 'DIVIDE'),
('\(', 'LP'),
('\)', 'RP'),
('=', 'EQUALS'),
]
lx = Lexer(rules, skip_whitespace=True)
lx.input('erw = _abc + 12*(R4-623902) ')
try:
for tok in lx.tokens():
print tok
except LexerError, err:
print 'LexerError at position', err.pos
したがいろんなことも明らかに非効率的です。はありまregex方々に支持してもらえればと思いますに行くにも便利でより効率的に/エレガント?
具体的には、あるのを避けるループを全ての正規表現規則直線的に見つけ合う?
解決
できる合併すべてのregexesひとつに、"|"オペレーターとしてregex図書館のみならず、すべての目の肥えたとの間トークン注意が必要確保のための好みのトークン(例えば避けるマッチングキーワードとしての識別子).
他のヒント
であることになりました。スキャナークラスで記載されていないの標準ライブラリですが、値段。次に例を示します。
import re
scanner = re.Scanner([
(r"-?[0-9]+\.[0-9]+([eE]-?[0-9]+)?", lambda scanner, token: float(token)),
(r"-?[0-9]+", lambda scanner, token: int(token)),
(r" +", lambda scanner, token: None),
])
>>> scanner.scan("0 -1 4.5 7.8e3")[0]
[0, -1, 4.5, 7800.0]
見 この pythonで書きします。このプします。
import collections
import re
Token = collections.namedtuple('Token', ['typ', 'value', 'line', 'column'])
def tokenize(s):
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
]
tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
get_token = re.compile(tok_regex).match
line = 1
pos = line_start = 0
mo = get_token(s)
while mo is not None:
typ = mo.lastgroup
if typ == 'NEWLINE':
line_start = pos
line += 1
elif typ != 'SKIP':
val = mo.group(typ)
if typ == 'ID' and val in keywords:
typ = val
yield Token(typ, val, line, mo.start()-line_start)
pos = mo.end()
mo = get_token(s, pos)
if pos != len(s):
raise RuntimeError('Unexpected character %r on line %d' %(s[pos], line))
statements = '''
IF quantity THEN
total := total + price * quantity;
tax := price * 0.05;
ENDIF;
'''
for token in tokenize(statements):
print(token)
のことながら、こちらの腕の見せ所での線
tok_regex = '|'.join('(?P<%s>%s)' % pair for pair in token_specification)
こちらの (?P<ID>PATTERN)
ますマークはデフォルトパラメータと合致した結果の名前を指定 ID
.
re.match
がアクセスされる方までの位置引数:
pos = 0
end = len(text)
while pos < end:
match = regexp.match(text, pos)
# do something with your match
pos = match.end()
を見つけてください。pygmentsる船舶は、shitloadのlexersのための構文強調表示を目的と異なる実装は、に基づき正規表現オブジェクト
まれてくる可能性があることで組み合わせ、トークンregexesまないベンチマークです。のようなもの:
x = re.compile('(?P<NUMBER>[0-9]+)|(?P<VAR>[a-z]+)')
a = x.match('9999').groupdict() # => {'VAR': None, 'NUMBER': '9999'}
if a:
token = [a for a in a.items() if a[1] != None][0]
フィルターは使ってはいくつかのベンチマーキング...
更新: ではこのような組み合わせのすべてのトークンを記述機能のように:
def find_token(lst):
for tok in lst:
if tok[1] != None: return tok
raise Exception
ょほぼ同じ速度(そteensy高速化)。この高速な数の電話を一致させるためには、そのループトークンを差別もあると死滅します。
こなう直接の回答へのご質問ありがとうで入手できるかもしれませんが、見たいと思い AOETOOLS.による この 文書のpythonコード生成目標にすべきます。
としておregexesが本当につくスピードでまわregexes.最初のうご注regexesの順るのにデフォルトです。き姿を追加する簡単なプロファイラのコードが収集したトークンをカウントごとのトークン型ならびにその手段としては、本体です。のその他のソリューションするバケットうごregexes(以降にキースペース、キャラクターは比較的小さい)として使用の配列または辞書に必要なregexes後の単差別の最初の文字です。
しかし、私はだんこのルートは、是非経験してほしいのようなもの AOETOOLS これを使用する場合と比べ、より迅速にくいします。