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?

137
demandé sur Community 2008-11-06 04:29:25

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
293
répondu paxdiablo 2010-05-12 02:19:48

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).

19
répondu James Curran 2008-11-06 04:09:32

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 .

11
répondu Corin 2012-05-04 02:00:34

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})/;
7
répondu Jonathan Leffler 2008-11-07 07:03:54

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

7
répondu smileart 2016-04-12 14:33:52
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.

3
répondu Salvador Dali 2014-10-31 07:18:16

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}

1
répondu Jonathan Leffler 2008-11-06 04:08:16

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])$
0
répondu Marvin Frommhold 2011-03-16 14:12:41

Steven Levithan utilise ce regex dans son post qui valide les chiffres romains avant de "deromaniser" la valeur:

/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/
0
répondu Mottie 2014-06-22 19:26:33

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
    {
    }
}
-1
répondu Vince Ypma 2015-01-01 12:05:16