class StringScanner - strscan: Ruby Standard Library Documentation

# class StringScanner

### Public Class Methods

new(string, fixed_anchor: false) → string_scanner click to toggle source

Returns a new `StringScanner` object whose [stored string] is the given `string`; sets the [fixed-anchor property]:

    scanner = StringScanner.new('foobarbaz')
    scanner.string        # => "foobarbaz"
    scanner.fixed_anchor? # => false
    put_situation(scanner)
    # Situation:
    #   pos:       0
    #   charpos:   0
    #   rest:      "foobarbaz"
    #   rest_size: 9

    static VALUE
    strscan_initialize(int argc, VALUE *argv, VALUE self)
    {
        struct strscanner *p;
        VALUE str, options;

        p = check_strscan(self);
        rb_scan_args(argc, argv, "11", &str, &options);
        options = rb_check_hash_type(options);
        if (!NIL_P(options)) {
            VALUE fixed_anchor;
            ID keyword_ids[1];
            keyword_ids[0] = rb_intern("fixed_anchor");
            rb_get_kwargs(options, keyword_ids, 0, 1, &fixed_anchor);
            if (fixed_anchor == Qundef) {
                p->fixed_anchor_p = false;
            }
            else {
                p->fixed_anchor_p = RTEST(fixed_anchor);
            }
        }
        else {
            p->fixed_anchor_p = false;
        }
        StringValue(str);
        p->str = str;

        return self;
    }

### Public Instance Methods

<<(p1)

- Appends the given `more_string` to the [stored string].

- Returns `self`.

- Does not affect the [positions] or [match values].

    scanner = StringScanner.new('foo')
    scanner.string           # => "foo"
    scanner.terminate
    scanner.concat('barbaz') # => #<StringScanner 3/9 "foo" @ "barba...">
    scanner.string           # => "foobarbaz"
    put_situation(scanner)
    # Situation:
    #   pos:       3
    #   charpos:   3
    #   rest:      "barbaz"
    #   rest_size: 6

Alias for: concat
[](specifier) → substring or nil click to toggle source

Returns a captured substring or `nil`; see [Captured Match Values].

When there are captures:

    scanner = StringScanner.new('Fri Dec 12 1975 14:39')
    scanner.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /)

- `specifier` zero: returns the entire matched substring:

      scanner[0]         # => "Fri Dec 12 "
      scanner.pre_match  # => ""
      scanner.post_match # => "1975 14:39"

- `specifier` positive integer. returns the `n`th capture, or `nil` if out of range:

      scanner[1] # => "Fri"
      scanner[2] # => "Dec"
      scanner[3] # => "12"
      scanner[4] # => nil

- `specifier` negative integer. counts backward from the last subgroup:

      scanner[-1] # => "12"
      scanner[-4] # => "Fri Dec 12 "
      scanner[-5] # => nil

- `specifier` symbol or string. returns the named subgroup, or `nil` if no such:

      scanner[:wday]  # => "Fri"
      scanner['wday'] # => "Fri"
      scanner[:month] # => "Dec"
      scanner[:day]   # => "12"
      scanner[:nope]  # => nil

When there are no captures, only `[0]` returns non-`nil`:

    scanner = StringScanner.new('foobarbaz')
    scanner.exist?(/bar/)
    scanner[0] # => "bar"
    scanner[1] # => nil

For a failed match, even `[0]` returns `nil`:

    scanner.scan(/nope/) # => nil
    scanner[0]           # => nil
    scanner[1]           # => nil

    static VALUE
    strscan_aref(VALUE self, VALUE idx)
    {
        const char *name;
        struct strscanner *p;
        long i;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p))        return Qnil;

        switch (TYPE(idx)) {
            case T_SYMBOL:
                idx = rb_sym2str(idx);
                /* fall through */
            case T_STRING:
                if (!RTEST(p->regex)) return Qnil;
                RSTRING_GETMEM(idx, name, i);
                i = name_to_backref_number(&(p->regs), p->regex, name, name + i, rb_enc_get(idx));
                break;
            default:
                i = NUM2LONG(idx);
        }

        if (i < 0)
            i += p->regs.num_regs;
        if (i < 0)                 return Qnil;
        if (i >= p->regs.num_regs) return Qnil;
        if (p->regs.beg[i] == -1)  return Qnil;

        return extract_range(p,
                             adjust_register_position(p, p->regs.beg[i]),
                             adjust_register_position(p, p->regs.end[i]));
    }

