D-11, Ruby 正规表达式(三) 字符 && Regular Expression Matching

经过两天,开始看得懂/a{3,}?/i是什麽意思了。

这是一个比较歪楼的比喻。

这是一段随意从网路上抓下来的Regexp
写得很严谨,e-mail网址的状况都有设想到。

/^(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6})$/i

但在某些状况下与这段是一样的意思。

/^.+@.+$/

例如:

2.7.3 :020 > reg = /^(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6})$/i

2.7.3 :022 > mail = "[email protected]"
 => "[email protected]"

2.7.3 :023 > mail.match?(reg)
 => true
2.7.3 :024 > reg = /^.+@.+$/
 => /^.+@.+$/
2.7.3 :025 > mail = "[email protected]"
 => "[email protected]"
2.7.3 :026 > mail.match?(reg)
 => true

/^.+@.+$/这样也过了!?先不论够不够严谨(因为如果我这样写连@@@这样也能过),重点在於再严谨的表达式也要搭配正确的方法使用,如果没有正确的方法,资料库内也是只会出现一堆奇奇怪怪的mail,当然email的涵盖范围很大,各种形式的mail都有可能出现,去验证e-mail到底正不正确,不如花时间去查这mail注册过没或是否uniq,或是假设mail不正确时,怎麽让注册不通过或让用户修改。

正规表达式用在检测资料内有无特定资料,会比去规定用户输入正不正确时轻松,例如密码要大小写英文,不可以11111123123这样。

结论是Regexp不适合验证email??!!


天书时间

/./:点代表任何字元包含符号,但只有m模式下可匹配\n

/abc/: 这样等於要匹配有"abc"的部分。

/[abc]/: 这样等於a or b or c。另外特殊字元被[]包住失去效果,成为单纯符号。

2.7.3 :055 > /[$%]/.match("ac%$%")
 => #<MatchData "%">
2.7.3 :056 > "ac%$%".scan(/[$%]/)
 => ["%", "$", "%"]

/[^abc]/:这样等於不要a or b or c

2.7.3 :029 > "applebc".scan(/[^abc]/)
 => ["p", "p", "l", "e"]

/[a-z]//[A-Z]/: Any single character in the range。
/[0-9]/: Any single number in the range。
突然不会讲中文...

\w:英文、数字与底线。
\W:非英文、数字与底线。

2.7.3 :068 > /\w*/.match("apple")
 => #<MatchData "apple">
2.7.3 :069 > /\w/.match("apple")
 => #<MatchData "a">

\d:数字。
\D:非数字。

\s:空白字元,包括空白、定位、换行、换页。 它们 ====> ( \t\r\n\f\v)
\S:非空白字元。

2.7.3 :094 > "You are a\ngood boy".scan(/(\s)/)
 => [[" "], [" "], ["\n"], [" "]]
2.7.3 :095 > "You are a\ngood boy".scan(/(\S)/)
 => [["Y"], ["o"], ["u"], ["a"], ["r"], ["e"], ["a"], ["g"], ["o"], ["o"], ["d"], ["b"], ["o"], ["y"]]

锚(补充)

(?=regexp):确保後面的资料符合regexp。

2.7.3 :109 > /\w*+@(?=(gmail.com))/.match?("[email protected]")
 => false
2.7.3 :110 > /\w*+@(?=(gmail.com))/.match?("[email protected]")
 => true

(?!regexp):确保後面的资料符合regexp。

2.7.3 :111 > /\w*+@(?!(gmail.com))/.match?("[email protected]")
 => false

(?<=regexp):确保前面的资料符合regexp。

2.7.3 :120 > /\d*(?<=0978)/.match("0978123111")
 => #<MatchData "0978">
2.7.3 :121 > /\d*(?<=0978)/.match?("0978123111")
 => true
2.7.3 :122 > /\d*(?<=0978)/.match?("0968123111")
 => false

(?<!regexp):确保前面的资料符合regexp。


实作看看吧

请多爱用令人尊敬且有爱的大神制作的Rubular:https://rubular.com/

def taiwan_mobile_number(nums)
  reg = /\A((\+8869\d{2})|(09\d{2}))+((\d{6})|(\-+\d{3}){2}|(\s+\d{3}){2})\z/
  nums.match?(reg)
