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:
parent
6af650256b
commit
10082360b9
3 changed files with 165 additions and 0 deletions
2
NEWS
2
NEWS
|
@ -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)
|
||||
|
||||
|
|
68
string.c
68
string.c
|
@ -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);
|
||||
|
|
|
@ -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\)/)
|
||||
|
|
Loading…
Add table
Reference in a new issue