class StringScanner
类 StringScanner 支持将存储的字符串作为流进行处理;以下代码使用字符串 'foobarbaz' 创建一个新的 StringScanner 对象
require 'strscan' scanner = StringScanner.new('foobarbaz')
关于示例¶ ↑
这里的所有示例都假设已经引入了 StringScanner
require 'strscan'
这里的一些示例假设定义了这些常量
MULTILINE_TEXT = <<~EOT Go placidly amid the noise and haste, and remember what peace there may be in silence. EOT HIRAGANA_TEXT = 'こんにちは' ENGLISH_TEXT = 'Hello'
这里的一些示例假设定义了某些辅助方法
-
put_situation(scanner):显示扫描器的方法pos、charpos、rest和rest_size的值。 -
put_match_values(scanner):显示扫描器的匹配值。 -
match_values_cleared?(scanner):返回扫描器的匹配值是否已清除。
请参阅示例[此处]。
StringScanner 对象¶ ↑
此代码创建一个 StringScanner 对象(我们将其简称为扫描器),并展示它的一些基本属性
scanner = StringScanner.new('foobarbaz') scanner.string # => "foobarbaz" put_situation(scanner) # Situation: # pos: 0 # charpos: 0 # rest: "foobarbaz" # rest_size: 9
扫描器具有
-
一个存储字符串,它是
-
初始通过
StringScanner.new(string)设置为给定的string(在上面的示例中为'foobarbaz')。 -
可通过方法
string=(new_string)和concat(more_string)修改。 -
由方法
string返回。
更多信息请参见下面的 存储字符串。
-
-
一个位置;一个基于零的索引,指向存储字符串的字节(不是指向字符)
-
初始通过
StringScanner.new设置为0。 -
由方法
pos返回。 -
可通过方法
reset、terminate和pos=(new_pos)显式修改。 -
可通过隐式方式修改(各种遍历方法等)。
更多信息请参见下面的 字节位置。
-
-
一个目标子字符串,它是存储字符串的尾部子字符串;它从当前位置扩展到存储字符串的末尾
-
初始通过
StringScanner.new(string)设置为给定的string(在上面的示例中为'foobarbaz')。 -
由方法
rest返回。 -
通过对存储字符串或位置的任何修改进行修改。
最重要的是:搜索和遍历方法在目标子字符串上操作,该子字符串可能(并且通常是)小于整个存储字符串。
更多信息请参见下面的 目标子字符串。
-
存储字符串¶ ↑
存储字符串 是存储在 StringScanner 对象中的字符串。
以下每个方法都设置、修改或返回存储的字符串
方法 |
作用 |
|---|---|
::new(string) |
为给定的字符串创建一个新的扫描器。 |
string=(new_string) |
替换现有的存储字符串。 |
concat(more_string) |
将字符串附加到现有的存储字符串。 |
string |
返回存储的字符串。 |
位置¶ ↑
StringScanner 对象维护一个基于零的字节位置和一个基于零的字符位置。
以下每个方法都显式设置位置
方法 |
作用 |
|---|---|
reset |
将两个位置都设置为零(存储字符串的开头)。 |
terminate |
将两个位置都设置为存储字符串的末尾。 |
pos=(new_byte_position) |
设置字节位置;调整字符位置。 |
字节位置(位置)¶ ↑
字节位置(或简称为位置)是存储字符串中字节的基于零的索引;对于一个新的 StringScanner 对象,字节位置为零。
当字节位置是
-
零(在开头)时,目标子字符串是整个存储字符串。
-
等于存储字符串的大小(在末尾)时,目标子字符串是空字符串
''。
要获取或设置字节位置
-
pos:返回字节位置。 -
pos=(new_pos):设置字节位置。
许多方法使用字节位置作为查找匹配的基础;许多其他方法设置、递增或递减字节位置
scanner = StringScanner.new('foobar') scanner.pos # => 0 scanner.scan(/foo/) # => "foo" # Match found. scanner.pos # => 3 # Byte position incremented. scanner.scan(/foo/) # => nil # Match not found. scanner.pos # => 3 # Byte position not changed.
某些方法隐式修改字节位置;请参阅
字符位置¶ ↑
字符位置是存储字符串中字符的基于零的索引;对于新的 StringScanner 对象,字符位置为零。
方法 charpos 返回字符位置;其值可能不会被显式重置。
某些方法会更改(递增或重置)字符位置;请参阅
示例(字符串包含多字节字符)
scanner = StringScanner.new(ENGLISH_TEXT) # Five 1-byte characters. scanner.concat(HIRAGANA_TEXT) # Five 3-byte characters scanner.string # => "Helloこんにちは" # Twenty bytes in all. put_situation(scanner) # Situation: # pos: 0 # charpos: 0 # rest: "Helloこんにちは" # rest_size: 20 scanner.scan(/Hello/) # => "Hello" # Five 1-byte characters. put_situation(scanner) # Situation: # pos: 5 # charpos: 5 # rest: "こんにちは" # rest_size: 15 scanner.getch # => "こ" # One 3-byte character. put_situation(scanner) # Situation: # pos: 8 # charpos: 6 # rest: "んにちは" # rest_size: 12
目标子字符串¶ ↑
目标子字符串是 存储字符串 中从当前 字节位置 扩展到存储字符串末尾的部分;它始终是
-
整个存储字符串(字节位置为零)。
-
存储字符串的尾部子字符串(字节位置为正)。
目标子字符串由方法 rest 返回,其大小由方法 rest_size 返回。
示例
scanner = StringScanner.new('foobarbaz') put_situation(scanner) # Situation: # pos: 0 # charpos: 0 # rest: "foobarbaz" # rest_size: 9 scanner.pos = 3 put_situation(scanner) # Situation: # pos: 3 # charpos: 3 # rest: "barbaz" # rest_size: 6 scanner.pos = 9 put_situation(scanner) # Situation: # pos: 9 # charpos: 9 # rest: "" # rest_size: 0
设置目标子字符串¶ ↑
每当出现以下情况时,都会设置目标子字符串
查询目标子字符串¶ ↑
下表总结(详细信息和示例请参见链接)
方法 |
返回 |
|---|---|
rest |
目标子字符串。 |
rest_size |
目标子字符串的大小(以字节为单位)。 |
搜索目标子字符串¶ ↑
搜索方法检查目标子字符串,但不推进位置 或(通过暗示)缩短目标子字符串。
下表总结(详细信息和示例请参见链接)
方法 |
返回 | 设置匹配值? |
|---|---|---|
check(pattern) |
匹配的前导子字符串或 nil。 |
是。 |
check_until(pattern) |
匹配的子字符串(任何位置)或 nil。 |
是。 |
exist?(pattern) |
匹配的子字符串(任何位置)的末尾索引。 | 是。 |
match?(pattern) |
匹配的前导子字符串的大小或 nil。 |
是。 |
peek(size) |
给定长度(字节)的前导子字符串。 | 否。 |
peek_byte |
Integer 前导字节或 nil。 |
否。 |
rest |
目标子字符串(从字节位置到末尾)。 | 否。 |
遍历目标子字符串¶ ↑
遍历方法检查目标子字符串,如果成功
-
推进位置。
-
缩短目标子字符串。
下表总结(详细信息和示例请参见链接)
方法 |
返回 | 设置匹配值? |
|---|---|---|
get_byte |
前导字节或 nil。 |
否。 |
getch |
前导字符或 nil。 |
否。 |
scan(pattern) |
匹配的前导子字符串或 nil。 |
是。 |
scan_byte |
Integer 前导字节或 nil。 |
否。 |
scan_until(pattern) |
匹配的子字符串(任何位置)或 nil。 |
是。 |
skip(pattern) |
匹配的前导子字符串大小或 nil。 |
是。 |
skip_until(pattern) |
到匹配的子字符串末尾的位置增量或 nil。 |
是。 |
unscan |
self. |
否。 |
查询扫描器¶ ↑
这些方法都会查询扫描器对象,而不会修改它(详情和示例请参见链接)。
方法 |
返回 |
|---|---|
beginning_of_line? |
true 或 false。 |
charpos |
字符位置。 |
eos? |
true 或 false。 |
fixed_anchor? |
true 或 false。 |
inspect |
String 表示的 self。 |
pos |
字节位置。 |
rest |
目标子字符串。 |
rest_size |
目标子字符串的大小。 |
string |
存储的字符串。 |
匹配¶ ↑
StringScanner 通过 Ruby 类 Regexp 实现模式匹配,其匹配行为与 Ruby 的相同,但 固定锚点属性 除外。
匹配器方法¶ ↑
每个匹配器方法 都接受一个参数 pattern,并尝试在 目标子字符串 中查找匹配的子字符串。
方法 |
模式类型 | 匹配目标子字符串 | 成功返回 | 可能会更新位置? |
|---|---|---|---|---|
check |
Regexp 或 String。 |
在开头。 | 匹配的子字符串。 | 否。 |
check_until |
Regexp 或 String。 |
任何位置。 | 子字符串。 | 否。 |
match? |
Regexp 或 String。 |
在开头。 | 匹配大小。 | 否。 |
exist? |
Regexp 或 String。 |
任何位置。 | 子字符串大小。 | 否。 |
scan |
Regexp 或 String。 |
在开头。 | 匹配的子字符串。 | 是。 |
scan_until |
Regexp 或 String。 |
任何位置。 | 子字符串。 | 是。 |
skip |
Regexp 或 String。 |
在开头。 | 匹配大小。 | 是。 |
skip_until |
Regexp 或 String。 |
任何位置。 | 子字符串大小。 | 是。 |
选择哪个匹配器取决于
-
您要在哪里查找匹配项
-
在目标子字符串中的任何位置:
check_until、exist?、scan_until、skip_until。
-
是否要
-
遍历,通过前进位置:
scan、scan_until、skip、skip_until。 -
保持位置不变:
check、check_until、match?、exist?。
-
-
您希望返回值是什么
-
子字符串:
check_until、scan_until。 -
子字符串大小:
exist?、skip_until。
匹配值¶ ↑
StringScanner 对象中的匹配值通常包含最近一次尝试匹配的结果。
每个匹配值都可以被认为是
-
清除:初始状态,或者在匹配尝试失败后:通常为
false、nil或{}。 -
设置:在匹配尝试成功后:
true、字符串、数组或哈希。
这些方法都会清除匹配值
这些方法中的每一种都尝试基于模式进行匹配,并且要么设置匹配值(如果成功),要么清除匹配值(如果失败);
基本匹配值¶ ↑
基本匹配值是指那些与捕获无关的值。
这些方法中的每一种都会返回一个基本匹配值
方法 |
匹配后返回 | 不匹配后返回 |
|---|---|---|
matched? |
true. |
false. |
matched_size |
匹配的子字符串的大小。 | nil. |
matched |
匹配的子字符串。 | nil. |
pre_match |
匹配的子字符串之前的子字符串。 | nil. |
post_match |
匹配的子字符串之后的子字符串。 | nil. |
请参见下面的示例。
捕获的匹配值¶ ↑
捕获的匹配值是指那些与 捕获 相关的值。
这些方法中的每一种都会返回一个捕获的匹配值
方法 |
匹配后返回 | 不匹配后返回 |
|---|---|---|
size |
捕获的子字符串的计数。 | nil. |
[](n) |
第 n 个捕获的子字符串。 |
nil. |
captures |
所有捕获的子字符串的 Array。 |
nil. |
values_at(*n) |
指定捕获的子字符串的 Array。 |
nil. |
named_captures |
命名捕获的 Hash。 |
{}. |
请参见下面的示例。
匹配值示例¶ ↑
成功的基本匹配尝试(无捕获)
scanner = StringScanner.new('foobarbaz') scanner.exist?(/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
失败的基本匹配尝试(无捕获);
scanner = StringScanner.new('foobarbaz') scanner.exist?(/nope/) match_values_cleared?(scanner) # => true
成功的未命名捕获匹配尝试
scanner = StringScanner.new('foobarbazbatbam') scanner.exist?(/(foo)bar(baz)bat(bam)/) put_match_values(scanner) # Basic match values: # matched?: true # matched_size: 15 # pre_match: "" # matched : "foobarbazbatbam" # post_match: "" # Captured match values: # size: 4 # captures: ["foo", "baz", "bam"] # named_captures: {} # values_at: ["foobarbazbatbam", "foo", "baz", "bam", nil] # []: # [0]: "foobarbazbatbam" # [1]: "foo" # [2]: "baz" # [3]: "bam" # [4]: nil
成功的命名捕获匹配尝试;与上面的未命名捕获相同,但 named_captures 除外
scanner = StringScanner.new('foobarbazbatbam') scanner.exist?(/(?<x>foo)bar(?<y>baz)bat(?<z>bam)/) scanner.named_captures # => {"x"=>"foo", "y"=>"baz", "z"=>"bam"}
失败的未命名捕获匹配尝试
scanner = StringScanner.new('somestring') scanner.exist?(/(foo)bar(baz)bat(bam)/) match_values_cleared?(scanner) # => true
失败的命名捕获匹配尝试;与上面的未命名捕获相同,但 named_captures 除外
scanner = StringScanner.new('somestring') scanner.exist?(/(?<x>foo)bar(?<y>baz)bat(?<z>bam)/) match_values_cleared?(scanner) # => false scanner.named_captures # => {"x"=>nil, "y"=>nil, "z"=>nil}
固定锚点属性¶ ↑
StringScanner 中的模式匹配与 Ruby 中的相同,但其固定锚点属性除外,该属性确定 '\A' 的含义
-
false(默认值):匹配当前字节位置。scanner = StringScanner.new('foobar') scanner.scan(/\A./) # => "f" scanner.scan(/\A./) # => "o" scanner.scan(/\A./) # => "o" scanner.scan(/\A./) # => "b"
-
true:匹配目标子字符串的开头;除非字节位置为零,否则永远不匹配scanner = StringScanner.new('foobar', fixed_anchor: true) scanner.scan(/\A./) # => "f" scanner.scan(/\A./) # => nil scanner.reset scanner.scan(/\A./) # => "f"
固定锚点属性在创建 StringScanner 对象时设置,并且可能无法修改(请参见 StringScanner.new);方法 fixed_anchor? 返回设置。
公共类方法
源
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;
}
返回一个新的 StringScanner 对象,其 存储的字符串 是给定的 string;设置 固定锚点属性
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_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]));
}
返回捕获的子字符串或 nil;请参见 捕获的匹配值。
当存在捕获时
scanner = StringScanner.new('Fri Dec 12 1975 14:39') scanner.scan(/(?<wday>\w+) (?<month>\w+) (?<day>\d+) /)
-
specifier为零:返回整个匹配的子字符串scanner[0] # => "Fri Dec 12 " scanner.pre_match # => "" scanner.post_match # => "1975 14:39"
-
specifier为正整数。返回第n个捕获,如果超出范围则返回nilscanner[1] # => "Fri" scanner[2] # => "Dec" scanner[3] # => "12" scanner[4] # => nil
-
specifier为负整数。从最后一个子组向后计数scanner[-1] # => "12" scanner[-4] # => "Fri Dec 12 " scanner[-5] # => nil
-
specifier为符号或字符串。返回命名的子组,如果没有则返回nilscanner[:wday] # => "Fri" scanner['wday'] # => "Fri" scanner[:month] # => "Dec" scanner[:day] # => "12" scanner[:nope] # => nil
当没有捕获时,只有 [0] 返回非 nil
scanner = StringScanner.new('foobarbaz') scanner.exist?(/bar/) scanner[0] # => "bar" scanner[1] # => nil
对于失败的匹配,即使是 [0] 也返回 nil
scanner.scan(/nope/) # => nil scanner[0] # => nil scanner[1] # => nil
源
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;
}
返回 位置 是否在行的开头;即在 存储的字符串 的开头或紧跟在换行符之后
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? 是 StringScanner#beginning_of_line? 的别名。
源
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;
}
如果最近一次匹配尝试成功,则返回索引为 (1..) 的 捕获的匹配值 数组,否则返回 nil
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_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)));
}
调用序列:charpos -> character_position
返回 字符位置(初始值为零),该位置可能与方法 pos 给出的 字节位置 不同
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.getch # => "こ" # 3-byte character. scanner.getch # => "ん" # 3-byte character. put_situation(scanner) # Situation: # pos: 6 # charpos: 2 # rest: "にちは" # rest_size: 9
源
static VALUE
strscan_check(VALUE self, VALUE re)
{
return strscan_do_scan(self, re, 0, 1, 1);
}
尝试在 目标子字符串 的开头 匹配 给定的 pattern;不修改 位置。
如果匹配成功
-
返回匹配的子字符串。
-
设置所有 匹配值。
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
如果匹配失败
-
返回
nil。 -
清除所有 匹配值。
scanner.check(/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);
}
尝试在 目标子字符串 中的任何位置(在任何 位置)匹配 给定的 pattern;不修改 位置。
如果匹配成功
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
如果匹配失败
-
清除所有 匹配值。
-
返回
nil。
scanner.check_until(/nope/) # => nil match_values_cleared?(scanner) # => true
源
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;
}
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_exist_p(VALUE self, VALUE re)
{
return strscan_do_scan(self, re, 0, 0, 0);
}
尝试在 目标子字符串 中的任何位置(在任何 位置)匹配 给定的 pattern;不修改 位置。
如果匹配成功
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
如果匹配失败
-
返回
nil。 -
清除所有 匹配值。
scanner.exist?(/nope/) # => nil match_values_cleared?(scanner) # => true
源
static VALUE
strscan_fixed_anchor_p(VALUE self)
{
struct strscanner *p;
p = check_strscan(self);
return p->fixed_anchor_p ? Qtrue : Qfalse;
}
返回是否设置了 固定锚点属性。
源
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]));
}
调用序列:get_byte -> byte_as_character 或 nil
返回下一个字节(如果可用)
-
scanner = StringScanner.new(HIRAGANA_TEXT) # => #<StringScanner 0/15 @ "\xE3\x81\x93\xE3\x82..."> scanner.string # => "こんにちは" [scanner.get_byte, scanner.pos, scanner.charpos] # => ["\xE3", 1, 1] [scanner.get_byte, scanner.pos, scanner.charpos] # => ["\x81", 2, 2] [scanner.get_byte, scanner.pos, scanner.charpos] # => ["\x93", 3, 1] [scanner.get_byte, scanner.pos, scanner.charpos] # => ["\xE3", 4, 2] [scanner.get_byte, scanner.pos, scanner.charpos] # => ["\x82", 5, 3] [scanner.get_byte, scanner.pos, scanner.charpos] # => ["\x93", 6, 2]
-
否则,返回
nil,并且不更改位置。scanner.terminate [scanner.get_byte, scanner.pos, scanner.charpos] # => [nil, 15, 5]
源
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]));
}
调用序列:getch -> 字符或 nil
返回下一个(可能是多字节)字符,如果可用
-
如果位置位于字符的开头
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" [scanner.getch, scanner.pos, scanner.charpos] # => ["こ", 3, 1] [scanner.getch, scanner.pos, scanner.charpos] # => ["ん", 6, 2] [scanner.getch, scanner.pos, scanner.charpos] # => ["に", 9, 3] [scanner.getch, scanner.pos, scanner.charpos] # => ["ち", 12, 4] [scanner.getch, scanner.pos, scanner.charpos] # => ["は", 15, 5] [scanner.getch, scanner.pos, scanner.charpos] # => [nil, 15, 5]
-
如果位置位于多字节字符内(即,不在其开头),则行为类似于
get_byte(返回 1 字节字符)scanner.pos = 1 [scanner.getch, scanner.pos, scanner.charpos] # => ["\x81", 2, 2] [scanner.getch, scanner.pos, scanner.charpos] # => ["\x93", 3, 1] [scanner.getch, scanner.pos, scanner.charpos] # => ["ん", 6, 2]
-
如果位置位于存储的字符串的末尾,则返回
nil且不修改位置scanner.terminate [scanner.getch, scanner.pos, scanner.charpos] # => [nil, 15, 5]
源
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;
}
返回 self 的字符串表示形式,其中可能显示
scanner = StringScanner.new("Fri Dec 12 1975 14:39") scanner.pos = 11 scanner.inspect # => "#<StringScanner 11/21 \"...c 12 \" @ \"1975 ...\">"
如果在字符串的开头,则省略上面的第 4 项(后面的子字符串)
scanner.reset scanner.inspect # => "#<StringScanner 0/21 @ \"Fri D...\">"
如果在字符串的末尾,则省略上面的所有项
scanner.terminate scanner.inspect # => "#<StringScanner fin>"
源
static VALUE
strscan_match_p(VALUE self, VALUE re)
{
return strscan_do_scan(self, re, 0, 0, 1);
}
尝试在 目标子字符串 的开头 匹配 给定的 pattern;不修改 位置。
如果匹配成功
-
设置匹配值。
-
返回匹配的子字符串的大小(以字节为单位)。
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
如果匹配失败
-
清除匹配值。
-
返回
nil。 -
不递增位置。
scanner.match?(/nope/) # => nil match_values_cleared?(scanner) # => true
源
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]));
}
如果最近的匹配尝试成功,则返回匹配的子字符串,否则返回 nil;请参阅基本匹配值
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_p(VALUE self)
{
struct strscanner *p;
GET_SCANNER(self, p);
return MATCHED_P(p) ? Qtrue : Qfalse;
}
如果最近的匹配尝试成功,则返回 true,否则返回 false;请参阅基本匹配值
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_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]);
}
如果最近的匹配匹配尝试成功,则返回匹配的子字符串的大小(以字节为单位),否则返回 nil;请参阅基本匹配值
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_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;
}
如果最近的匹配尝试成功,则返回索引为 (1..) 的捕获匹配值的数组,否则返回 nil;请参阅捕获匹配值
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_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);
}
返回子字符串 string[pos, length];不更新匹配值或位置
scanner = StringScanner.new('foobarbaz') scanner.pos = 3 scanner.peek(3) # => "bar" scanner.terminate scanner.peek(3) # => ""
源
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));
}
查看当前字节并将其作为整数返回。
s = StringScanner.new('ab') s.peek_byte # => 97
调用序列:pos -> byte_position
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.pos # => 0 scanner.getch # => "こ" # 3-byte character. scanner.charpos # => 1 scanner.pos # => 3
调用序列:pos = n -> n pointer = n -> n
不影响匹配值。
对于非负数 n,将位置设置为 n
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.pos = 3 # => 3 scanner.rest # => "んにちは" scanner.charpos # => 1
对于负数 n,从存储的字符串的末尾开始计数
scanner.pos = -9 # => -9 scanner.pos # => 6 scanner.rest # => "にちは" scanner.charpos # => 2
源
static VALUE
strscan_get_pos(VALUE self)
{
struct strscanner *p;
GET_SCANNER(self, p);
return INT2FIX(p->curr);
}
调用序列:pos -> byte_position
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.pos # => 0 scanner.getch # => "こ" # 3-byte character. scanner.charpos # => 1 scanner.pos # => 3
源
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);
}
调用序列:pos = n -> n pointer = n -> n
不影响匹配值。
对于非负数 n,将位置设置为 n
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.pos = 3 # => 3 scanner.rest # => "んにちは" scanner.charpos # => 1
对于负数 n,从存储的字符串的末尾开始计数
scanner.pos = -9 # => -9 scanner.pos # => 6 scanner.rest # => "にちは" scanner.charpos # => 2
源
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));
}
如果最近的匹配尝试成功,则返回匹配的子字符串之后的子字符串,否则返回 nil;请参阅基本匹配值
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_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]));
}
如果最近的匹配尝试成功,则返回匹配的子字符串之前的子字符串,否则返回 nil;请参阅基本匹配值
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_reset(VALUE self)
{
struct strscanner *p;
GET_SCANNER(self, p);
p->curr = 0;
CLEAR_MATCH_STATUS(p);
return self;
}
将字节位置和字符位置都设置为零,并清除匹配值;返回 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_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));
}
返回存储的字符串的“其余部分”(当前位置之后的所有部分),即目标子字符串
scanner = StringScanner.new('foobarbaz') scanner.rest # => "foobarbaz" scanner.pos = 3 scanner.rest # => "barbaz" scanner.terminate scanner.rest # => ""
源
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);
}
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_scan(VALUE self, VALUE re)
{
return strscan_do_scan(self, re, 1, 1, 1);
}
调用序列:scan(pattern) -> 子字符串 或 nil
如果匹配成功
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.pos = 6 scanner.scan(/に/) # => "に" put_match_values(scanner) # Basic match values: # matched?: true # matched_size: 3 # pre_match: "こん" # matched : "に" # post_match: "ちは" # Captured match values: # size: 1 # captures: [] # named_captures: {} # values_at: ["に", nil] # []: # [0]: "に" # [1]: nil put_situation(scanner) # Situation: # pos: 9 # charpos: 3 # rest: "ちは" # rest_size: 6
如果匹配失败
-
返回
nil。 -
不递增字节和字符位置。
-
清除匹配值。
scanner.scan(/nope/) # => nil match_values_cleared?(scanner) # => true
源
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;
}
扫描一个字节并将其作为整数返回。此方法对多字节字符不敏感。另请参阅:getch。
源
static VALUE
strscan_scan_until(VALUE self, VALUE re)
{
return strscan_do_scan(self, re, 1, 1, 0);
}
调用序列:scan_until(pattern) -> 子字符串 或 nil
尝试在目标子字符串中的任何位置(任何位置)匹配给定的 pattern。
如果匹配尝试成功
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.pos = 6 scanner.scan_until(/ち/) # => "にち" put_match_values(scanner) # Basic match values: # matched?: true # matched_size: 3 # pre_match: "こんに" # matched : "ち" # post_match: "は" # Captured match values: # size: 1 # captures: [] # named_captures: {} # values_at: ["ち", nil] # []: # [0]: "ち" # [1]: nil put_situation(scanner) # Situation: # pos: 12 # charpos: 4 # rest: "は" # rest_size: 3
如果匹配尝试失败
-
清除匹配数据。
-
返回
nil。 -
不更新位置。
scanner.scan_until(/nope/) # => nil match_values_cleared?(scanner) # => true
源
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);
}
如果最近的匹配尝试成功,则返回捕获的计数,否则返回 nil;请参阅捕获匹配值
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_skip(VALUE self, VALUE re)
{
return strscan_do_scan(self, re, 1, 0, 1);
}
调用序列:skip(pattern) match_size 或 nil
如果匹配成功
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.pos = 6 scanner.skip(/に/) # => 3 put_match_values(scanner) # Basic match values: # matched?: true # matched_size: 3 # pre_match: "こん" # matched : "に" # post_match: "ちは" # Captured match values: # size: 1 # captures: [] # named_captures: {} # values_at: ["に", nil] # []: # [0]: "に" # [1]: nil put_situation(scanner) # Situation: # pos: 9 # charpos: 3 # rest: "ちは" # rest_size: 6 scanner.skip(/nope/) # => nil match_values_cleared?(scanner) # => true
源
static VALUE
strscan_skip_until(VALUE self, VALUE re)
{
return strscan_do_scan(self, re, 1, 0, 0);
}
调用序列:skip_until(pattern) -> matched_substring_size 或 nil
尝试在目标子字符串中的任何位置(任何位置)匹配给定的 pattern;不修改位置。
如果匹配尝试成功
-
设置匹配值。
-
返回匹配的子字符串的大小。
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.pos = 6 scanner.skip_until(/ち/) # => 6 put_match_values(scanner) # Basic match values: # matched?: true # matched_size: 3 # pre_match: "こんに" # matched : "ち" # post_match: "は" # Captured match values: # size: 1 # captures: [] # named_captures: {} # values_at: ["ち", nil] # []: # [0]: "ち" # [1]: nil put_situation(scanner) # Situation: # pos: 12 # charpos: 4 # rest: "は" # rest_size: 3
如果匹配尝试失败
-
清除匹配值。
-
返回
nil。
scanner.skip_until(/nope/) # => nil match_values_cleared?(scanner) # => true
源
static VALUE
strscan_get_string(VALUE self)
{
struct strscanner *p;
GET_SCANNER(self, p);
return p->str;
}
返回存储的字符串
scanner = StringScanner.new('foobar') scanner.string # => "foobar" scanner.concat('baz') scanner.string # => "foobarbaz"
源
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;
}
将存储的字符串替换为给定的 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_terminate(VALUE self)
{
struct strscanner *p;
GET_SCANNER(self, p);
p->curr = S_LEN(p);
CLEAR_MATCH_STATUS(p);
return self;
}
调用序列:terminate -> self
将扫描器设置为字符串末尾;返回 self
scanner = StringScanner.new(HIRAGANA_TEXT) scanner.string # => "こんにちは" scanner.scan_until(/に/) put_situation(scanner) # Situation: # pos: 9 # charpos: 3 # rest: "ちは" # rest_size: 6 match_values_cleared?(scanner) # => false scanner.terminate # => #<StringScanner fin> put_situation(scanner) # Situation: # pos: 15 # charpos: 5 # rest: "" # rest_size: 0 match_values_cleared?(scanner) # => true
源
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;
}
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
如果匹配值被清除,则引发异常
scanner.scan(/nope/) # => nil match_values_cleared?(scanner) # => true scanner.unscan # Raises StringScanner::Error.
源
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;
}
返回捕获的子字符串数组,如果没有则返回 nil。
对于每个 specifier,返回的子字符串为 [specifier];请参阅[]。
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_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;
}
返回 self 的浅拷贝;副本中的存储的字符串与 self 中的字符串相同。
源
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);
}
源
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);
}