# class StringScanner

`StringScanner` provides for lexical scanning operations on a `String`. Here is an example of its usage:

    require 'strscan'

    s = StringScanner.new('This is an example string')
    s.eos?               # -> false

    p s.scan(/\w+/)      # -> "This"
    p s.scan(/\w+/)      # -> nil
    p s.scan(/\s+/)      # -> " "
    p s.scan(/\s+/)      # -> nil
    p s.scan(/\w+/)      # -> "is"
    s.eos?               # -> false

    p s.scan(/\s+/)      # -> " "
    p s.scan(/\w+/)      # -> "an"
    p s.scan(/\s+/)      # -> " "
    p s.scan(/\w+/)      # -> "example"
    p s.scan(/\s+/)      # -> " "
    p s.scan(/\w+/)      # -> "string"
    s.eos?               # -> true

    p s.scan(/\s+/)      # -> nil
    p s.scan(/\w+/)      # -> nil

Scanning a string means remembering the position of a scan pointer, which is just an index. The point of scanning is to move forward a bit at a time, so matches are sought after the scan pointer; usually immediately after it.

Given the string “test string”, here are the pertinent scan pointer positions:

      t e s t   s t r i n g
    0 1 2 ...             1
                          0

When you `scan` for a pattern (a regular expression), the match must occur at the character after the scan pointer. If you use `scan_until`, then the match can occur anywhere after the scan pointer. In both cases, the scan pointer moves just beyond the last character of the match, ready to scan again from the next character onwards. This is demonstrated by the example above.

## `Method` Categories¶ ↑

There are other methods besides the plain scanners. You can look ahead in the string without actually scanning. You can access the most recent match. You can modify the string being scanned, reset or terminate the scanner, find out or change the position of the scan pointer, skip ahead, and so on.

### Advancing the Scan Pointer¶ ↑

- `getch`

- `get_byte`

- `scan`

- `scan_until`

- `skip`

- `skip_until`

### Looking Ahead¶ ↑

- `check`

- `check_until`

- `exist?`

- `match?`

- `peek`

### Finding Where we Are¶ ↑

- `beginning_of_line?` (`#bol?`)

- `eos?`

- `rest?`

- `rest_size`

- `pos`

### Setting Where we Are¶ ↑

- `reset`

- `terminate`

- `pos=`

### Match `Data`¶ ↑

- `matched`

- `matched?`

- `matched_size`

- `#[]`

- `pre_match`

- `post_match`

### Miscellaneous¶ ↑

- `<<`

- `concat`

- `string`

- `string=`

- `unscan`

There are aliases to several of the methods.

### Public Class Methods

must_C_version click to toggle source

This method is defined for backward compatibility.

    static VALUE
    strscan_s_mustc(VALUE self)
    {
        return self;
    }

new(string, fixed_anchor: false) click to toggle source
new(string, dup = false)

Creates a new `StringScanner` object to scan over the given `string`.

If `fixed_anchor` is `true`, `\A` always matches the beginning of the string. Otherwise, `\A` always matches the current position.

`dup` argument is obsolete and not used now.

    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

<<(str)

Appends `str` to the string being scanned. This method does not affect scan pointer.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.scan(/Fri /)
    s << " +1000 GMT"
    s.string            # -> "Fri Dec 12 1975 14:39 +1000 GMT"
    s.scan(/Dec/)       # -> "Dec"

Alias for: concat
[](n) click to toggle source

Returns the n-th subgroup in the most recent match.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.scan(/(\w+) (\w+) (\d+) /)       # -> "Fri Dec 12 "
    s[0]                               # -> "Fri Dec 12 "
    s[1]                               # -> "Fri"
    s[2]                               # -> "Dec"
    s[3]                               # -> "12"
    s.post_match                       # -> "1975 14:39"
    s.pre_match                        # -> ""

    s.reset
    s.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /)       # -> "Fri Dec 12 "
    s[0]                               # -> "Fri Dec 12 "
    s[1]                               # -> "Fri"
    s[2]                               # -> "Dec"
    s[3]                               # -> "12"
    s[:wday]                           # -> "Fri"
    s[:month]                          # -> "Dec"
    s[:day]                            # -> "12"
    s.post_match                       # -> "1975 14:39"
    s.pre_match                        # -> ""

    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?() click to toggle source

Returns `true` if and only if the scan pointer is at the beginning of the line.

    s = StringScanner.new("test\ntest\n")
    s.bol?           # => true
    s.scan(/te/)
    s.bol?           # => false
    s.scan(/st\n/)
    s.bol?           # => true
    s.terminate
    s.bol?           # => true

    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 click to toggle source