beginning_of_line? → true or false click to toggle source

Returns whether the [position] is at the beginning of a line; that is, at the beginning of the [stored string] or immediately after a newline:

    scanner = StringScanner.new(MULTILINE_TEXT)
    scanner.string
    # => "Go placidly amid the noise and haste,\nand remember what peace there may be in silence.\n"
    scanner.pos                # => 0
    scanner.beginning_of_line? # => true

    scanner.scan_until(/,/)    # => "Go placidly amid the noise and haste,"
    scanner.beginning_of_line? # => false

    scanner.scan(/\n/)         # => "\n"
    scanner.beginning_of_line? # => true

    scanner.terminate
    scanner.beginning_of_line? # => true

    scanner.concat('x')
    scanner.terminate
    scanner.beginning_of_line? # => false

StringScanner#bol? is an alias for `StringScanner#beginning_of_line?`.

    static VALUE
    strscan_bol_p(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (CURPTR(p) > S_PEND(p)) return Qnil;
        if (p->curr == 0) return Qtrue;
        return (*(CURPTR(p) - 1) == '\n') ? Qtrue : Qfalse;
    }

captures → substring_array or nil click to toggle source

Returns the array of [captured match values] at indexes `(1..)` if the most recent match attempt succeeded, or `nil` otherwise:

    scanner = StringScanner.new('Fri Dec 12 1975 14:39')
    scanner.captures         # => nil

    scanner.exist?(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /)
    scanner.captures         # => ["Fri", "Dec", "12"]
    scanner.values_at(*0..4) # => ["Fri Dec 12 ", "Fri", "Dec", "12", nil]

    scanner.exist?(/Fri/)
    scanner.captures         # => []

    scanner.scan(/nope/)
    scanner.captures         # => nil

    static VALUE
    strscan_captures(VALUE self)
    {
        struct strscanner *p;
        int   i, num_regs;
        VALUE new_ary;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p))        return Qnil;

        num_regs = p->regs.num_regs;
        new_ary  = rb_ary_new2(num_regs);

        for (i = 1; i < num_regs; i++) {
            VALUE str;
            if (p->regs.beg[i] == -1)
                str = Qnil;
            else
                str = extract_range(p,
                                    adjust_register_position(p, p->regs.beg[i]),
                                    adjust_register_position(p, p->regs.end[i]));
            rb_ary_push(new_ary, str);
        }

        return new_ary;
    }

charpos() click to toggle source

    static VALUE
    strscan_get_charpos(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);

        return LONG2NUM(rb_enc_strlen(S_PBEG(p), CURPTR(p), rb_enc_get(p->str)));
    }

check(pattern) → matched_substring or nil click to toggle source

Attempts to [match] the given `pattern` at the beginning of the [target substring]; does not modify the [positions].

If the match succeeds:

- Returns the matched substring.

- Sets all [match values].

    scanner = StringScanner.new('foobarbaz')
    scanner.pos = 3
    scanner.check('bar') # => "bar"
    put_match_values(scanner)
    # Basic match values:
    #   matched?:       true
    #   matched_size:   3
    #   pre_match:      "foo"
    #   matched  :      "bar"
    #   post_match:     "baz"
    # Captured match values:
    #   size:           1
    #   captures:       []
    #   named_captures: {}
    #   values_at:      ["bar", nil]
    #   []:
    #     [0]:          "bar"
    #     [1]:          nil
    # => 0..1
    put_situation(scanner)
    # Situation:
    #   pos:       3
    #   charpos:   3
    #   rest:      "barbaz"
    #   rest_size: 6

If the match fails:

- Returns `nil`.

- Clears all [match values].

    scanner.check(/nope/)          # => nil
    match_values_cleared?(scanner) # => true

    static VALUE
    strscan_check(VALUE self, VALUE re)
    {
        return strscan_do_scan(self, re, 0, 1, 1);
    }

check_until(pattern) → substring or nil click to toggle source

