Ruby 用 re 模块以化整为零的方式构建正则表达式

weakish · 2014年03月20日 · 最后由 jex 回复于 2014年04月24日 · 2538 次阅读

正则表达式能以非常 concise 的形式表达复杂的逻辑,不过复杂的表达式难写、难读,比如下面这个匹配日期的正则表达式:

/\A((?:19|20)[0-9]{2})[\- \/.](0[1-9]|1[012])[\- \/.](0[1-9]|[12][0-9]|3[01])\z/

/x换成多行模式后可读性稍微好一点:

/\A((?:19|20)[0-9]{2})[\- \/.] #年
(0[1-9]|1[012])[\- \/.] # 月
(0[1-9]|[12][0-9]|3[01])\z #日
/x

而用了re 模块之后,无论是写起来,还是读起来,都很容易:

require 're'

include Re

delim                = re.any("- /.")
century_prefix       = re("19") | re("20")
under_ten            = re("0") + re.any("1-9")
ten_to_twelve        = re("1") + re.any("012")
ten_and_under_thirty = re.any("12") + re.any("0-9")
thirties             = re("3") + re.any("01")

year = (century_prefix + re.digit.repeat(2)).capture(:year)
month = (under_ten | ten_to_twelve).capture(:month)
day = (under_ten | ten_and_under_thirty | thirties).capture(:day)

date = (year + delim + month + delim + day).all

代码变长了,但是每个部分都很短小,也很容易验证。同时,捕获的部分可以通过相应的变量名访问:

result = date.match("2009-01-23")
result[:year]      # => "2009"
result[:month]     # => "01"
result[:day]       # => "23"

值得指出的是,re 不是 Ruby 的标准模块。而是第三方开发维护的。这里是链接: https://github.com/jimweirich/re

话说这个 gem 的作者好眼熟。貌似是不久前驾鹤西去的那个大牛。致以崇高敬意!

看起来不错,但就像 ActiveRecords 一样,让程序员离真相越来越远了,呵呵

不错。可读性大为改善。

?:19|20,这个是什么意思呢?没见过。

#4 楼 @chenge 不记录的分组……

#5 楼 @Kabie ?是 0 或 1 的意思吧。能举例解释下么?

#6 楼 @chenge 这里就是他想要匹配 19xx 年或者 20xx 年……但是又不想让结果的分组里出现一个(19|20), 所以给分组加上 ?: 前缀……就不会记录了……

http://www.ruby-doc.org/core-2.1.1/Regexp.html#class-Regexp-label-Grouping

#7 楼 @Kabie 谢谢,明白了。正则内容还是很不少。

#1 楼 @tylerlong 貌似还真是。。。。

匿名 #11 2014年04月06日

咋一看以为是 Parsec

#11 楼 @jex 被你一说发现稍微有点像

需要 登录 后方可回复, 如果你还没有账号请 注册新账号