Comment associer seulement des chiffres romains valides avec une expression régulière?
en pensant à mon autre problème , j'ai décidé que je ne pouvais même pas créer une expression régulière qui correspondrait aux chiffres romains (et encore moins une grammaire sans contexte qui les générerait)
le problème est de ne faire correspondre que des chiffres romains valides. Par exemple, 990 n'est pas "XM", C'est "CMXC "
mon problème en faisant le regex pour ceci est que pour permettre ou non certains caractères, je dois regarder en arrière. Prenons des milliers et des centaines, par exemple.
je peux laisser les M{0,2}C?M (pour tenir compte de 900, 1000, 1900, 2000, 2900 et 3000). Cependant, si la correspondance est sur CM, Je ne peux pas permettre que les caractères suivants soient C ou d (Parce que je suis déjà à 900).
Comment puis-je l'exprimer dans un regex?
Si ce n'est pas exprimable dans une regex, c'est exprimable dans un contexte de grammaire?
10 réponses
, Essayez:
^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$
en le décomposant:
M{0,4}
spécifie la section des milliers et la limite essentiellement entre 0
et 4000
. C'est relativement simple:
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})
Un peu plus complexe, c'est pour la section des centaines et couvre toutes les possibilités:
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})
mêmes règles que dans la section précédente mais pour le lieu des tens:
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})
Ceci est la section des unités, manipulant 0
à travers 9
et aussi similaires aux deux sections précédentes (les chiffres romains, malgré leur étrangeté apparente, suivent quelques règles logiques une fois que vous comprenez ce qu'ils sont):
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
en fait, votre prémisse est défectueuse. 990 est " XM", ainsi que"CMXC".
les Romains se souciaient beaucoup moins des" règles " que votre professeur de CE2. Tant que ça tenait debout, C'était OK. Donc " IIII "était aussi bon que" IV " pour 4. Et "IIM" était complètement cool pour 998.
(si vous avez du mal à gérer cela... Rappelez-vous que l'orthographe anglaise n'a pas été officialisée avant les années 1700. D'ici là, aussi longtemps que le lecteur pourrait comprendre, c'était assez bon).
pour éviter de correspondre à la chaîne vide, vous aurez besoin de répéter le modèle quatre fois et remplacer chaque 0
par un 1
à tour de rôle, et de rendre compte de V
, L
et 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}))
dans ce cas (parce que ce modèle utilise ^
et $
), vous feriez mieux de vérifier d'abord les lignes vides et de ne pas prendre la peine de les apparier. Si vous utilisez limites de mots alors vous Je n'ai pas de problème parce qu'il n'y a pas de mot vide. (Au moins regex n'en définit pas un; ne commencez pas à philosopher, je suis pragmatique ici!)
dans mon propre cas (monde réel), j'avais besoin de chiffres de correspondance à la fin des mots et je n'ai trouvé aucun autre moyen de les contourner. J'ai dû enlever les numéros de note de bas de page de mon document de texte simple, où le texte comme "la mer Rouge cl et la Grande Barrière de corail cli " avait été converti en the Red Seacl and the Great Barrier Reefcli
. Mais j'ai toujours eu des problèmes avec les mots valides comme Tahiti
et fantastic
sont lavés en Tahit
et fantasti
.
heureusement, l'éventail des nombres est limité à 1..3999 ou à peu près. Par conséquent, vous pouvez construire la regex pièce-repas.
<opt-thousands-part><opt-hundreds-part><opt-tens-part><opt-units-part>
chacune de ces parties traitera des caprices de la notation Romaine. Par exemple, en utilisant la notation Perl:
<opt-hundreds-part> = m/(CM|DC{0,3}|CD|C{1,3})?/;
répéter et assembler.
ajouté : le <opt-hundreds-part>
peut être comprimé plus loin:
<opt-hundreds-part> = m/(C[MD]|D?C{0,3})/;
depuis le 'D?La clause C {0,3} ' ne correspond à rien, il n'y a pas besoin de point d'interrogation. Et, très probablement, les parenthèses devraient être la non - capture type-in Perl:
<opt-hundreds-part> = m/(?:C[MD]|D?C{0,3})/;
bien sûr, tout cela devrait être insensible à la casse, aussi.
vous pouvez également étendre cela pour traiter les options mentionnées par James Curran (pour autoriser XM ou IM pour 990 ou 999, et CCCC pour 400, etc).
<opt-hundreds-part> = m/(?:[IXC][MD]|D?C{0,4})/;
Juste pour l'enregistrer ici:
(^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$)
correspond à tous les chiffres romains. Ne se soucie pas des chaînes vides (nécessite au moins une lettre en chiffres romains). Devrait fonctionner en PCRE, Perl, Python et Ruby.
en Ligne Ruby démo: http://rubular.com/r/KLPR1zq3Hj
conversion en ligne: 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'
pour les gens qui veulent vraiment comprendre la logique, s'il vous plaît jeter un oeil à une explication étape par étape sur 3 pages sur diveintopython .
la seule différence par rapport à la solution originale (qui avait M{0,4}
) est que j'ai trouvé que "MMMM" n'est pas un chiffre romain valide (aussi les vieux Romains n'ont probablement pas pensé à ce nombre énorme et seront en désaccord avec moi). Si vous n'aimez pas les vieux Romains, veuillez pardonner. moi et utiliser {0,4} version.
comme Jeremy et Pax l'ont souligné plus haut ... '^M{0,4}(CM|CD|D?C{0,3}) (XC|XL|L?X{0,3}) (IX|IV|V?Je devrais être la solution que vous cherchez ...
L'URL spécifique qui aurait dû être jointe (IMHO) est http://thehazeltree.org/diveintopython/7.html
exemple 7.8 est la forme abrégée utilisant {n, m}
le problème de la solution de Jeremy et Pax est, qu'il ne correspond aussi"rien".
le regex suivant attend au moins un chiffre romain:
^(M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})|[IDCXMLV])$
j'écrirais des fonctions à mon travail pour moi. Voici deux fonctions en chiffres romains dans 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
{
}
}