diff --git a/string.c b/string.c index 6bb8a24313ce..74a96ddf1366 100644 --- a/string.c +++ b/string.c @@ -9845,6 +9845,81 @@ rb_str_rpartition(VALUE str, VALUE sep) RSTRING_LEN(str)-pos-RSTRING_LEN(sep))); } +/* + * call-seq: + * str.before(sep) -> str or nil + * str.before(regexp) -> str or nil + * + * Searches sep or pattern (regexp) in the string + * and returns the part before it. + * If it is not found, returns nil. + * + * "hello".before("l") #=> "he" + * "hello".before(/ll/i) #=> "he" + */ +VALUE +rb_str_before(VALUE str, VALUE sep) +{ + if (RSTRING_LEN(str) == 0) { + failed: return Qnil; + } + + sep = get_pat_quoted(sep, 0); + + if (RB_TYPE_P(sep, T_REGEXP)) { + rb_reg_search(sep, str, 0, 0); + VALUE match = rb_backref_get(); + + return rb_reg_match_pre(match); + } + + long pos = rb_str_index(str, sep, 0); + if (pos < 0) goto failed; + + return str_substr(str, 0, pos, TRUE); +} + +/* + * call-seq: + * str.after(sep) -> str or nil + * str.after(regexp) -> str or nil + * + * Searches sep or pattern (regexp) in the string + * and returns the part after it. + * If it is not found, returns nil. + * + * "hello".after("l") #=> "lo" + * "hello".after(/ll/i) #=> "o" + */ +VALUE +rb_str_after(VALUE str, VALUE sep) +{ + long len = RSTRING_LEN(str); + + if (len == 0) { + failed: return Qnil; + } + + sep = get_pat_quoted(sep, 0); + + if (RB_TYPE_P(sep, T_REGEXP)) { + rb_reg_search(sep, str, 0, 0); + VALUE match = rb_backref_get(); + + return rb_reg_match_post(match); + } + + long pos = rb_str_index(str, sep, 0); + if (pos < 0) goto failed; + + return str_substr( + str, + pos + RSTRING_LEN(sep), + len - pos, + TRUE + ); +} + /* * call-seq: * str.start_with?([prefixes]+) -> true or false @@ -11293,6 +11368,9 @@ Init_String(void) rb_define_method(rb_cString, "partition", rb_str_partition, 1); rb_define_method(rb_cString, "rpartition", rb_str_rpartition, 1); + rb_define_method(rb_cString, "before", rb_str_before, 1); + rb_define_method(rb_cString, "after", rb_str_after, 1); + rb_define_method(rb_cString, "encoding", rb_obj_encoding, 0); /* in encoding.c */ rb_define_method(rb_cString, "force_encoding", rb_str_force_encoding, 1); rb_define_method(rb_cString, "b", rb_str_b, 0); diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 507e067a0df8..35074459b6df 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -2627,6 +2627,34 @@ def (hyphen = Object.new).to_str; "-"; end assert_equal("hello", hello, bug) end + def test_before + assert_equal("hello", "hello world".before(" ")) + assert_equal(nil, "onetwothree".before("four")) + assert_equal("he", "hello world".before("l")) + assert_equal("cat", "cats and dogs".before("s")) + + assert_equal("foo", "foobarbaz".before(/bar/)) + assert_equal(nil, "cats and dogs".before(/pigs/)) + assert_equal("he", "hello".before(/ll/)) + assert_equal("", "hello".before(//)) + + assert_equal(nil, "".before("")) + end + + def test_after + assert_equal("world", "hello world".after(" ")) + assert_equal(nil, "onetwothree".after("four")) + assert_equal("lo world", "hello world".after("l")) + assert_equal(" and dogs", "cats and dogs".after("s")) + + assert_equal("baz", "foobarbaz".after(/bar/)) + assert_equal(nil, "cats and dogs".after(/pigs/)) + assert_equal("o", "hello".after(/ll/)) + assert_equal("hello", "hello".after(//)) + + assert_equal(nil, "".after("")) + end + def test_setter assert_raise(TypeError) { $/ = 1 } name = "\u{5206 884c}"