1. ホーム
  2. regex

[解決済み] 正規表現で有効なローマ数字のみにマッチさせるには?

2022-04-15 01:29:30

質問内容

について考える その他の問題 ローマ数字にマッチする正規表現を作ることもできない(ましてや、ローマ数字を生成する文脈自由文法を作ることもできない)と判断しました。

問題は、有効なローマ数字のみをマッチングさせることです。 例えば、990は"XM"ではなく、"CMXC"です。

この正規表現を作るときに困ったのは、ある文字を許可するかしないかを決めるために、過去を振り返る必要があることです。 たとえば、千と百を考えてみましょう。

私はM{0,2}C?Mを許可することができます(900、1000、1900、2000、2900、3000を許可するため)。しかし、CMにマッチする場合、私は次の文字がCまたはDであることを許可することはできません(すでに900になっているため)。

正規表現でこれを表現するにはどうしたらよいでしょうか?

単純に正規表現ができないのであれば、文脈自由文法で表現できるのでしょうか?

どのように解決するのですか?

以下の正規表現を使用することができます。

^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$

分解してみる M{0,4} は thousands セクションを指定し、基本的にそのセクションを 04000 . 比較的単純なものです。

   0: <empty>  matched by M{0}
1000: M        matched by M{1}
2000: MM       matched by M{2}
3000: MMM      matched by M{3}
4000: MMMM     matched by M{4}

もちろん、以下のような使い方も可能です。 M* を許可する 任意の の数(0を含む)、より大きな数を許可したい場合は、千の数。

次に (CM|CD|D?C{0,3}) 少し複雑ですが、これは数百のセクションのためのもので、あらゆる可能性をカバーしています。

  0: <empty>  matched by D?C{0} (with D not there)
100: C        matched by D?C{1} (with D not there)
200: CC       matched by D?C{2} (with D not there)
300: CCC      matched by D?C{3} (with D not there)
400: CD       matched by CD
500: D        matched by D?C{0} (with D there)
600: DC       matched by D?C{1} (with D there)
700: DCC      matched by D?C{2} (with D there)
800: DCCC     matched by D?C{3} (with D there)
900: CM       matched by CM

3つ目は (XC|XL|L?X{0,3}) は、前のセクションと同じルールに従いますが、10番目の場所についてです。

 0: <empty>  matched by L?X{0} (with L not there)
10: X        matched by L?X{1} (with L not there)
20: XX       matched by L?X{2} (with L not there)
30: XXX      matched by L?X{3} (with L not there)
40: XL       matched by XL
50: L        matched by L?X{0} (with L there)
60: LX       matched by L?X{1} (with L there)
70: LXX      matched by L?X{2} (with L there)
80: LXXX     matched by L?X{3} (with L there)
90: XC       matched by XC

そして、最後に。 (IX|IV|V?I{0,3}) はユニットセクションで 0 を通して 9 また、前の2つのセクションと同様です(ローマ数字は、一見奇妙に見えますが、それが何であるかを理解すれば、いくつかの論理的なルールに従います)。

0: <empty>  matched by V?I{0} (with V not there)
1: I        matched by V?I{1} (with V not there)
2: II       matched by V?I{2} (with V not there)
3: III      matched by V?I{3} (with V not there)
4: IV       matched by IV
5: V        matched by V?I{0} (with V there)
6: VI       matched by V?I{1} (with V there)
7: VII      matched by V?I{2} (with V there)
8: VIII     matched by V?I{3} (with V there)
9: IX       matched by IX


ただ、その正規表現は空文字列にもマッチすることに留意してください。もしこれが嫌なら(そしてあなたの正規表現エンジンが十分にモダンなら)、正のlook-behindとlook-aheadを使用することができます。

(?<=^)M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})(?=$)

(もう一つの方法は、長さが0でないことを事前にチェックすることです)。