ネストされた括弧/ブラケットを処理したいので、それらを処理する「正しい」方法は、それらを個別にトークン化し、ネストレベルを追跡することです。したがって、単一の正規表現ではなく、さまざまなトークン タイプに対して複数の正規表現が必要です。
これは Python ですが、Java への変換はそれほど難しくありません。
# just comma
sep_re = re.compile(r',')
# open paren or open bracket
inc_re = re.compile(r'[[(]')
# close paren or close bracket
dec_re = re.compile(r'[)\]]')
# string literal
# (I was lazy with the escaping. Add other escape sequences, or find an
# "official" regex to use.)
chunk_re = re.compile(r'''"(?:[^"\\]|\\")*"|'(?:[^'\\]|\\')*[']''')
# This class could've been just a generator function, but I couldn;'t
# find a way to manage the state in the match function that wasn't
# awkward.
class tokenizer:
def __init__(self):
self.pos = 0
def _match(self, regex, s):
m = regex.match(s, self.pos)
if m:
self.pos += len(m.group(0))
self.token = m.group(0)
else:
self.token = ''
return self.token
def tokenize(self, s):
field = '' # the field we're working on
depth = 0 # how many parens/brackets deep we are
while self.pos < len(s):
if not depth and self._match(sep_re, s):
# In Java, change the "yields" to append to a List, and you'll
# have something roughly equivalent (but non-lazy).
yield field
field = ''
else:
if self._match(inc_re, s):
depth += 1
elif self._match(dec_re, s):
depth -= 1
elif self._match(chunk_re, s):
pass
else:
# everything else we just consume one character at a time
self.token = s[self.pos]
self.pos += 1
field += self.token
yield field
使用法:
>>> list(tokenizer().tokenize('foo=(3,(5+7),8),bar="hello,world",baz'))
['foo=(3,(5+7),8)', 'bar="hello,world"', 'baz']
この実装には、いくつかのショートカットが必要です。
- 文字列エスケープは非常に怠惰です:
\"
二重引用符で囲まれた文字列と\'
単一引用符で囲まれた文字列のみをサポートします。これは簡単に修正できます。
- ネストレベルのみを追跡します。括弧が (括弧ではなく) 括弧と一致していることは確認しません。それが気になる場合は
depth
、ある種のスタックに変更して、それに括弧/ブラケットをプッシュ/ポップすることができます。