私の他の問題 について考えると、ローマ数字と一致する正規表現を作成することさえできないと判断しました(それらを生成するコンテキストフリーの文法は言うまでもありません)
問題は、有効なローマ数字のみに一致することです。たとえば、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}
これにより、千単位のセクションが指定され、基本的に0
と4000
の間に制限されます。それは比較的簡単です:
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}
(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
(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
実際、あなたの前提には欠陥があります。 990 [〜#〜] is [〜#〜] "XM"および "CMXC"。
ローマ人は、あなたの3年生の先生よりも「ルール」についてはそれほど心配していませんでした。それが合計する限り、それはOKでした。したがって、「IIII」は4の「IV」と同程度でした。また、「IIM」は998の完全にクールでした。
(もしあなたがそれに対処するのに苦労しているなら...英語のスペリングは1700年代まで正式でなかったことを覚えておいてください。それまで、読者が理解できる限り、それは十分でした)。
ここに保存するだけです:
(^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$)
すべてのローマ数字に一致します。空の文字列を気にしません(少なくとも1つのローマ数字が必要です)。 PCRE、Perl、PythonおよびRuby。
オンラインRuby demo: http://rubular.com/r/KLPR1zq3Hj
オンライン変換: http://www.onlineconversion.com/roman_numerals_advanced.htm
空の文字列との一致を回避するには、パターンを4回繰り返し、各0
を1
に順番に置き換え、V
、L
、およびD
を考慮する必要があります。
(M{1,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|M{0,4}(CM|C?D|D?C{1,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|M{0,4}(CM|CD|D?C{0,3})(XC|X?L|L?X{1,3})(IX|IV|V?I{0,3})|M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|I?V|V?I{1,3}))
この場合(このパターンは^
と$
を使用するため)、最初に空の行をチェックし、それらを一致させない方がよいでしょう。 単語の境界 を使用している場合、空の単語などは存在しないため、問題はありません。 (少なくとも正規表現は定義していません;哲学を始めないでください、私はここで実用的です!)
私自身の特定の(実際の)場合、Wordの末尾に一致する数字が必要でしたが、他の方法は見つかりませんでした。プレーンテキストドキュメントから脚注番号を削除する必要がありました。cl グレートバリアリーフcli"はthe Red Seacl and the Great Barrier Reefcli
に変換されていました。しかし、Tahiti
やfantastic
のような有効な単語に問題があり、Tahit
やfantasti
にスクラブされます。
幸いなことに、数値の範囲は1..3999程度に制限されています。したがって、正規表現のピースミールを作成できます。
<opt-thousands-part><opt-hundreds-part><opt-tens-part><opt-units-part>
これらの部分のそれぞれは、ローマ表記の気まぐれに対処します。たとえば、Perl表記を使用する場合:
<opt-hundreds-part> = m/(CM|DC{0,3}|CD|C{1,3})?/;
繰り返して組み立てます。
追加:<opt-hundreds-part>
はさらに圧縮できます。
<opt-hundreds-part> = m/(C[MD]|D?C{0,3})/;
'D?C {0,3}'句は何にも一致しないため、疑問符は不要です。そして、ほとんどの場合、括弧は非キャプチャー型でなければなりません-Perlでは:
<opt-hundreds-part> = m/(?:C[MD]|D?C{0,3})/;
もちろん、大文字と小文字は区別されません。
また、これを拡張して、James Curranが言及しているオプションを処理することもできます(990または999にはXMまたはIM、400にはCCCCなどを許可します)。
<opt-hundreds-part> = m/(?:[IXC][MD]|D?C{0,4})/;
import re
pattern = '^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
if re.search(pattern, 'XCCMCI'):
print 'Valid Roman'
else:
print 'Not valid Roman'
ロジックを本当に理解したい人のために、 diveintopython の3ページのステップごとの説明をご覧ください。
元のソリューション(M{0,4}
)「MMMM」は有効なローマ数字ではないことがわかったためです(古いローマ人もおそらくその巨大な数字について考えておらず、私と意見が合わないでしょう)。あなたが古いローマ人に反対している人なら、私を許して{0,4}バージョンを使ってください。
ジェレミーとパックスが上で指摘したように... '^ M {0,4}(CM | CD | D?C {0,3})(XC | XL | L?X {0,3})(IX | IV | V?I {0,3})$ 'は、あなたが求めている解決策でなければなりません...
添付されるべき特定のURL(IMHO)は http://thehazeltree.org/diveintopython/7.html です。
例7.8は{n、m}を使用した短い形式です
Steven Levithanは、この正規表現を his post で使用し、値を「脱ローマ化」する前にローマ数字を検証します。
/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/
私の場合、テキスト内のすべてのローマ数字を見つけて1つのWordに置き換えようとしたため、行の開始と終了を使用できませんでした。そのため、@ paxdiabloソリューションでは、長さゼロの一致が多数見つかりました。私は次の式になりました:
(?=\b[MCDXLVI]{1,6}\b)M{0,4}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3})
私の最後のPythonコードは次のようなものでした:
import re
text = "RULES OF LIFE: I. STAY CURIOUS; II. NEVER STOP LEARNING"
text = re.sub(r'(?=\b[MCDXLVI]{1,6}\b)M{0,4}(?:CM|CD|D?C{0,3})(?:XC|XL|L?X{0,3})(?:IX|IV|V?I{0,3})', 'ROMAN', text)
print(text)
出力:
RULES OF LIFE: ROMAN. STAY CURIOUS; ROMAN. NEVER STOP LEARNING
JeremyとPaxのソリューションの問題は、「何もない」にも一致することです。
次の正規表現では、少なくとも1つのローマ数字が必要です。
^(M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|[IDCXMLV])$