Javascript 如何创建检查罗马数字的正则表达式?

Javascript 如何创建检查罗马数字的正则表达式?,javascript,regex,roman-numerals,Javascript,Regex,Roman Numerals,我需要创建正则表达式来验证用户输入是否: 4位数字或 类似XXXXXX-YY的值,其中X是从I到XXXIII的罗马数字,YY是两个拉丁字符(A-Z) 那么,与I和XXXIII之间的罗马数字匹配的部分是: (?:X(?:X(?:V(?:I(?:I?I)?)?|X(?:I(?:I?I)?)?|I(?:[VX]|I?I)?)?|V(?:I(?:I?I)?)?|I(?:[VX]|I?I)?)?|V(?:I(?:I?I)?)?|I(?:[VX]|I?I)?) 据透露, #!/usr/bin/env p

我需要创建正则表达式来验证用户输入是否:

  • 4位数字或
  • 类似XXXXXX-YY的值,其中X是从I到XXXIII的罗马数字,YY是两个拉丁字符(A-Z)

那么,与I和XXXIII之间的罗马数字匹配的部分是:

(?:X(?:X(?:V(?:I(?:I?I)?)?|X(?:I(?:I?I)?)?|I(?:[VX]|I?I)?)?|V(?:I(?:I?I)?)?|I(?:[VX]|I?I)?)?|V(?:I(?:I?I)?)?|I(?:[VX]|I?I)?)
据透露,

#!/usr/bin/env perl
use Regexp::Assemble;
use Roman;

my $ra = new Regexp::Assemble;

for my $num (1..33) {
    $ra->add(Roman($num));
} 

print $ra->re, "\n";
这将匹配一个4位输入,或一个罗马数字(范围1-33),后跟一个破折号和两个字母

为了解释正则表达式,下面是带有注释的扩展源代码:

// Test for a 4-digit number
(                                       // Start required capturing group
    ^                                   // Start of string
    [0-9]{4}                            // Test for 0-9, exactly 4 times
    $                                   // End of string
)                                       // End required capturing group
|                                       // OR
// Test for Roman Numerals, 1 - 33, followed by a dash and two letters
(                                       // Start required capturing group
    ^                                   // Start of string
    (?:                                 // Start required non-capturing group
        // Test for 1 - 29
        (?:                             // Start required non-capturing group
            // Test for 10, 20, (and implied 0, although the Romans did not have a digit, or mathematical concept, for 0)
            [X]{0,2}                    // X, optionally up to 2 times
            (?:                         // Start required non-capturing group
                // Test for 1 - 4, and 9
                [I]                     // I, exactly once (I = 1)
                (?:                     // Start optional non-capturing group
                    // IV = 4, IX = 9
                    [XV]?               // Optional X or V, exactly once
                    |                   // OR
                    // II = 2, III = 3
                    [I]{0,2}            // Optional I, up to 2 times
                )?                      // End optional non-capturing group
                |                       // OR
                // Test for 5 - 8
                (?:                     // Start optional non-capturing group
                    [V][I]{0,3}         // Required V, followed by optional I, up to 3 times
                )?                      // End optional non-capturing group
            )                           // End required non-capturing group
        )                               // End required non-capturing group
        |                               // OR
        // Test for 30 - 33
        (?:                             // Start required non-capturing group
            // Test for 30
            [X]{3}                      // X exactly 3 times
            // Test for 1 - 3
            [I]{0,3}                    // Optional I, up to 3 times
        )                               // End required non-capturing group
    )                                   // End required non-capturing group
    // Test for dash and two letters
    \-                                  // Literal -, exactly 1 time
    [A-Z]{2}                            // Alphabetic character, exactly 2 times
    $                                   // End of string
)                                       // End required capturing group
4位数字和尾随的
\-[A-Z]{2}
对我来说是不言而喻的。我使用罗马数字的方法是:

  • 打开Excel以1-33填充列
  • 将该列转换为罗马数字(共有7种不同变体)
  • 检查是否有任何品种与1-33不同(它们不是)
  • 将罗马数字移动到最小数量的唯一模式中,将其限制为33(即,“然后你要数到三十三,不多也不少。你要数到三十三,数到三十三。你不能数到三十四,也不能数到三十二,除非你接着数到三十三。三十五是正确的。”)
  • 意识到多达39个是单一模式(
    ^([X]{0,3}([I]([XV]?|[I]{0,2});([V][I]{0,3})))$
    ,为了更清晰,更改为捕获组)
  • 更改模式以允许最多29个
  • 增加了另一个允许30到39
  • 构建整个模式并在RegexBuddy(这是一个非常有用的工具)中测试数字0-20000和罗马数字1-150,后跟“-AA”
  • 这个模式奏效了,所以我把它贴了出来(然后又拿了一杯咖啡,自己给了一个“阿塔男孩”来完成我认为是一个可爱的周六早上挑战)

  • 通过额外的括号,我假设您指的是非捕获组
    (?:…)
    。我经常使用这些来对事物进行分组(这里分组是非常必要的)。我将它们设置为非捕获,因为我不需要捕获子组,只需要捕获父组(在这个用例中,我认为它们实际上也不需要被捕获,但这样做并没有坏处)。通过使它们不被捕获,它们不会创建加快处理速度的反向引用(尽管对于单个输入,所获得的时间可以忽略不计).

    根据要求,这些是可能的罗马数字格式。为便于阅读,仅显示最大X数

    说明:

    ^                Begin of string
    (                Begin of group 1.
      \d{4}             4 digits
    
    |                 OR
    
      (?=[IVX])         Look-ahead: Must be followed by a I, V or X
      (                  Begin of group 2.
         X{0,3}I{0,3}       = 0 1 2 3  + { 0 ; 10 ; 20 ; 30} (roman)
      |                  OR
         X{0,2}VI{0,3}      = 5 6 7 8  + { 0 ; 10 ; 20 }     (roman)
      |                  OR
         X{0,2}I?[VX]       = 4 9      + { 0 ; 10 ; 20 }     (roman)
      )                  End of group 2
      -[A-Z]{2}          Postfixed by a hyphen and two letters
    )                 End of group 1.
    $                End of string
    

    请看另一个问题:@RobW,它可以是1到6个字符,因为期望值是从I到XXXIII(即从1到33)。您是如何构造该模式的,以及所有无关的括号是什么?不,实际上,我是说,您为什么要编写
    [V][I]{0,3}
    而不是
    VI{0,3}
    。另外,您使用了错误的注释字符:regex require
    #
    。哦,等等,这是Javascript,禁止您使用
    /x
    (?x)
    mode.Javascript的正则表达式是所有语言中最差的。非常糟糕。XRegExp插件有点帮助。我手工构建了这个模式。确切的方法现在在文章中。哦,因为我在编写正则表达式时倾向于在字符类中思考。我想你是对的,它可能更简单。我是originally把它们写成
    [vV][iI]{0,3}
    ,然后添加了不区分大小写的开关(并删除了小写匹配)。哇,这太令人印象深刻了!需要比技术更多的思考。
    XXX III     (or: <empty>, I or II instead of III)
    XX V       (or: IV, IX and X instead of IV)
    
    /^(\d{4}|(?=[IVX])(X{0,3}I{0,3}|X{0,2}VI{0,3}|X{0,2}I?[VX])-[A-Z]{2})$/i
    
    ^                Begin of string
    (                Begin of group 1.
      \d{4}             4 digits
    
    |                 OR
    
      (?=[IVX])         Look-ahead: Must be followed by a I, V or X
      (                  Begin of group 2.
         X{0,3}I{0,3}       = 0 1 2 3  + { 0 ; 10 ; 20 ; 30} (roman)
      |                  OR
         X{0,2}VI{0,3}      = 5 6 7 8  + { 0 ; 10 ; 20 }     (roman)
      |                  OR
         X{0,2}I?[VX]       = 4 9      + { 0 ; 10 ; 20 }     (roman)
      )                  End of group 2
      -[A-Z]{2}          Postfixed by a hyphen and two letters
    )                 End of group 1.
    $                End of string