Attempts to [match] the given `pattern` anywhere (at any [position]) in the [target substring]; does not modify the [positions].

If the match succeeds:

- Sets all [match values].

- Returns the matched substring, which extends from the current [position] to the end of the matched substring.

    scanner = StringScanner.new('foobarbazbatbam')
    scanner.pos = 6
    scanner.check_until(/bat/) # => "bazbat"
    put_match_values(scanner)
    # Basic match values:
    #   matched?:       true
    #   matched_size:   3
    #   pre_match:      "foobarbaz"
    #   matched  :      "bat"
    #   post_match:     "bam"
    # Captured match values:
    #   size:           1
    #   captures:       []
    #   named_captures: {}
    #   values_at:      ["bat", nil]
    #   []:
    #     [0]:          "bat"
    #     [1]:          nil
    put_situation(scanner)
    # Situation:
    #   pos:       6
    #   charpos:   6
    #   rest:      "bazbatbam"
    #   rest_size: 9

If the match fails:

- Clears all [match values].

- Returns `nil`.

    scanner.check_until(/nope/)    # => nil
    match_values_cleared?(scanner) # => true

    static VALUE
    strscan_check_until(VALUE self, VALUE re)
    {
        return strscan_do_scan(self, re, 0, 1, 0);
    }

concat(more_string) → self click to toggle source

- Appends the given `more_string` to the [stored string].

- Returns `self`.

- Does not affect the [positions] or [match values].

    scanner = StringScanner.new('foo')
    scanner.string           # => "foo"
    scanner.terminate
    scanner.concat('barbaz') # => #<StringScanner 3/9 "foo" @ "barba...">
    scanner.string           # => "foobarbaz"
    put_situation(scanner)
    # Situation:
    #   pos:       3
    #   charpos:   3
    #   rest:      "barbaz"
    #   rest_size: 6

    static VALUE
    strscan_concat(VALUE self, VALUE str)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        StringValue(str);
        rb_str_append(p->str, str);
        return self;
    }

Also aliased as: <<
eos? → true or false click to toggle source

Returns whether the [position] is at the end of the [stored string]:

    scanner = StringScanner.new('foobarbaz')
    scanner.eos? # => false
    pos = 3
    scanner.eos? # => false
    scanner.terminate
    scanner.eos? # => true

    static VALUE
    strscan_eos_p(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        return EOS_P(p) ? Qtrue : Qfalse;
    }

exist?(pattern) → byte_offset or nil click to toggle source

Attempts to [match] the given `pattern` anywhere (at any [position]) n the [target substring]; does not modify the [positions].

If the match succeeds:

- Returns a byte offset: the distance in bytes between the current [position] and the end of the matched substring.

- Sets all [match values].

    scanner = StringScanner.new('foobarbazbatbam')
    scanner.pos = 6
    scanner.exist?(/bat/) # => 6
    put_match_values(scanner)
    # Basic match values:
    #   matched?:       true
    #   matched_size:   3
    #   pre_match:      "foobarbaz"
    #   matched  :      "bat"
    #   post_match:     "bam"
    # Captured match values:
    #   size:           1
    #   captures:       []
    #   named_captures: {}
    #   values_at:      ["bat", nil]
    #   []:
    #     [0]:          "bat"
    #     [1]:          nil
    put_situation(scanner)
    # Situation:
    #   pos:       6
    #   charpos:   6
    #   rest:      "bazbatbam"
    #   rest_size: 9

If the match fails:

- Returns `nil`.

- Clears all [match values].

    scanner.exist?(/nope/)         # => nil
    match_values_cleared?(scanner) # => true

    static VALUE
    strscan_exist_p(VALUE self, VALUE re)
    {
        return strscan_do_scan(self, re, 0, 0, 0);
    }

fixed_anchor? → true or false click to toggle source

Returns whether the [fixed-anchor property] is set.

    static VALUE
    strscan_fixed_anchor_p(VALUE self)
    {
        struct strscanner *p;
        p = check_strscan(self);
        return p->fixed_anchor_p ? Qtrue : Qfalse;
    }

