Preview Ruby 4.1 is a preview. It can change before the final release. Switch to Ruby 4.0 →
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;
rb_len_t 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;
const char *ptr = RSTRING_PTR(str);
const char *const str_start = ptr;
const rb_len_t str_len = RSTRING_LEN(str);
const char *const eptr = str_start + str_len;
if (split_type == SPLIT_TYPE_AWK) {
const 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) {
const char *substr_start = ptr;
const char *sptr = RSTRING_PTR(spat);
rb_len_t 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 */
const 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();
rb_len_t len = RSTRING_LEN(str);
rb_len_t start = beg;
int idx;
int last_null = 0;
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);
end = RMATCH_BEG(match, 0);
if (start == end && RMATCH_BEG(match, 0) == RMATCH_END(match, 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 = RMATCH_END(match, 0);
}
last_null = 0;
for (idx = 1; idx < RMATCH_NREGS(match); idx++) {
if (RMATCH_BEG(match, idx) == -1) continue;
SPLIT_STR(RMATCH_BEG(match, idx), RMATCH_END(match, idx) - RMATCH_BEG(match, 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('') # => ["こ", "ん", "に", "ち", "は"]
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('に') # => ["こん", "ちは"]
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 included:
'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]
-