Как вы сопоставляете только действительные римские цифры с регулярным выражением?
думал о У моего друга проблема, Я решил, что даже не могу создать регулярное выражение, которое будет соответствовать римским цифрам (не говоря уже о контекстно-свободной грамматике, которая будет их генерировать)
проблема соответствия действует только римские цифры. Например, 990-это не "XM", это "CMXC"
моя проблема в создании регулярного выражения для этого заключается в том, что для того, чтобы разрешить или не разрешить определенные символы, мне нужно оглянуться назад. Давайте возьмем тысячи и сотни, для образец.
Я могу разрешить M{0,2}C?M (с учетом 900, 1000, 1900, 2000, 2900 и 3000). Однако, если матч находится на CM, я не могу позволить следующим символам быть C или D (потому что я уже на 900).
Как я могу выразить это в regex?
Если это просто не выражается в регулярном выражении, можно ли это выразить в контекстно-свободной грамматике?
10 ответов
попробуй:
^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})
те же правила, что и в предыдущем разделе, но для десятки место:
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
а также похож на предыдущие два раздела (римские цифры, несмотря на их кажущуюся странность, следуют некоторым логическим правилам, как только вы выясните, что они такое):
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".
римляне были гораздо меньше обеспокоены "правилами", чем ваш учитель третьего класса. Пока все складывалось, все было в порядке. Следовательно, " IIII "был так же хорош, как" IV " для 4. И "IIM" был полностью крут для 998.
(Если у вас есть проблемы с этим... Помните, что английское правописание не было формализовано до 1700-х годов. А до тех пор, пока читатель может это понять, это было достаточно хорошо).
чтобы избежать сопоставления пустой строки, вам нужно будет повторить шаблон четыре раза и заменить каждый 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}))
в этом случае (потому что этот шаблон использует ^
и $
) вам было бы лучше сначала проверить пустые строки и не утруждать себя их сопоставлением. Если вы используете границы слов тогда у вас нет проблем, потому что нет такой вещи, как пустое слово. (По крайней мере, regex не определяет его; не начинайте философствовать, я здесь прагматичен!)
в моем собственном конкретном (реальном) случае мне нужны были совпадающие цифры в окончаниях слов, и я не нашел другого способа обойти это. Мне нужно было стереть номера снос из моего обычного текстового документа, где текст, такой как " Красное мореcl и Большой Барьерный рифкомандной строки " был преобразован в the Red Seacl and the Great Barrier Reefcli
. Но у меня все еще были проблемы с действительными словами, такими как Tahiti
и fantastic
очищаются в Tahit
и fantasti
.
к счастью, диапазон чисел ограничен 1..3999 или около того. Таким образом, вы можете создать регулярное выражение piece-meal.
<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})/;
конечно, все это также должно быть нечувствительным к регистру.
вы также можете расширить это, чтобы иметь дело с опциями, упомянутыми Джеймсом Карраном (разрешить XM или IM для 990 или 999, и CCCC для 400 и т. д.).
<opt-hundreds-part> = m/(?:[IXC][MD]|D?C{0,4})/;
просто, чтобы сохранить его здесь:
(^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$)
соответствует всем римским цифрам. Не заботится о пустых строках (требуется хотя бы одна Римская цифра). Должен работать в PCRE, Perl, Python и Ruby.
онлайн Ruby demo:http://rubular.com/r/KLPR1zq3Hj
Онлайн Преобразование:http://www.onlineconversion.com/roman_numerals_advanced.htm
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'
для людей, которые действительно хотят понять логику, пожалуйста, посмотрите на пошаговое объяснение на 3 страницах на diveintopython.
единственное отличие от исходного решения (которое имело M{0,4}
), потому что я обнаружил, что "мммм" не является действительной римской цифрой (также старые римляне, скорее всего, не думали об этом огромном количестве и не согласятся со мной). Если вы один из disagreing старых римлян, пожалуйста, прости меня и использовать {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}
проблема решения от Джереми и Пакса заключается в том, что оно также соответствует "ничего".
следующее регулярное выражение ожидает по крайней мере одну римскую цифру:
^(M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|[IDCXMLV])$
Стивен Levithan использует этот regex в поста который проверяет римские цифры до "дероманизации" значение:
/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/
Я бы написал функции для моей работы для меня. Вот две римские числительные функции в PowerShell.
function ConvertFrom-RomanNumeral
{
<#
.SYNOPSIS
Converts a Roman numeral to a number.
.DESCRIPTION
Converts a Roman numeral - in the range of I..MMMCMXCIX - to a number.
.EXAMPLE
ConvertFrom-RomanNumeral -Numeral MMXIV
.EXAMPLE
"MMXIV" | ConvertFrom-RomanNumeral
#>
[CmdletBinding()]
[OutputType([int])]
Param
(
[Parameter(Mandatory=$true,
HelpMessage="Enter a roman numeral in the range I..MMMCMXCIX",
ValueFromPipeline=$true,
Position=0)]
[ValidatePattern("^M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$")]
[string]
$Numeral
)
Begin
{
$RomanToDecimal = [ordered]@{
M = 1000
CM = 900
D = 500
CD = 400
C = 100
XC = 90
L = 50
X = 10
IX = 9
V = 5
IV = 4
I = 1
}
}
Process
{
$roman = $Numeral + " "
$value = 0
do
{
foreach ($key in $RomanToDecimal.Keys)
{
if ($key.Length -eq 1)
{
if ($key -match $roman.Substring(0,1))
{
$value += $RomanToDecimal.$key
$roman = $roman.Substring(1)
break
}
}
else
{
if ($key -match $roman.Substring(0,2))
{
$value += $RomanToDecimal.$key
$roman = $roman.Substring(2)
break
}
}
}
}
until ($roman -eq " ")
$value
}
End
{
}
}
function ConvertTo-RomanNumeral
{
<#
.SYNOPSIS
Converts a number to a Roman numeral.
.DESCRIPTION
Converts a number - in the range of 1 to 3,999 - to a Roman numeral.
.EXAMPLE
ConvertTo-RomanNumeral -Number (Get-Date).Year
.EXAMPLE
(Get-Date).Year | ConvertTo-RomanNumeral
#>
[CmdletBinding()]
[OutputType([string])]
Param
(
[Parameter(Mandatory=$true,
HelpMessage="Enter an integer in the range 1 to 3,999",
ValueFromPipeline=$true,
Position=0)]
[ValidateRange(1,3999)]
[int]
$Number
)
Begin
{
$DecimalToRoman = @{
Ones = "","I","II","III","IV","V","VI","VII","VIII","IX";
Tens = "","X","XX","XXX","XL","L","LX","LXX","LXXX","XC";
Hundreds = "","C","CC","CCC","CD","D","DC","DCC","DCCC","CM";
Thousands = "","M","MM","MMM"
}
$column = @{Thousands = 0; Hundreds = 1; Tens = 2; Ones = 3}
}
Process
{
[int[]]$digits = $Number.ToString().PadLeft(4,"0").ToCharArray() |
ForEach-Object { [Char]::GetNumericValue($_) }
$RomanNumeral = ""
$RomanNumeral += $DecimalToRoman.Thousands[$digits[$column.Thousands]]
$RomanNumeral += $DecimalToRoman.Hundreds[$digits[$column.Hundreds]]
$RomanNumeral += $DecimalToRoman.Tens[$digits[$column.Tens]]
$RomanNumeral += $DecimalToRoman.Ones[$digits[$column.Ones]]
$RomanNumeral
}
End
{
}
}