get_byte() click to toggle source

    static VALUE
    strscan_get_byte(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        CLEAR_MATCH_STATUS(p);
        if (EOS_P(p))
            return Qnil;

        p->prev = p->curr;
        p->curr++;
        MATCHED(p);
        adjust_registers_to_matched(p);
        return extract_range(p,
                             adjust_register_position(p, p->regs.beg[0]),
                             adjust_register_position(p, p->regs.end[0]));
    }

getch() click to toggle source

    static VALUE
    strscan_getch(VALUE self)
    {
        struct strscanner *p;
        long len;

        GET_SCANNER(self, p);
        CLEAR_MATCH_STATUS(p);
        if (EOS_P(p))
            return Qnil;

        len = rb_enc_mbclen(CURPTR(p), S_PEND(p), rb_enc_get(p->str));
        len = minl(len, S_RESTLEN(p));
        p->prev = p->curr;
        p->curr += len;
        MATCHED(p);
        adjust_registers_to_matched(p);
        return extract_range(p,
                             adjust_register_position(p, p->regs.beg[0]),
                             adjust_register_position(p, p->regs.end[0]));
    }

inspect → string click to toggle source

Returns a string representation of `self` that may show:

1. The current [position].

2. The size (in bytes) of the [stored string].

3. The substring preceding the current position.

4. The substring following the current position (which is also the [target substring]).

    scanner = StringScanner.new("Fri Dec 12 1975 14:39")
    scanner.pos = 11
    scanner.inspect # => "#<StringScanner 11/21 \"...c 12 \" @ \"1975 ...\">"

If at beginning-of-string, item 4 above (following substring) is omitted:

    scanner.reset
    scanner.inspect # => "#<StringScanner 0/21 @ \"Fri D...\">"

If at end-of-string, all items above are omitted:

    scanner.terminate
    scanner.inspect # => "#<StringScanner fin>"

    static VALUE
    strscan_inspect(VALUE self)
    {
        struct strscanner *p;
        VALUE a, b;

        p = check_strscan(self);
        if (NIL_P(p->str)) {
            a = rb_sprintf("#<%"PRIsVALUE" (uninitialized)>", rb_obj_class(self));
            return a;
        }
        if (EOS_P(p)) {
            a = rb_sprintf("#<%"PRIsVALUE" fin>", rb_obj_class(self));
            return a;
        }
        if (p->curr == 0) {
            b = inspect2(p);
            a = rb_sprintf("#<%"PRIsVALUE" %ld/%ld @ %"PRIsVALUE">",
                           rb_obj_class(self),
                           p->curr, S_LEN(p),
                           b);
            return a;
        }
        a = inspect1(p);
        b = inspect2(p);
        a = rb_sprintf("#<%"PRIsVALUE" %ld/%ld %"PRIsVALUE" @ %"PRIsVALUE">",
                       rb_obj_class(self),
                       p->curr, S_LEN(p),
                       a, b);
        return a;
    }

match?(pattern) → updated_position or nil click to toggle source

Attempts to [match] the given `pattern` at the beginning of the [target substring]; does not modify the [positions].

If the match succeeds:

- Sets [match values].

- Returns the size in bytes of the matched substring.

    scanner = StringScanner.new('foobarbaz')
    scanner.pos = 3
    scanner.match?(/bar/) => 3
    put_match_values(scanner)
    # Basic match values:
    #   matched?:       true
    #   matched_size:   3
    #   pre_match:      "foo"
    #   matched  :      "bar"
    #   post_match:     "baz"
    # Captured match values:
    #   size:           1
    #   captures:       []
    #   named_captures: {}
    #   values_at:      ["bar", nil]
    #   []:
    #     [0]:          "bar"
    #     [1]:          nil
    put_situation(scanner)
    # Situation:
    #   pos:       3
    #   charpos:   3
    #   rest:      "barbaz"
    #   rest_size: 6

If the match fails:

- Clears match values.

- Returns `nil`.

- Does not increment positions.

    scanner.match?(/nope/)         # => nil
    match_values_cleared?(scanner) # => true

    static VALUE
    strscan_match_p(VALUE self, VALUE re)
    {
        return strscan_do_scan(self, re, 0, 0, 1);
    }

matched → matched_substring or nil click to toggle source