Returns the subgroups in the most recent match (not including the full match). If nothing was priorly matched, it returns nil.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.scan(/(\w+) (\w+) (\d+) /)       # -> "Fri Dec 12 "
    s.captures                         # -> ["Fri", "Dec", "12"]
    s.scan(/(\w+) (\w+) (\d+) /)       # -> nil
    s.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 = 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

Returns the character position of the scan pointer. In the ‘reset’ position, this value is zero. In the ‘terminated’ position (i.e. the string is exhausted), this value is the size of the string.

In short, it’s a 0-based index into the string.

    s = StringScanner.new("abc\u00e4def\u00f6ghi")
    s.charpos                # -> 0
    s.scan_until(/\u00e4/)   # -> "abc\u00E4"
    s.pos                    # -> 5
    s.charpos                # -> 4

    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) click to toggle source

This returns the value that `scan` would return, without advancing the scan pointer. The match register is affected, though.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.check /Fri/               # -> "Fri"
    s.pos                       # -> 0
    s.matched                   # -> "Fri"
    s.check /12/                # -> nil
    s.matched                   # -> nil

Mnemonic: it “checks” to see whether a `scan` will return a value.

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

check_until(pattern) click to toggle source

This returns the value that `scan_until` would return, without advancing the scan pointer. The match register is affected, though.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.check_until /12/          # -> "Fri Dec 12"
    s.pos                       # -> 0
    s.matched                   # -> 12

Mnemonic: it “checks” to see whether a `scan_until` will return a value.

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

clear() click to toggle source

Equivalent to `terminate`. This method is obsolete; use `terminate` instead.

    static VALUE
    strscan_clear(VALUE self)
    {
        rb_warning("StringScanner#clear is obsolete; use #terminate instead");
        return strscan_terminate(self);
    }

concat(str) click to toggle source

Appends `str` to the string being scanned. This method does not affect scan pointer.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.scan(/Fri /)
    s << " +1000 GMT"
    s.string            # -> "Fri Dec 12 1975 14:39 +1000 GMT"
    s.scan(/Dec/)       # -> "Dec"

    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: <<
empty?() click to toggle source

Equivalent to `eos?`. This method is obsolete, use `eos?` instead.

    static VALUE
    strscan_empty_p(VALUE self)
    {
        rb_warning("StringScanner#empty? is obsolete; use #eos? instead");
        return strscan_eos_p(self);
    }

eos?() click to toggle source

Returns `true` if the scan pointer is at the end of the string.

    s = StringScanner.new('test string')
    p s.eos?          # => false
    s.scan(/test/)
    p s.eos?          # => false
    s.terminate
    p s.eos?          # => true

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

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

exist?(pattern) click to toggle source

Looks ahead to see if the `pattern` exists anywhere in the string, without advancing the scan pointer. This predicates whether a `scan_until` will return a value.

    s = StringScanner.new('test string')
    s.exist? /s/            # -> 3
    s.scan /test/           # -> "test"
    s.exist? /s/            # -> 2
    s.exist? /e/            # -> nil

    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

Whether `scanner` uses fixed anchor mode or not.

If fixed anchor mode is used, `\A` always matches the beginning of the string. Otherwise, `\A` always matches the current position.

    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

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

    s = StringScanner.new('ab')
    s.get_byte         # => "a"
    s.get_byte         # => "b"
    s.get_byte         # => nil

    s = StringScanner.new("\244\242".force_encoding("euc-jp"))
    s.get_byte         # => "\xA4"
    s.get_byte         # => "\xA2"
    s.get_byte         # => nil

    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]));
    }

getbyte() click to toggle source

Equivalent to `get_byte`. This method is obsolete; use `get_byte` instead.

    static VALUE
    strscan_getbyte(VALUE self)
    {
        rb_warning("StringScanner#getbyte is obsolete; use #get_byte instead");
        return strscan_get_byte(self);
    }

getch() click to toggle source

Scans one character and returns it. This method is multibyte character sensitive.

    s = StringScanner.new("ab")
    s.getch           # => "a"
    s.getch           # => "b"
    s.getch           # => nil

    s = StringScanner.new("\244\242".force_encoding("euc-jp"))
    s.getch           # => "\x{A4A2}"   # Japanese hira-kana "A" in EUC-JP
    s.getch           # => nil

    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() click to toggle source

Returns a string that represents the `StringScanner` object, showing:

- the current position

- the size of the string

- the characters surrounding the scan pointer

  s = `StringScanner.new`(“Fri Dec 12 1975 14:39”) s.inspect # -> ‘#<StringScanner 0/21 @ “Fri D…”>’ s.scan_until /12/ # -> “Fri Dec 12” s.inspect # -> ‘#<StringScanner 10/21 “…ec 12” @ “ 1975…”>’

    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) click to toggle source

