35、Ruby 字符串与正则表达式全解析

Ruby 字符串与正则表达式全解析

1. 字符串操作基础

在 Ruby 中,字符串操作十分丰富,涵盖了字符转换、校验和计算、字符计数与删除等功能。

1.1 字符转换与去重

使用 tr_s 方法可以进行字符转换并去除重复字符。例如:

"bead".tr_s("aeiou", " ")     # => "b d"

此代码将字符串中的元音字母替换为空格,并去除重复的空格。

1.2 校验和计算

Ruby 提供了多种计算校验和的方法:
- sum 方法:计算弱 16 位校验和。

"hello".sum          # => 532
  • sum(8) 方法:计算 8 位校验和。
"hello".sum(8)       # => 20
  • crypt 方法:进行单向加密校验和计算,需传入两个字母数字字符作为 “盐”,结果可能因平台而异。
"hello".crypt("ab")  # => "abl0JrMf6tlhw"
1.3 字符计数、删除与去重
  • count 方法:用于统计特定字符的数量。
"hello".count('aeiou')  # => 2
  • delete 方法:删除特定字符。
"hello".delete('aeiou') # => "hll"
  • squeeze 方法:去除连续重复的字符。
"hello".squeeze('a-z')  # => "helo"

当有多个参数时,取它们的交集;以 ^ 开头的参数表示取反。

"hello".count('a-z', '^aeiou')   # => 3
"hello".delete('a-z', '^aeiou')  # => "eo"
2. 文本格式化

Ruby 支持两种将值插入字符串的技术:字符串插值和 printf 风格的格式化。

2.1 字符串插值

使用双引号字符串字面量可以进行任意 Ruby 表达式的插值。

n, animal = 2, "mice"
"#{n+1} blind #{animal}"  # => '3 blind mice'
2.2 printf 风格的格式化

String 类定义了格式化运算符 % Kernel 模块定义了全局的 printf sprintf 方法。这些方法和 % 运算符类似于 C 语言的 printf 函数,具有精确控制字段宽度、指定浮点数有效位数以及解耦格式化值和插值字符串的优点。

以下是一些使用 % 运算符的示例:

# 替代上述插值的方法
printf('%d blind %s', n+1, animal)  # Prints '3 blind mice', returns nil
sprintf('%d blind %s', n+1, animal) # => '3 blind mice'
'%d blind %s' % [n+1, animal]  # Use array on right if more than one argument

# 格式化数字
'%d' % 10         # => '10'
'%x' % 10         # => 'a'
'%X' % 10         # => 'A'
'%o' % 10         # => '12'
'%f' % 1234.567   # => '1234.567000'
'%e' % 1234.567   # => '1.234567e+03'
'%E' % 1234.567   # => '1.234567e+03'
'%g' % 1234.567   # => '1234.57'
'%g' % 1.23456E12 # => '1.23456e+12'

# 字段宽度
'%5s' % '<<<'     # '  <<<'
'%-5s' % '>>>'    # '>>>  '
'%5d' % 123       # '  123'
'%05d' % 123      # '00123'

# 精度
'%.2f' % 123.456  # '123.46'
'%.2e' % 123.456  # '1.23e+02'
'%.6e' % 123.456  # '1.234560e+02'
'%.4g' % 123.456  # '123.5'

# 字段和精度组合
'%6.4g' % 123.456 # ' 123.5'
'%3s' % 'ruby'    # 'ruby'
'%3.3s' % 'ruby'  # 'rub'

# 格式化多个参数
args = ['Syntax Error', 'test.rb', 20]
"%s: in '%s' line %d" % args    # => "Syntax Error: in 'test.rb' line 20"
"%2$s:%3$d: %1$s" % args        # => "test.rb:20: Syntax Error"
3. 二进制字符串的打包与解包

Ruby 的字符串可以存储二进制数据。 Array.pack String.unpack 方法在处理二进制文件格式或二进制网络协议时非常有用。

3.1 打包

使用 Array.pack 方法将数组元素编码为二进制字符串。

a = [1,2,3,4,5,6,7,8,9,10]
b = a.pack('i10')
3.2 解包

使用 String.unpack 方法将二进制字符串解码为数组。

c = b.unpack('i*')
c == a                      # => true

另一个示例:

m = 'hello world'
data = [m.size, m]
template = 'Sa*'
b = data.pack(template)     # => "\v\000hello world"
b.unpack(template)          # => [11, "hello world"]
4. 正则表达式基础

正则表达式用于描述文本模式,Ruby 的 Regexp 类实现了正则表达式, Regexp String 都定义了模式匹配方法和运算符。

4.1 正则表达式字面量

