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}"