String#split
static VALUE
rb_str_split_m(int argc, VALUE *argv, VALUE str)
{
rb_encoding *enc;
VALUE spat;
VALUE limit;
split_type_t split_type;
long beg, end, i = 0, empty_count = -1;
int lim = 0;
VALUE result, tmp;
result = rb_block_given_p() ? Qfalse : Qnil;
if (rb_scan_args(argc, argv, "02", &spat, &limit) == 2) {
lim = NUM2INT(limit);
if (lim <= 0) limit = Qnil;
else if (lim == 1) {
if (RSTRING_LEN(str) == 0)
return result ? rb_ary_new2(0) : str;
tmp = str_duplicate(rb_cString, str);
if (!result) {
rb_yield(tmp);
return str;
}
return rb_ary_new3(1, tmp);
}
i = 1;
}
if (NIL_P(limit) && !lim) empty_count = 0;
enc = STR_ENC_GET(str);
split_type = SPLIT_TYPE_REGEXP;
if (!NIL_P(spat)) {
spat = get_pat_quoted(spat, 0);
}
else if (NIL_P(spat = rb_fs)) {
split_type = SPLIT_TYPE_AWK;
}
else if (!(spat = rb_fs_check(spat))) {
rb_raise(rb_eTypeError, "value of $; must be String or Regexp");
}
else {
rb_category_warn(RB_WARN_CATEGORY_DEPRECATED, "$; is set to non-nil value");
}
if (split_type != SPLIT_TYPE_AWK) {
switch (BUILTIN_TYPE(spat)) {
case T_REGEXP:
rb_reg_options(spat); /* check if uninitialized */
tmp = RREGEXP_SRC(spat);
split_type = literal_split_pattern(tmp, SPLIT_TYPE_REGEXP);
if (split_type == SPLIT_TYPE_AWK) {
spat = tmp;
split_type = SPLIT_TYPE_STRING;
}
break;
case T_STRING:
mustnot_broken(spat);
split_type = literal_split_pattern(spat, SPLIT_TYPE_STRING);
break;
default:
UNREACHABLE_RETURN(Qnil);
}
}
#define SPLIT_STR(beg, len) ( \
empty_count = split_string(result, str, beg, len, empty_count), \
str_mod_check(str, str_start, str_len))
beg = 0;
char *ptr = RSTRING_PTR(str);
char *const str_start = ptr;
const long str_len = RSTRING_LEN(str);
char *const eptr = str_start + str_len;
if (split_type == SPLIT_TYPE_AWK) {
char *bptr = ptr;
int skip = 1;
unsigned int c;
if (result) result = rb_ary_new();
end = beg;
if (is_ascii_string(str)) {
while (ptr < eptr) {
c = (unsigned char)*ptr++;
if (skip) {
if (ascii_isspace(c)) {
beg = ptr - bptr;
}
else {
end = ptr - bptr;
skip = 0;
if (!NIL_P(limit) && lim <= i) break;
}
}
else if (ascii_isspace(c)) {
SPLIT_STR(beg, end-beg);
skip = 1;
beg = ptr - bptr;
if (!NIL_P(limit)) ++i;
}
else {
end = ptr - bptr;
}
}
}
else {
while (ptr < eptr) {
int n;
c = rb_enc_codepoint_len(ptr, eptr, &n, enc);
ptr += n;
if (skip) {
if (rb_isspace(c)) {
beg = ptr - bptr;
}
else {
end = ptr - bptr;
skip = 0;
if (!NIL_P(limit) && lim <= i) break;
}
}
else if (rb_isspace(c)) {
SPLIT_STR(beg, end-beg);
skip = 1;
beg = ptr - bptr;
if (!NIL_P(limit)) ++i;
}
else {
end = ptr - bptr;
}
}
}
}
else if (split_type == SPLIT_TYPE_STRING) {
char *substr_start = ptr;
char *sptr = RSTRING_PTR(spat);
long slen = RSTRING_LEN(spat);
if (result) result = rb_ary_new();
mustnot_broken(str);
enc = rb_enc_check(str, spat);
while (ptr < eptr &&
(end = rb_memsearch(sptr, slen, ptr, eptr - ptr, enc)) >= 0) {
/* Check we are at the start of a char */
char *t = rb_enc_right_char_head(ptr, ptr + end, eptr, enc);
if (t != ptr + end) {
ptr = t;
continue;
}
SPLIT_STR(substr_start - str_start, (ptr+end) - substr_start);
str_mod_check(spat, sptr, slen);
ptr += end + slen;
substr_start = ptr;
if (!NIL_P(limit) && lim <= ++i) break;
}
beg = ptr - str_start;
}
else if (split_type == SPLIT_TYPE_CHARS) {
int n;
if (result) result = rb_ary_new_capa(RSTRING_LEN(str));
mustnot_broken(str);
enc = rb_enc_get(str);
while (ptr < eptr &&
(n = rb_enc_precise_mbclen(ptr, eptr, enc)) > 0) {
SPLIT_STR(ptr - str_start, n);
ptr += n;
if (!NIL_P(limit) && lim <= ++i) break;
}
beg = ptr - str_start;
}
else {
if (result) result = rb_ary_new();
long len = RSTRING_LEN(str);
long start = beg;
long idx;
int last_null = 0;
struct re_registers *regs;
VALUE match = 0;
for (; rb_reg_search(spat, str, start, 0) >= 0;
(match ? (rb_match_unbusy(match), rb_backref_set(match)) : (void)0)) {
match = rb_backref_get();
if (!result) rb_match_busy(match);
regs = RMATCH_REGS(match);
end = BEG(0);
if (start == end && BEG(0) == END(0)) {
if (!ptr) {
SPLIT_STR(0, 0);
break;
}
else if (last_null == 1) {
SPLIT_STR(beg, rb_enc_fast_mbclen(ptr+beg, eptr, enc));
beg = start;
}
else {
if (start == len)
start++;
else
start += rb_enc_fast_mbclen(ptr+start,eptr,enc);
last_null = 1;
continue;
}
}
else {
SPLIT_STR(beg, end-beg);
beg = start = END(0);
}
last_null = 0;
for (idx=1; idx < regs->num_regs; idx++) {
if (BEG(idx) == -1) continue;
SPLIT_STR(BEG(idx), END(idx)-BEG(idx));
}
if (!NIL_P(limit) && lim <= ++i) break;
}
if (match) rb_match_unbusy(match);
}
if (RSTRING_LEN(str) > 0 && (!NIL_P(limit) || RSTRING_LEN(str) > beg || lim < 0)) {
SPLIT_STR(beg, RSTRING_LEN(str)-beg);
}
return result ? result : str;
}
Creates an array of substrings by splitting self at each occurrence of the given field separator field_sep.
With no arguments given, splits using the field separator $;, whose default value is nil.
With no block given, returns the array of substrings:
'abracadabra'.split('a') # => ["", "br", "c", "d", "br"]
When field_sep is nil or ' ' (a single space), splits at each sequence of whitespace:
'foo bar baz'.split(nil) # => ["foo", "bar", "baz"] 'foo bar baz'.split(' ') # => ["foo", "bar", "baz"] "foo \n\tbar\t\n baz".split(' ') # => ["foo", "bar", "baz"] 'foo bar baz'.split(' ') # => ["foo", "bar", "baz"] ''.split(' ') # => []
When field_sep is an empty string, splits at every character:
'abracadabra'.split('') # => ["a", "b", "r", "a", "c", "a", "d", "a", "b", "r", "a"] ''.split('') # => [] 'тест'.split('') # => ["т", "е", "с", "т"] 'こんにちは'.split('') # => ["こ", "ん", "に", "ち", "は"]
When field_sep is a non-empty string and different from ' ' (a single space), uses that string as the separator:
'abracadabra'.split('a') # => ["", "br", "c", "d", "br"] 'abracadabra'.split('ab') # => ["", "racad", "ra"] ''.split('a') # => [] 'тест'.split('т') # => ["", "ес"] 'こんにちは'.split('に') # => ["こん", "ちは"]
When field_sep is a Regexp, splits at each occurrence of a matching substring:
'abracadabra'.split(/ab/) # => ["", "racad", "ra"] '1 + 1 == 2'.split(/\W+/) # => ["1", "1", "2"] 'abracadabra'.split(//) # => ["a", "b", "r", "a", "c", "a", "d", "a", "b", "r", "a"]
If the Regexp contains groups, their matches are included in the returned array:
'1:2:3'.split(/(:)()()/, 2) # => ["1", ":", "", "", "2:3"]
Argument limit sets a limit on the size of the returned array; it also determines whether trailing empty strings are included in the returned array.
When limit is zero, there is no limit on the size of the array, but trailing empty strings are omitted:
'abracadabra'.split('', 0) # => ["a", "b", "r", "a", "c", "a", "d", "a", "b", "r", "a"] 'abracadabra'.split('a', 0) # => ["", "br", "c", "d", "br"] # Empty string after last 'a' omitted.
When limit is a positive integer, there is a limit on the size of the array (no more than n - 1 splits occur), and trailing empty strings are included:
'abracadabra'.split('', 3) # => ["a", "b", "racadabra"] 'abracadabra'.split('a', 3) # => ["", "br", "cadabra"] 'abracadabra'.split('', 30) # => ["a", "b", "r", "a", "c", "a", "d", "a", "b", "r", "a", ""] 'abracadabra'.split('a', 30) # => ["", "br", "c", "d", "br", ""] 'abracadabra'.split('', 1) # => ["abracadabra"] 'abracadabra'.split('a', 1) # => ["abracadabra"]
When limit is negative, there is no limit on the size of the array, and trailing empty strings are omitted:
'abracadabra'.split('', -1) # => ["a", "b", "r", "a", "c", "a", "d", "a", "b", "r", "a", ""] 'abracadabra'.split('a', -1) # => ["", "br", "c", "d", "br", ""]
If a block is given, it is called with each substring and returns self:
'foo bar baz'.split(' ') {|substring| p substring }
Output :
"foo" "bar" "baz"
Note that the above example is functionally equivalent to:
'foo bar baz'.split(' ').each {|substring| p substring }
Output :
"foo" "bar" "baz"
But the latter:
-
Has poorer performance because it creates an intermediate array.
-
Returns an array (instead of
self).
Related: see Converting to Non-String.
Ruby version changes
-
3.0
-
The following methods now return or yield
Stringinstances instead of subclass instances when called on subclass instances: [Bug #10845]
-