正则表达式字面量由斜杠字符分隔,后面可跟一个或多个可选的标志字符,用于指定模式匹配的额外信息。

/Ruby?/  # Matches the text "Rub" followed by an optional "y"
/ruby?/i  # Case-insensitive: matches "ruby" or "RUB", etc.
/./mu     # Matches Unicode characters in Multiline mode

允许的修饰符字符如下表所示:
| Modifier | Description |
| — | — |
| i | Ignore case when matching text. |
| m | The pattern is to be matched against multiline text, so treat newline as an ordinary character: allow . to match newlines. |
| x | Extended syntax: allow whitespace and comments in regexp. |
| o | Perform #{} interpolations only once, the first time the regexp literal is evaluated. |
| u,e,s,n | Interpret the regexp as Unicode (UTF - 8), EUC, SJIS, or ASCII. If none of these modifiers is specified, the regular expression is assumed to use the source encoding. |

除了使用斜杠分隔,还可以使用 %r 开头,后跟自定义分隔符。

%r|/|         # Matches a single slash character, no escape required
%r[</(.*)>]i  # Flag characters are allowed with this syntax, too

正则表达式语法中,一些字符有特殊含义,需要使用反斜杠进行转义。

/\(\)/     # Matches open and close parentheses
/\\/       # Matches a single backslash

正则表达式字面量可以包含转义字符,也支持使用 #{} 语法进行插值。

money = /[$\u20AC\u{a3}\u{a5}]/
prefix = ","
/#{prefix}\t/

插值通常在每次计算正则表达式字面量时重新进行,但使用 o 修饰符时,插值仅在代码首次解析时执行一次。

