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'

这里的一些示例假设定义了某些辅助方法

请参阅示例[此处]

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=(new_string) 替换现有的存储字符串。
concat(more_string) 将字符串附加到现有的存储字符串。
string 返回存储的字符串。

位置

StringScanner 对象维护一个基于零的字节位置和一个基于零的字符位置

以下每个方法都显式设置位置

方法 作用
reset 将两个位置都设置为零(存储字符串的开头)。
terminate 将两个位置都设置为存储字符串的末尾。
pos=(new_byte_position) 设置字节位置;调整字符位置。

字节位置(位置)

字节位置(或简称为位置)是存储字符串中字节的基于零的索引;对于一个新的 StringScanner 对象,字节位置为零。

当字节位置是

要获取或设置字节位置

许多方法使用字节位置作为查找匹配的基础;许多其他方法设置、递增或递减字节位置

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.

某些方法隐式修改字节位置;请参阅

这些方法的值直接从 posstring 的值派生而来

字符位置

字符位置是存储字符串中字符的基于零的索引;对于新的 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? truefalse
charpos 字符位置。
eos? truefalse
fixed_anchor? truefalse
inspect String 表示的 self
pos 字节位置。
rest 目标子字符串。
rest_size 目标子字符串的大小。
string 存储的字符串。

匹配

StringScanner 通过 Ruby 类 Regexp 实现模式匹配,其匹配行为与 Ruby 的相同,但 固定锚点属性 除外。

匹配器方法

每个匹配器方法 都接受一个参数 pattern,并尝试在 目标子字符串 中查找匹配的子字符串。

方法 模式类型 匹配目标子字符串 成功返回 可能会更新位置?
check RegexpString 在开头。 匹配的子字符串。 否。
check_until RegexpString 任何位置。 子字符串。 否。
match? RegexpString 在开头。 匹配大小。 否。
exist? RegexpString 任何位置。 子字符串大小。 否。
scan RegexpString 在开头。 匹配的子字符串。 是。
scan_until RegexpString 任何位置。 子字符串。 是。
skip RegexpString 在开头。 匹配大小。 是。
skip_until RegexpString 任何位置。 子字符串大小。 是。


选择哪个匹配器取决于

匹配值

StringScanner 对象中的匹配值通常包含最近一次尝试匹配的结果。

每个匹配值都可以被认为是

这些方法都会清除匹配值

这些方法中的每一种都尝试基于模式进行匹配,并且要么设置匹配值(如果成功),要么清除匹配值(如果失败);

基本匹配值

基本匹配值是指那些与捕获无关的值。

这些方法中的每一种都会返回一个基本匹配值

方法 匹配后返回 不匹配后返回
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' 的含义

固定锚点属性在创建 StringScanner 对象时设置,并且可能无法修改(请参见 StringScanner.new);方法 fixed_anchor? 返回设置。