Tests whether the given `pattern` is matched from the current scan pointer. Returns the length of the match, or `nil`. The scan pointer is not advanced.

    s = StringScanner.new('test string')
    p s.match?(/\w+/)   # -> 4
    p s.match?(/\w+/)   # -> 4
    p s.match?("test")  # -> 4
    p s.match?(/\s+/)   # -> nil

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

matched() click to toggle source

Returns the last matched string.

    s = StringScanner.new('test string')
    s.match?(/\w+/)     # -> 4
    s.matched           # -> "test"

    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?() click to toggle source

Returns `true` if and only if the last match was successful.

    s = StringScanner.new('test string')
    s.match?(/\w+/)     # => 4
    s.matched?          # => true
    s.match?(/\d+/)     # => nil
    s.matched?          # => false

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

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

matched_size() click to toggle source

Returns the size of the most recent match in bytes, or `nil` if there was no recent match. This is different than `matched.size`, which will return the size in characters.

    s = StringScanner.new('test string')
    s.check /\w+/           # -> "test"
    s.matched_size          # -> 4
    s.check /\d+/           # -> nil
    s.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 a hash of string variables matching the regular expression.

    scan = StringScanner.new('foobarbaz')
    scan.match?(/(?<f>foo)(?<r>bar)(?<z>baz)/)
    scan.named_captures # -> {"f"=>"foo", "r"=>"bar", "z"=>"baz"}

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

        return data.captures;
    }

peek(len) click to toggle source

Extracts a string corresponding to `string[pos,len]`, without advancing the scan pointer.

    s = StringScanner.new('test string')
    s.peek(7)          # => "test st"
    s.peek(7)          # => "test st"

    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);
    }

peep(p1) click to toggle source

Equivalent to `peek`. This method is obsolete; use `peek` instead.

    static VALUE
    strscan_peep(VALUE self, VALUE vlen)
    {
        rb_warning("StringScanner#peep is obsolete; use #peek instead");
        return strscan_peek(self, vlen);
    }

pointer()

Returns the byte position of the scan pointer. In the ‘reset’ position, this value is zero. In the ‘terminated’ position (i.e. the string is exhausted), this value is the bytesize of the string.

In short, it’s a 0-based index into bytes of the string.

    s = StringScanner.new('test string')
    s.pos               # -> 0
    s.scan_until /str/  # -> "test str"
    s.pos               # -> 8
    s.terminate         # -> #<StringScanner fin>
    s.pos               # -> 11

Alias for: pos
pointer=(p1)

Sets the byte position of the scan pointer.

    s = StringScanner.new('test string')
    s.pos = 7            # -> 7
    s.rest               # -> "ring"

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

Returns the byte position of the scan pointer. In the ‘reset’ position, this value is zero. In the ‘terminated’ position (i.e. the string is exhausted), this value is the bytesize of the string.

In short, it’s a 0-based index into bytes of the string.

    s = StringScanner.new('test string')
    s.pos               # -> 0
    s.scan_until /str/  # -> "test str"
    s.pos               # -> 8
    s.terminate         # -> #<StringScanner fin>
    s.pos               # -> 11

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

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

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

Sets the byte position of the scan pointer.

    s = StringScanner.new('test string')
    s.pos = 7            # -> 7
    s.rest               # -> "ring"

    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() click to toggle source

Returns the post-match (in the regular expression sense) of the last scan.

    s = StringScanner.new('test string')
    s.scan(/\w+/)           # -> "test"
    s.scan(/\s+/)           # -> " "
    s.pre_match             # -> "test"
    s.post_match            # -> "string"

    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() click to toggle source

Returns the pre-match (in the regular expression sense) of the last scan.

    s = StringScanner.new('test string')
    s.scan(/\w+/)           # -> "test"
    s.scan(/\s+/)           # -> " "
    s.pre_match             # -> "test"
    s.post_match            # -> "string"

    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() click to toggle source

Reset the scan pointer (index 0) and clear matching data.

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

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

rest() click to toggle source

Returns the “rest” of the string (i.e. everything after the scan pointer). If there is no more data (eos? = true), it returns `""`.

    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?() click to toggle source

Returns true if and only if there is more data in the string. See `eos?`. This method is obsolete; use `eos?` instead.

    s = StringScanner.new('test string')
    s.eos?              # These two
    s.rest?             # are opposites.

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

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

rest_size() click to toggle source

`s.rest_size` is equivalent to `s.rest.size`.

    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);
    }

restsize() click to toggle source