Returns the matched substring from the most recent [match] attempt if it was successful, or `nil` otherwise; see [Basic Matched Values]:

    scanner = StringScanner.new('foobarbaz')
    scanner.matched        # => nil
    scanner.pos = 3
    scanner.match?(/bar/)  # => 3
    scanner.matched        # => "bar"
    scanner.match?(/nope/) # => nil
    scanner.matched        # => nil

    static VALUE
    strscan_matched(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p)) return Qnil;
        return extract_range(p,
                             adjust_register_position(p, p->regs.beg[0]),
                             adjust_register_position(p, p->regs.end[0]));
    }

matched? → true or false click to toggle source

Returns `true` of the most recent [match attempt] was successful, `false` otherwise; see [Basic Matched Values]:

    scanner = StringScanner.new('foobarbaz')
    scanner.matched?       # => false
    scanner.pos = 3
    scanner.exist?(/baz/)  # => 6
    scanner.matched?       # => true
    scanner.exist?(/nope/) # => nil
    scanner.matched?       # => false

    static VALUE
    strscan_matched_p(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        return MATCHED_P(p) ? Qtrue : Qfalse;
    }

matched_size → substring_size or nil click to toggle source

Returns the size (in bytes) of the matched substring from the most recent match [match attempt] if it was successful, or `nil` otherwise; see [Basic Matched Values]:

    scanner = StringScanner.new('foobarbaz')
    scanner.matched_size   # => nil

    pos = 3
    scanner.exist?(/baz/)  # => 9
    scanner.matched_size   # => 3

    scanner.exist?(/nope/) # => nil
    scanner.matched_size   # => nil

    static VALUE
    strscan_matched_size(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p)) return Qnil;
        return LONG2NUM(p->regs.end[0] - p->regs.beg[0]);
    }

named_captures → hash click to toggle source

Returns the array of captured match values at indexes (1..) if the most recent match attempt succeeded, or nil otherwise; see [Captured Match Values]:

    scanner = StringScanner.new('Fri Dec 12 1975 14:39')
    scanner.named_captures # => {}

    pattern = /(?<wday>\w+) (?<month>\w+) (?<day>\d+) /
    scanner.match?(pattern)
    scanner.named_captures # => {"wday"=>"Fri", "month"=>"Dec", "day"=>"12"}

    scanner.string = 'nope'
    scanner.match?(pattern)
    scanner.named_captures # => {"wday"=>nil, "month"=>nil, "day"=>nil}

    scanner.match?(/nosuch/)
    scanner.named_captures # => {}

    static VALUE
    strscan_named_captures(VALUE self)
    {
        struct strscanner *p;
        named_captures_data data;
        GET_SCANNER(self, p);
        data.self = self;
        data.captures = rb_hash_new();
        if (!RB_NIL_P(p->regex)) {
            onig_foreach_name(RREGEXP_PTR(p->regex), named_captures_iter, &data);
        }

        return data.captures;
    }

peek(length) → substring click to toggle source

Returns the substring `string[pos, length]`; does not update [match values] or [positions]:

    scanner = StringScanner.new('foobarbaz')
    scanner.pos = 3
    scanner.peek(3)   # => "bar"
    scanner.terminate
    scanner.peek(3)   # => ""

    static VALUE
    strscan_peek(VALUE self, VALUE vlen)
    {
        struct strscanner *p;
        long len;

        GET_SCANNER(self, p);

        len = NUM2LONG(vlen);
        if (EOS_P(p))
            return str_new(p, "", 0);

        len = minl(len, S_RESTLEN(p));
        return extract_beg_len(p, p->curr, len);
    }

peek_byte() click to toggle source

Peeks at the current byte and returns it as an integer.

    s = StringScanner.new('ab')
    s.peek_byte         # => 97

    static VALUE
    strscan_peek_byte(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (EOS_P(p))
            return Qnil;

        return INT2FIX((unsigned char)*CURPTR(p));
    }

pointer()
Alias for: pos
pointer=(p1)
Alias for: pos=
pos() click to toggle source

    static VALUE
    strscan_get_pos(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        return INT2FIX(p->curr);
    }

