Index: re.c =================================================================== --- re.c (revision 27799) +++ re.c (working copy) @@ -725,6 +725,28 @@ return hash; } +/* + * call-seq: + * rxp.ncaptures => fixnum + * + * Returns number of captures of rxp. + * + * /foo/.ncaptures #=> 0 + * /bar(baz)/.ncaptures #=> 1 + * /bar(b)a(z)/.ncaptures #=> 2 + * /bar(b(az))/.ncaptures #=> 2 + * /bar(?:baz)/.ncaptures #=> 0 + * /(?foo)bar(baz)/.ncaptures #=> 1 + */ + +static VALUE +rb_reg_ncaptures(VALUE re) +{ + regex_t *reg = RREGEXP(re)->ptr; + int ncapts = onig_number_of_captures(reg); + return INT2NUM(ncapts); +} + static int onig_new_with_source(regex_t** reg, const UChar* pattern, const UChar* pattern_end, OnigOptionType option, OnigEncoding enc, const OnigSyntaxType* syntax, @@ -3519,6 +3541,7 @@ rb_define_method(rb_cRegexp, "fixed_encoding?", rb_reg_fixed_encoding_p, 0); rb_define_method(rb_cRegexp, "names", rb_reg_names, 0); rb_define_method(rb_cRegexp, "named_captures", rb_reg_named_captures, 0); + rb_define_method(rb_cRegexp, "ncaptures", rb_reg_ncaptures, 0); rb_define_const(rb_cRegexp, "IGNORECASE", INT2FIX(ONIG_OPTION_IGNORECASE)); rb_define_const(rb_cRegexp, "EXTENDED", INT2FIX(ONIG_OPTION_EXTEND)); Index: test/ruby/test_regexp.rb =================================================================== --- test/ruby/test_regexp.rb (revision 27799) +++ test/ruby/test_regexp.rb (working copy) @@ -816,4 +816,13 @@ bug2547 = '[ruby-core:27374]' assert_raise(SyntaxError, bug2547) {eval('/#{"\\\\"}y/')} end + + def test_ncaptures + assert_equal(0, /foo/.ncaptures) + assert_equal(1, /bar(baz)/.ncaptures) + assert_equal(2, /bar(b)a(z)/.ncaptures) + assert_equal(2, /bar(b(az))/.ncaptures) + assert_equal(0, /bar(?:baz)/.ncaptures) + assert_equal(1, /(?foo)bar(baz)/.ncaptures) + end end