`s.restsize` is equivalent to `s.rest_size`. This method is obsolete; use `rest_size` instead.

    static VALUE
    strscan_restsize(VALUE self)
    {
        rb_warning("StringScanner#restsize is obsolete; use #rest_size instead");
        return strscan_rest_size(self);
    }

scan(pattern) → String click to toggle source

Tries to match with `pattern` at the current position. If there’s a match, the scanner advances the “scan pointer” and returns the matched string. Otherwise, the scanner returns `nil`.

    s = StringScanner.new('test string')
    p s.scan(/\w+/)   # -> "test"
    p s.scan(/\w+/)   # -> nil
    p s.scan(/\s+/)   # -> " "
    p s.scan("str")   # -> "str"
    p s.scan(/\w+/)   # -> "ing"
    p s.scan(/./)     # -> nil

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

scan_full(pattern, advance_pointer_p, return_string_p) click to toggle source

Tests whether the given `pattern` is matched from the current scan pointer. Advances the scan pointer if `advance_pointer_p` is true. Returns the matched string if `return_string_p` is true. The match register is affected.

“full” means “#scan with full parameters”.

    static VALUE
    strscan_scan_full(VALUE self, VALUE re, VALUE s, VALUE f)
    {
        return strscan_do_scan(self, re, RTEST(s), RTEST(f), 1);
    }

scan_until(pattern) click to toggle source

Scans the string until the `pattern` is matched. Returns the substring up to and including the end of the match, advancing the scan pointer to that location. If there is no match, `nil` is returned.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.scan_until(/1/)        # -> "Fri Dec 1"
    s.pre_match              # -> "Fri Dec "
    s.scan_until(/XYZ/)      # -> nil

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

search_full(pattern, advance_pointer_p, return_string_p) click to toggle source

Scans the string until the `pattern` is matched. Advances the scan pointer if `advance_pointer_p`, otherwise not. Returns the matched string if `return_string_p` is true, otherwise returns the number of bytes advanced. This method does affect the match register.

    static VALUE
    strscan_search_full(VALUE self, VALUE re, VALUE s, VALUE f)
    {
        return strscan_do_scan(self, re, RTEST(s), RTEST(f), 0);
    }

size click to toggle source

Returns the amount of subgroups in the most recent match. The full match counts as a subgroup.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.scan(/(\w+) (\w+) (\d+) /)       # -> "Fri Dec 12 "
    s.size                             # -> 4

    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(pattern) click to toggle source

Attempts to skip over the given `pattern` beginning with the scan pointer. If it matches, the scan pointer is advanced to the end of the match, and the length of the match is returned. Otherwise, `nil` is returned.

It’s similar to `scan`, but without returning the matched string.

    s = StringScanner.new('test string')
    p s.skip(/\w+/)   # -> 4
    p s.skip(/\w+/)   # -> nil
    p s.skip(/\s+/)   # -> 1
    p s.skip("st")    # -> 2
    p s.skip(/\w+/)   # -> 4
    p s.skip(/./)     # -> nil

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

skip_until(pattern) click to toggle source

Advances the scan pointer until `pattern` is matched and consumed. Returns the number of bytes advanced, or `nil` if no match was found.

Look ahead to match `pattern`, and advance the scan pointer to the end of the match. Return the number of characters advanced, or `nil` if the match was unsuccessful.

It’s similar to `scan_until`, but without returning the intervening string.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.skip_until /12/           # -> 10
    s                           #

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

string() click to toggle source

Returns the string being scanned.

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

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

string=(str) click to toggle source

Changes the string being scanned to `str` and resets the scanner. Returns `str`.

    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
clear

Sets the scan pointer to the end of the string and clear matching data.

    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() click to toggle source

Sets the scan pointer to the previous position. Only one previous position is remembered, and it changes with each scanning operation.

    s = StringScanner.new('test string')
    s.scan(/\w+/)        # => "test"
    s.unscan
    s.scan(/../)         # => "te"
    s.scan(/\d/)         # => nil
    s.unscan             # ScanError: unscan failed: previous match record not exist

    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( i1, i2, ... iN ) → an_array click to toggle source

Returns the subgroups in the most recent match at the given indices. If nothing was priorly matched, it returns nil.

    s = StringScanner.new("Fri Dec 12 1975 14:39")
    s.scan(/(\w+) (\w+) (\d+) /)       # -> "Fri Dec 12 "
    s.values_at 0, -1, 5, 2            # -> ["Fri Dec 12 ", "12", nil, "Dec"]
    s.scan(/(\w+) (\w+) (\d+) /)       # -> nil
    s.values_at 0, -1, 5, 2            # -> nil

    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 click to toggle source
clone

Duplicates a `StringScanner` object.

    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;
    }