Also aliased as: pointer
pos=(p1) click to toggle source

    static VALUE
    strscan_set_pos(VALUE self, VALUE v)
    {
        struct strscanner *p;
        long i;

        GET_SCANNER(self, p);
        i = NUM2INT(v);
        if (i < 0) i += S_LEN(p);
        if (i < 0) rb_raise(rb_eRangeError, "index out of range");
        if (i > S_LEN(p)) rb_raise(rb_eRangeError, "index out of range");
        p->curr = i;
        return LONG2NUM(i);
    }

Also aliased as: pointer=
post_match → substring click to toggle source

Returns the substring that follows the matched substring from the most recent match attempt if it was successful, or `nil` otherwise; see [Basic Match Values]:

    scanner = StringScanner.new('foobarbaz')
    scanner.post_match     # => nil

    scanner.pos = 3
    scanner.match?(/bar/)  # => 3
    scanner.post_match     # => "baz"

    scanner.match?(/nope/) # => nil
    scanner.post_match     # => nil

    static VALUE
    strscan_post_match(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p)) return Qnil;
        return extract_range(p,
                             adjust_register_position(p, p->regs.end[0]),
                             S_LEN(p));
    }

pre_match → substring click to toggle source

Returns the substring that precedes the matched substring from the most recent match attempt if it was successful, or `nil` otherwise; see [Basic Match Values]:

    scanner = StringScanner.new('foobarbaz')
    scanner.pre_match      # => nil

    scanner.pos = 3
    scanner.exist?(/baz/)  # => 6
    scanner.pre_match      # => "foobar" # Substring of entire string, not just target string.

    scanner.exist?(/nope/) # => nil
    scanner.pre_match      # => nil

    static VALUE
    strscan_pre_match(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p)) return Qnil;
        return extract_range(p,
                             0,
                             adjust_register_position(p, p->regs.beg[0]));
    }

reset → self click to toggle source

Sets both [byte position] and [character position] to zero, and clears [match values]; returns `self`:

    scanner = StringScanner.new('foobarbaz')
    scanner.exist?(/bar/)          # => 6
    scanner.reset                  # => #<StringScanner 0/9 @ "fooba...">
    put_situation(scanner)
    # Situation:
    #   pos:       0
    #   charpos:   0
    #   rest:      "foobarbaz"
    #   rest_size: 9
    # => nil
    match_values_cleared?(scanner) # => true

    static VALUE
    strscan_reset(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        p->curr = 0;
        CLEAR_MATCH_STATUS(p);
        return self;
    }

rest → target_substring click to toggle source

Returns the ‘rest’ of the [stored string] (all after the current [position]), which is the [target substring]:

    scanner = StringScanner.new('foobarbaz')
    scanner.rest # => "foobarbaz"
    scanner.pos = 3
    scanner.rest # => "barbaz"
    scanner.terminate
    scanner.rest # => ""

    static VALUE
    strscan_rest(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (EOS_P(p)) {
            return str_new(p, "", 0);
        }
        return extract_range(p, p->curr, S_LEN(p));
    }

rest_size → integer click to toggle source

Returns the size (in bytes) of the `rest` of the [stored string]:

    scanner = StringScanner.new('foobarbaz')
    scanner.rest      # => "foobarbaz"
    scanner.rest_size # => 9
    scanner.pos = 3
    scanner.rest      # => "barbaz"
    scanner.rest_size # => 6
    scanner.terminate
    scanner.rest      # => ""
    scanner.rest_size # => 0

    static VALUE
    strscan_rest_size(VALUE self)
    {
        struct strscanner *p;
        long i;

        GET_SCANNER(self, p);
        if (EOS_P(p)) {
            return INT2FIX(0);
        }
        i = S_RESTLEN(p);
        return INT2FIX(i);
    }

scan(p1) click to toggle source

    static VALUE
    strscan_scan(VALUE self, VALUE re)
    {
        return strscan_do_scan(self, re, 1, 1, 1);
    }

scan_byte → integer_byte click to toggle source

Scans one byte and returns it as an integer. This method is not multibyte character sensitive. See also: `getch`.

    static VALUE
    strscan_scan_byte(VALUE self)
    {
        struct strscanner *p;
        VALUE byte;

        GET_SCANNER(self, p);
        CLEAR_MATCH_STATUS(p);
        if (EOS_P(p))
            return Qnil;

        byte = INT2FIX((unsigned char)*CURPTR(p));
        p->prev = p->curr;
        p->curr++;
        MATCHED(p);
        adjust_registers_to_matched(p);
        return byte;
    }