[1,2].map{|x| /#{x}/}   # => [/1/, /2/]
[1,2].map{|x| /#{x}/o}  # => [/1/, /1/]
4.2 正则表达式工厂方法

可以使用 Regexp.new Regexp.compile 方法创建正则表达式。

Regexp.new("Ruby?")                          # /Ruby?/
Regexp.new("ruby?", Regexp::IGNORECASE)      # /ruby?/i
Regexp.compile(".", Regexp::MULTILINE, "u")  # /./mu

使用 Regexp.escape 方法在将字符串传递给 Regexp 构造函数之前对特殊正则表达式字符进行转义。

pattern = "[a-z]+"
suffix = Regexp.escape("()")
r = Regexp.new(pattern + suffix)  # /[a-z]+\(\)/

在 Ruby 1.9 中, Regexp.union 方法可以创建一个模式,该模式是任意数量的字符串或 Regexp 对象的 “并集”。

pattern = Regexp.union("Ruby", "Perl", "Python", /Java(Script)?/)
Regexp.union("()", "[]", "{}")   # => /\(\)|\[\]|\{\}/
5. 正则表达式语法

正则表达式语法丰富多样,下面将详细介绍其各个元素。

5.1 字面字符

大多数字符在正则表达式中直接匹配自身。

/ruby/             # Match "ruby"
/¥/                # Matches Yen sign
5.2 字符类
  • [] :匹配括号内的任意单个字符。
/[Rr]uby/          # Match "Ruby" or "ruby"
  • [^] :匹配不在括号内的任意单个字符。
/[^aeiou]/         # Match anything other than a lowercase vowel
5.3 特殊字符类
字符类 匹配内容
. 除换行符外的任意字符,使用 m 选项可匹配换行符。
\d 数字,等同于 [0-9]
\D 非数字,等同于 [^0-9]
\s 空白字符,等同于 [ \t\r\n\f]
\S 非空白字符,等同于 [^ \t\r\n\f]
\w 单词字符,等同于 [A-Za-z0-9_]
\W 非单词字符,等同于 [^A-Za-z0-9_]
5.4 重复
  • ? :匹配零个或一个字符。
/ruby?/            # Match "rub" or "ruby"
  • * :匹配零个或多个字符。
/ruby*/            # Match "rub" plus 0 or more ys
  • + :匹配一个或多个字符。
/ruby+/            # Match "rub" plus 1 or more ys
  • {n} :匹配恰好 n 个字符。
/\d{3}/            # Match exactly 3 digits
  • {n,} :匹配 n 个或更多字符。
/\d{3,}/           # Match 3 or more digits
  • {n,m} :匹配至少 n 个且至多 m 个字符。
/\d{3,5}/          # Match 3, 4, or 5 digits
5.5 非贪婪重复

默认情况下,重复是 “贪婪” 的,即尽可能匹配更多的字符。使用 ? 可以实现非贪婪匹配,即尽可能匹配最少的字符。

/<.*>/             # Greedy repetition: matches "<ruby>perl>"
/<.*?>/            # Nongreedy: matches "<ruby>" in "<ruby>perl>"
5.6 分组与反向引用
  • 分组:使用括号将表达式分组。
/(\D\d)+/          # Grouped: + repeats \D\d pair
  • 反向引用:使用 \1 \2 等引用之前匹配的分组。
/([Rr])uby&\1ails/ # Match ruby&rails or Ruby&Rails
5.7 命名分组与反向引用(Ruby 1.9)
/(?<first>\w)(?<second>\w)\k<second>\k<first>/
5.8 替代

使用 | 表示替代,匹配其中一个表达式。

/ruby|rube/        # Match "ruby" or "rube"
5.9 锚点

锚点用于指定匹配位置:
| 锚点 | 匹配位置 |
| — | — |
| ^ | 行开头 |
| $ | 行结尾 |
| \A | 字符串开头 |
| \Z | 字符串结尾(如果字符串以换行符结尾,则匹配换行符之前) |
| \z | 字符串结尾 |
| \G | 上一次匹配结束的位置 |
| \b | 单词边界 |
| \B | 非单词边界 |
| (?=re) | 正向预查断言,确保后面的字符匹配 re ,但不包含这些字符在匹配结果中。 |
| (?!re) | 负向预查断言,确保后面的字符不匹配 re 。 |
| (?<=re) | 正向回顾断言,确保前面的字符匹配 re ,但不包含这些字符在匹配结果中(Ruby 1.9)。 |
| (?<!re) | 负向回顾断言,确保前面的字符不匹配 re (Ruby 1.9)。 |

5.10 特殊语法与括号
/R(?#comment)/     # Matches "R". All the rest is a comment
/R(?i)uby/         # Case-insensitive while matching "uby"
/rub(?:y|le))/     # Group only without creating \1 backreference
6. 正则表达式模式匹配

Ruby 的基本模式匹配运算符是 =~ ,一个操作数必须是正则表达式,另一个必须是字符串。

pattern = /Ruby?/i
pattern =~ "backrub"    # Returns 4.
"rub ruby" =~ pattern   # 0
pattern =~ "r"          # nil

使用 =~ 运算符后,若匹配成功,全局变量 $~ 会保存一个 MatchData 对象,包含匹配的完整信息。

"hello" =~ /e\w{2}/     # 1
$~.string               # "hello"
$~.to_s                 # "ell"
$~.pre_match            # "h"
$~.post_match           # "o"

$~ 是一个特殊的线程局部和方法局部变量,不同线程和方法使用时互不影响。也可以使用 Regexp.last_match 方法替代 $~

当正则表达式包含括号内的子表达式时, MatchData 对象可以提供更多信息。

pattern = /(Ruby|Perl)(\s+)(rocks|sucks)!/
text = "Ruby\trocks!"
pattern =~ text
data = Regexp.last_match
data.size                 # => 4
data[0]                   # => "Ruby\trocks!"
data[1]                   # => "Ruby"
data[2]                   # => "\t"
data[3]                   # => "rocks"

在 Ruby 1.9 中,若模式包含命名捕获, MatchData 对象可以像哈希表一样使用,以捕获组的名称作为键。

pattern = /(?<lang>Ruby|Perl) (?<ver>\d(\.\d)+) (?<review>rocks|sucks)!/
if (pattern =~ "Ruby 1.9.1 rocks!")
  $~[:lang]            # => "Ruby"
end

综上所述,Ruby 提供了丰富的字符串操作和正则表达式功能,通过合理运用这些功能,可以高效地处理各种文本和模式匹配任务。

Ruby 字符串与正则表达式全解析

7. 正则表达式语法总结

为了更清晰地理解和使用正则表达式,下面对其语法进行总结:
| 语法类型 | 具体语法 | 匹配内容 |
| — | — | — |
| 字符类 | . | 除换行符外的任意字符,使用 m 选项可匹配换行符 |
| | [...] | 括号内的任意单个字符 |
| | [^...] | 不在括号内的任意单个字符 |
| | \w | 单词字符,等同于 [A-Za-z0-9_] |
| | \W | 非单词字符,等同于 [^A-Za-z0-9_] |
| | \s | 空白字符,等同于 [ \t\n\r\f] |
| | \S | 非空白字符,等同于 [^ \t\n\r\f] |
| | \d | 数字,等同于 [0–9] |
| | \D | 非数字,等同于 [^0–9] |
| 序列、替代、分组与引用 | ab | 表达式 a 后跟表达式 b |
| | a|b | 表达式 a 或表达式 b |
| | (re) | 分组,将 re 组合为一个语法单元,可与 * + ? | 等一起使用,同时捕获匹配的文本 |
| | (?:re) | 分组,但不捕获匹配的文本 |
| | (?<name>re) | 分组并捕获匹配的文本,同时为子表达式命名(Ruby 1.9) |
| | (?'name're) | 与 (?<name>re) 类似,单引号可替代尖括号(Ruby 1.9) |
| | \1...\9 | 匹配第 n 个分组子表达式匹配的相同文本 |
| | \10... | 若有足够多的前一个子表达式,则匹配第 n 个分组子表达式匹配的相同文本;否则,匹配指定八进制编码的字符 |
| | \k<name> | 匹配命名捕获组 name 匹配的相同文本 |
| | \g<n> | 再次匹配组 n n 可以是组名或组号(Ruby 1.9) |
| 重复 | re* | re 的零个或多个匹配 |
| | re+ | re 的一个或多个匹配 |
| | re? | re 的零个或一个匹配 |
| | re{n} | re 的恰好 n 个匹配 |
| | re{n,} | re n 个或更多匹配 |
| | re{n,m} | re 的至少 n 个且至多 m 个匹配 |
| 锚点 | ^ | 行开头 |
| | $ | 行结尾 |
| | \A | 字符串开头 |
| | \Z | 字符串结尾(如果字符串以换行符结尾,则匹配换行符之前) |
| | \z | 字符串结尾 |
| | \G | 上一次匹配结束的位置 |
| | \b | 单词边界(在括号外),退格符(在括号内) |
| | \B | 非单词边界 |
| | (?=re) | 正向预查断言,确保后面的字符匹配 re ,但不包含这些字符在匹配结果中 |
| | (?!re) | 负向预查断言,确保后面的字符不匹配 re |
| | (?<=re) | 正向回顾断言,确保前面的字符匹配 re ,但不包含这些字符在匹配结果中(Ruby 1.9) |
| | (?<!re) | 负向回顾断言,确保前面的字符不匹配 re (Ruby 1.9) |
| 杂项 | (?onflags-off-flags) | 不匹配任何内容,但打开 onflags 指定的标志,关闭 offflags 指定的标志 |
| | (?onflags-off-flags:x) | 匹配 x ,仅对该子表达式应用指定的标志 |
| | (?#...) | 注释,括号内的所有文本被忽略 |
| | (?>re) | 独立匹配 re ,不考虑匹配是否导致表达式其余部分匹配失败 |

8. 正则表达式使用流程示例

下面通过一个 mermaid 流程图展示使用正则表达式进行模式匹配的一般流程:

graph TD;
    A[定义正则表达式] --> B[定义待匹配的字符串];
    B --> C[使用 =~ 运算符进行匹配];
    C --> D{是否匹配成功};
    D -- 是 --> E[获取 MatchData 对象];
    E --> F[处理匹配结果];
    D -- 否 --> G[输出未匹配信息];
9. 综合应用示例

以下是一个综合应用字符串操作和正则表达式的示例,用于验证用户输入的电子邮件地址是否合法:

# 定义验证电子邮件地址的正则表达式
email_pattern = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

# 定义用户输入的电子邮件地址
user_email = "example@example.com"

# 进行匹配
if email_pattern =~ user_email
  puts "电子邮件地址合法"
else
  puts "电子邮件地址不合法"
end

在这个示例中,首先定义了一个用于验证电子邮件地址的正则表达式 email_pattern ,然后定义了用户输入的电子邮件地址 user_email ,最后使用 =~ 运算符进行匹配,并根据匹配结果输出相应的信息。

10. 总结

通过对 Ruby 字符串操作和正则表达式的详细介绍,我们了解到 Ruby 在文本处理方面提供了丰富而强大的功能。从字符串的基本操作,如字符转换、校验和计算、字符计数与删除,到文本格式化、二进制字符串的打包与解包,再到正则表达式的创建、语法规则和模式匹配,每一个部分都有其独特的用途和应用场景。

在实际开发中,我们可以根据具体需求灵活运用这些功能。例如,在处理用户输入时,可以使用正则表达式进行输入验证;在处理二进制数据时,可以使用 Array.pack String.unpack 方法进行数据的编码和解码;在生成报告时,可以使用字符串格式化功能来控制输出的格式。

总之,掌握 Ruby 的字符串操作和正则表达式将有助于我们更高效地处理各种文本和数据,提升开发效率和代码质量。

为了更好地掌握这些知识,建议大家多进行实践,通过编写不同的代码示例来加深对各个知识点的理解和运用。同时,在实际项目中遇到问题时,要善于查阅相关文档,不断积累经验,从而能够熟练运用 Ruby 的这些强大功能。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值