end

puts taiwan_mobile_number("0954 666 666") #=> true
puts taiwan_mobile_number("0905-999-999") #=> true
puts taiwan_mobile_number("0905999888") #=> true
puts taiwan_mobile_number("+886913-333-333") #=> true
puts taiwan_mobile_number("+886913555666") #=> true

这题其实非常简单,利用已经有的确定实例,来拼凑出Regexp
我把+8869..09..为第一区块,也可以把+8860就当第一区块。
-...-...\s...\s...当第二区块,因为有-後面就不该有\s,所以直接都重复两次。
当然一定有更漂亮写法,我只是依照资料以及需求来写。

回头认识一开始那段e-mail的测试Regexp

先不管其中是不是有更好的写法。

/^(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6})$/i

第一个区块。

(|(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*

[A-Za-Z0_9]+_+ : 大小写英文与数字组合,加1或多个__没有特殊意思,不需反斜线。
[A-Za-z0-9]+-+ : 大小写英文与数字组合,加1或多个--需要反斜线。
[A-Za-z0-9]+.+ : 大小写英文与数字组合,指定加1或多个.\.代表要真正的.
[A-Za-z0-9]+++ : 大小写英文与数字组合,加1或多个+
这四个可能的组合会出现0或多次,所以有一个*()外。

第二区块。

[A-Za-z0-9]+ #大小写英文与数字组合出现1或多次。

这个蛮重要的@前面不可以有._@这些符号。

第三区块@,代表一定会有一个@

第四区块。@後面。

((\w+\-+)|(\w+\.))*

(\w+-+) : 任意非符号包含_字元加1或多个-
(\w+.) : 任意非符号包含_,可以加真正的.
以上组合可以出现0或多次所以()*

第五区块。

\w{1,63}\.[a-zA-Z]{2,6}

任意字元最少1个最多63个,一定有一个.,接2~6个大小写英文字母。

虽然这边似乎怪怪的,e-mail有可能会有复数/(\.[a-zA-Z])*/,不过没关系,目的在认识别人写的Regexp

虽然我把正确表达式用三天分享很混,希望有机会能让自己再次遇到Regexp不会再那麽陌生。


今天的leetcode10. Regular Expression Matching
题目连结:https://leetcode.com/problems/regular-expression-matching/
题目重点:p是一个实体,将实体变成Regexp,记得用#{}

# @param {String} s
# @param {String} p
# @return {Boolean}
def is_match(s, p)
  reg = /(#{p})/
  s.match(reg)
  s == $1
  # s.match(/(#{p})/)
  # s == $1
end
2.7.3 :039 > p = "a"
 => "a"
2.7.3 :040 > reg = /(#{p})/
 => /(a)/
2.7.3 :041 > s = "aa"
 => "aa"
2.7.3 :042 > s.match(reg)
 => #<MatchData "a" 1:"a">
2.7.3 :043 > s == $1
 => false

非常Easy的一题对吧,但这题其实被leetcode分类为Hard喔。

当熟悉表达式这些天书符号後,可以再多看看Regexp里面的一些方法,对於leetcode的一些对字串检查或替换的问题,也是比上面花不了多几个字就解决了。


<<:  Day5-React Hook 篇-认识 useContext

>>:  Day04 - Next.js 的 file-based routing

[DAY6]Channal access token是什麽?

对於网上可用的服务,通常使用通过ID和密码进行身份验证,以验证使用者是否有权使用该服务。LINE 开...

.obj 之绘制 & Skybox

大家好,我是西瓜,你现在看到的是 2021 iThome 铁人赛『如何在网页中绘制 3D 场景?从 ...

SQL与NoSQL的连结(二)

接续前次实作. 由於资料转换需要透过一个 instance 运作, 先建立 Replication ...

二元树之 IF 上策 - DAY 17

假如用人数去施打疫苗图表 人数是概略计算非准确值 算一下总触发 IF 次数 348.5万 * 1 +...

一辈子被雷打中的机率也被我遇到了

最喜欢的是窗外清晨时的一望无际的雪白,心灵就像被大自然安抚,获得平静 今天来聊聊那近三年空服生涯中...