scan_integer(base: 10) click to toggle source

If ‘base` isn’t provided or is ‘10`, then it is equivalent to calling `#scan` with a `[+-]?d+` pattern, and returns an Integer or nil.

If ‘base` is `16`, then it is equivalent to calling `#scan` with a `[+-]?(0x)?+` pattern, and returns an Integer or nil.

The scanned string must be encoded with an ASCII compatible encoding, otherwise Encoding::CompatibilityError will be raised.

    # File strscan/lib/strscan/strscan.rb, line 15
    def scan_integer(base: 10)
      case base
      when 10
        scan_base10_integer
      when 16
        scan_base16_integer
      else
        raise ArgumentError, "Unsupported integer base: #{base.inspect}, expected 10 or 16"
      end
    end

scan_until(p1) click to toggle source

    static VALUE
    strscan_scan_until(VALUE self, VALUE re)
    {
        return strscan_do_scan(self, re, 1, 1, 0);
    }

size → captures_count click to toggle source

Returns the count of captures if the most recent match attempt succeeded, `nil` otherwise; see [Captures Match Values]:

    scanner = StringScanner.new('Fri Dec 12 1975 14:39')
    scanner.size                        # => nil

    pattern = /(?<wday>\w+) (?<month>\w+) (?<day>\d+) /
    scanner.match?(pattern)
    scanner.values_at(*0..scanner.size) # => ["Fri Dec 12 ", "Fri", "Dec", "12", nil]
    scanner.size                        # => 4

    scanner.match?(/nope/)              # => nil
    scanner.size                        # => nil

    static VALUE
    strscan_size(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p))        return Qnil;
        return INT2FIX(p->regs.num_regs);
    }

skip(p1) click to toggle source

    static VALUE
    strscan_skip(VALUE self, VALUE re)
    {
        return strscan_do_scan(self, re, 1, 0, 1);
    }

skip_until(p1) click to toggle source

    static VALUE
    strscan_skip_until(VALUE self, VALUE re)
    {
        return strscan_do_scan(self, re, 1, 0, 0);
    }

string → stored_string click to toggle source

Returns the [stored string]:

    scanner = StringScanner.new('foobar')
    scanner.string # => "foobar"
    scanner.concat('baz')
    scanner.string # => "foobarbaz"

    static VALUE
    strscan_get_string(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        return p->str;
    }

string = other_string → other_string click to toggle source

Replaces the [stored string] with the given `other_string`:

- Sets both [positions] to zero.

- Clears [match values].

- Returns `other_string`.

    scanner = StringScanner.new('foobar')
    scanner.scan(/foo/)
    put_situation(scanner)
    # Situation:
    #   pos:       3
    #   charpos:   3
    #   rest:      "bar"
    #   rest_size: 3
    match_values_cleared?(scanner) # => false

    scanner.string = 'baz'         # => "baz"
    put_situation(scanner)
    # Situation:
    #   pos:       0
    #   charpos:   0
    #   rest:      "baz"
    #   rest_size: 3
    match_values_cleared?(scanner) # => true

    static VALUE
    strscan_set_string(VALUE self, VALUE str)
    {
        struct strscanner *p = check_strscan(self);

        StringValue(str);
        p->str = str;
        p->curr = 0;
        CLEAR_MATCH_STATUS(p);
        return str;
    }

terminate() click to toggle source

    static VALUE
    strscan_terminate(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        p->curr = S_LEN(p);
        CLEAR_MATCH_STATUS(p);
        return self;
    }

unscan → self click to toggle source

Sets the [position] to its value previous to the recent successful [match] attempt:

    scanner = StringScanner.new('foobarbaz')
    scanner.scan(/foo/)
    put_situation(scanner)
    # Situation:
    #   pos:       3
    #   charpos:   3
    #   rest:      "barbaz"
    #   rest_size: 6
    scanner.unscan
    # => #<StringScanner 0/9 @ "fooba...">
    put_situation(scanner)
    # Situation:
    #   pos:       0
    #   charpos:   0
    #   rest:      "foobarbaz"
    #   rest_size: 9

