1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

string.c: add String#delete_prefix and String#delete_prefix!

to remove leading substr [Feature #12694] [fix GH-1632]

* string.c (rb_str_delete_prefix_bang): add a new method
  to remove prefix destuctively.

* string.c (rb_str_delete_prefix): add a new method
  to remove prefix non-destuctively.

* test/ruby/test_string.rb: add tests.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59132 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
sonots 2017-06-21 07:43:26 +00:00
parent 6af650256b
commit 10082360b9
3 changed files with 165 additions and 0 deletions

2
NEWS
View file

@ -85,6 +85,8 @@ with all sufficient information, see the ChangeLog file or Redmine
(same as "literal".freeze in Ruby 2.1+) [Feature #13295]
* String#{casecmp,casecmp?} now return nil for non-string arguments
instead of raising a TypeError. [Bug #13312]
* String##delete_prefix is added to remove prefix [Feature #12694]
* String#delete_prefix! is added to remove prefix destructively [Feature #12694]
=== Stdlib updates (outstanding ones only)

View file

@ -9191,6 +9191,72 @@ rb_str_end_with(int argc, VALUE *argv, VALUE str)
return Qfalse;
}
static long
deleted_prefix_length(VALUE str, VALUE prefix)
{
char *strptr, *prefixptr;
long olen, prefixlen;
StringValue(prefix);
if (is_broken_string(prefix)) return 0;
rb_enc_check(str, prefix);
/* return 0 if not start with prefix */
prefixlen = RSTRING_LEN(prefix);
if (prefixlen <= 0) return 0;
olen = RSTRING_LEN(str);
if (olen < prefixlen) return 0;
strptr = RSTRING_PTR(str);
prefixptr = RSTRING_PTR(prefix);
if (memcmp(strptr, prefixptr, prefixlen) != 0) return 0;
return prefixlen;
}
/*
* call-seq:
* str.delete_prefix!(prefix) -> self or nil
*
* Deletes leading <code>prefix</code> from <i>str</i>, returning
* <code>nil</code> if no change was made.
*
* "hello".delete_prefix!("hel") #=> "lo"
* "hello".delete_prefix!("llo") #=> nil
*/
static VALUE
rb_str_delete_prefix_bang(VALUE str, VALUE prefix)
{
long prefixlen;
str_modify_keep_cr(str);
prefixlen = deleted_prefix_length(str, prefix);
if (prefixlen <= 0) return Qnil;
return rb_str_drop_bytes(str, prefixlen);
}
/*
* call-seq:
* str.delete_prefix(prefix) -> new_str
*
* Returns a copy of <i>str</i> with leading <code>prefix</code> deleted.
*
* "hello".delete_prefix("hel") #=> "lo"
* "hello".delete_prefix("llo") #=> "hello"
*/
static VALUE
rb_str_delete_prefix(VALUE str, VALUE prefix)
{
long prefixlen;
prefixlen = deleted_prefix_length(str, prefix);
if (prefixlen <= 0) return rb_str_dup(str);
return rb_str_subseq(str, prefixlen, RSTRING_LEN(str) - prefixlen);
}
void
rb_str_setter(VALUE val, ID id, VALUE *var)
{
@ -10346,6 +10412,7 @@ Init_String(void)
rb_define_method(rb_cString, "strip", rb_str_strip, 0);
rb_define_method(rb_cString, "lstrip", rb_str_lstrip, 0);
rb_define_method(rb_cString, "rstrip", rb_str_rstrip, 0);
rb_define_method(rb_cString, "delete_prefix", rb_str_delete_prefix, 1);
rb_define_method(rb_cString, "sub!", rb_str_sub_bang, -1);
rb_define_method(rb_cString, "gsub!", rb_str_gsub_bang, -1);
@ -10354,6 +10421,7 @@ Init_String(void)
rb_define_method(rb_cString, "strip!", rb_str_strip_bang, 0);
rb_define_method(rb_cString, "lstrip!", rb_str_lstrip_bang, 0);
rb_define_method(rb_cString, "rstrip!", rb_str_rstrip_bang, 0);
rb_define_method(rb_cString, "delete_prefix!", rb_str_delete_prefix_bang, 1);
rb_define_method(rb_cString, "tr", rb_str_tr, 2);
rb_define_method(rb_cString, "tr_s", rb_str_tr_s, 2);

View file

@ -2416,6 +2416,101 @@ CODE
assert_equal("\u3042", s4)
end
def test_delete_prefix
assert_raise(TypeError) { 'hello'.delete_prefix(nil) }
assert_raise(TypeError) { 'hello'.delete_prefix(1) }
assert_raise(TypeError) { 'hello'.delete_prefix(/hel/) }
s = S("hello")
assert_equal("lo", s.delete_prefix('hel'))
assert_equal("hello", s)
s = S("hello")
assert_equal("hello", s.delete_prefix('lo'))
assert_equal("hello", s)
s = S("\u{3053 3093 306b 3061 306f}")
assert_equal("\u{306b 3061 306f}", s.delete_prefix("\u{3053 3093}"))
assert_equal("\u{3053 3093 306b 3061 306f}", s)
s = S("\u{3053 3093 306b 3061 306f}")
assert_equal("\u{3053 3093 306b 3061 306f}", s.delete_prefix('hel'))
assert_equal("\u{3053 3093 306b 3061 306f}", s)
s = S("hello")
assert_equal("hello", s.delete_prefix("\u{3053 3093}"))
assert_equal("hello", s)
# skip if argument is a broken string
s = S("\xe3\x81\x82")
assert_equal("\xe3\x81\x82", s.delete_prefix("\xe3"))
assert_equal("\xe3\x81\x82", s)
# clear coderange
s = S("\u{3053 3093}hello")
assert_not_predicate(s, :ascii_only?)
assert_predicate(s.delete_prefix("\u{3053 3093}"), :ascii_only?)
# argument should be converted to String
klass = Class.new {|klass| def to_str; 'a'; end }
s = S("abba")
assert_equal("bba", s.delete_prefix(klass.new))
assert_equal("abba", s)
end
def test_delete_prefix_bang
assert_raise(TypeError) { 'hello'.delete_prefix!(nil) }
assert_raise(TypeError) { 'hello'.delete_prefix!(1) }
assert_raise(TypeError) { 'hello'.delete_prefix!(/hel/) }
s = S("hello")
assert_equal("lo", s.delete_prefix!('hel'))
assert_equal("lo", s)
s = S("hello")
assert_equal(nil, s.delete_prefix!('lo'))
assert_equal("hello", s)
s = S("\u{3053 3093 306b 3061 306f}")
assert_equal("\u{306b 3061 306f}", s.delete_prefix!("\u{3053 3093}"))
assert_equal("\u{306b 3061 306f}", s)
s = S("\u{3053 3093 306b 3061 306f}")
assert_equal(nil, s.delete_prefix!('hel'))
assert_equal("\u{3053 3093 306b 3061 306f}", s)
s = S("hello")
assert_equal(nil, s.delete_prefix!("\u{3053 3093}"))
assert_equal("hello", s)
# skip if argument is a broken string
s = S("\xe3\x81\x82")
assert_equal(nil, s.delete_prefix!("\xe3"))
assert_equal("\xe3\x81\x82", s)
# clear coderange
s = S("\u{3053 3093}hello")
assert_not_predicate(s, :ascii_only?)
assert_predicate(s.delete_prefix!("\u{3053 3093}"), :ascii_only?)
# argument should be converted to String
klass = Class.new {|klass| def to_str; 'a'; end }
s = S("abba")
assert_equal("bba", s.delete_prefix!(klass.new))
assert_equal("bba", s)
s = S("ax").freeze
assert_raise_with_message(RuntimeError, /frozen/) {s.delete_prefix!("a")}
s = S("ax")
o = Struct.new(:s).new(s)
def o.to_str
s.freeze
"a"
end
assert_raise_with_message(RuntimeError, /frozen/) {s.delete_prefix!(o)}
end
=begin
def test_symbol_table_overflow
assert_in_out_err([], <<-INPUT, [], /symbol table overflow \(symbol [a-z]{8}\) \(RuntimeError\)/)