Raises an exception if match values are clear:

    scanner.scan(/nope/)           # => nil
    match_values_cleared?(scanner) # => true
    scanner.unscan                 # Raises StringScanner::Error.

    static VALUE
    strscan_unscan(VALUE self)
    {
        struct strscanner *p;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p))
            rb_raise(ScanError, "unscan failed: previous match record not exist");
        p->curr = p->prev;
        CLEAR_MATCH_STATUS(p);
        return self;
    }

values_at(*specifiers) → array_of_captures or nil click to toggle source

Returns an array of captured substrings, or `nil` of none.

For each `specifier`, the returned substring is `[specifier]`; see `[]`.

    scanner = StringScanner.new('Fri Dec 12 1975 14:39')
    pattern = /(?<wday>\w+) (?<month>\w+) (?<day>\d+) /
    scanner.match?(pattern)
    scanner.values_at(*0..3)               # => ["Fri Dec 12 ", "Fri", "Dec", "12"]
    scanner.values_at(*%i[wday month day]) # => ["Fri", "Dec", "12"]

    static VALUE
    strscan_values_at(int argc, VALUE *argv, VALUE self)
    {
        struct strscanner *p;
        long i;
        VALUE new_ary;

        GET_SCANNER(self, p);
        if (! MATCHED_P(p))        return Qnil;

        new_ary = rb_ary_new2(argc);
        for (i = 0; i<argc; i++) {
            rb_ary_push(new_ary, strscan_aref(self, argv[i]));
        }

        return new_ary;
    }

### Private Instance Methods

dup → shallow_copy click to toggle source

Returns a shallow copy of `self`; the [stored string] in the copy is the same string as in `self`.

    static VALUE
    strscan_init_copy(VALUE vself, VALUE vorig)
    {
        struct strscanner *self, *orig;

        self = check_strscan(vself);
        orig = check_strscan(vorig);
        if (self != orig) {
            self->flags = orig->flags;
            self->str = orig->str;
            self->prev = orig->prev;
            self->curr = orig->curr;
            if (rb_reg_region_copy(&self->regs, &orig->regs))
                rb_memerror();
            RB_GC_GUARD(vorig);
        }

        return vself;
    }

scan_base10_integer() click to toggle source

    static VALUE
    strscan_scan_base10_integer(VALUE self)
    {
        char *ptr;
        long len = 0;
        struct strscanner *p;

        GET_SCANNER(self, p);
        CLEAR_MATCH_STATUS(p);

        strscan_must_ascii_compat(p->str);

        ptr = CURPTR(p);

        long remaining_len = S_RESTLEN(p);

        if (remaining_len <= 0) {
            return Qnil;
        }

        if (ptr[len] == '-' || ptr[len] == '+') {
            len++;
        }

        if (!rb_isdigit(ptr[len])) {
            return Qnil;
        }

        MATCHED(p);
        p->prev = p->curr;

        while (len < remaining_len && rb_isdigit(ptr[len])) {
            len++;
        }

        return strscan_parse_integer(p, 10, len);
    }

scan_base16_integer() click to toggle source

    static VALUE
    strscan_scan_base16_integer(VALUE self)
    {
        char *ptr;
        long len = 0;
        struct strscanner *p;

        GET_SCANNER(self, p);
        CLEAR_MATCH_STATUS(p);

        strscan_must_ascii_compat(p->str);

        ptr = CURPTR(p);

        long remaining_len = S_RESTLEN(p);

        if (remaining_len <= 0) {
            return Qnil;
        }

        if (ptr[len] == '-' || ptr[len] == '+') {
            len++;
        }

        if ((remaining_len >= (len + 2)) && ptr[len] == '0' && ptr[len + 1] == 'x') {
            len += 2;
        }

        if (len >= remaining_len || !rb_isxdigit(ptr[len])) {
            return Qnil;
        }

        MATCHED(p);
        p->prev = p->curr;

        while (len < remaining_len && rb_isxdigit(ptr[len])) {
            len++;
        }

        return strscan_parse_integer(p, 16, len);
    }