轨道
/
jq
jq
/
练习
/
正则聊天机器人
正则聊天机器人

正则聊天机器人

学习练习

简介

正则表达式

正则表达式(regex)是一串用于指定文本搜索模式的字符。

学习正则表达式的语法超出了本主题的范围。 我们将重点介绍jq提供的那些用于使用正则表达式的表达式。

正则表达式流派

不同的工具实现的正则表达式版本各不相同。 jq内置了 Oniguruma 正则表达式库,它与 Perl v5.8 的正则表达式大体兼容。

jq使用的具体语法可以在 Oniguruma 的 GitHub 仓库中找到。

Caution

jq并没有为正则表达式提供任何特殊的语法。 它们只是以字符串的形式表示。 这意味着正则表达式中的任何反斜杠都需要在字符串中转义。

例如,数字字符类(\d)必须写成"\\d"。

正则表达式函数

jq中的正则表达式仅限于一组过滤器。

简单匹配

当你需要判断一个字符串是否匹配某个模式时,使用test过滤器。

STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])

这个过滤器会输出一个_布尔_结果。

"Hello World!" | test("W")    # => true
"Goodbye Mars" | test("W")    # => false
关于匹配的信息

当你需要提取真正匹配该模式的子字符串时,使用match过滤器。

STRING | match(REGEX)
STRING | match(REGEX; FLAGS)
STRING | match([REGEX, FLAGS])

这个过滤器会输出:

  • 如果没有匹配,则什么也不输出,或者
  • 如果匹配成功,则输出一个包含各种属性的对象。

下面这个例子使用反向引用语法\1来查找两个相同且连续的元音。

"Hello World!" | match("([aeiou])\\1")
# => empty

"Goodbye Mars" | match("([aeiou])\\1")
# => {
#      "offset": 1,
#      "length": 2,
#      "string": "oo",
#      "captures": [
#        {
#          "offset": 1,
#          "length": 1,
#          "string": "o",
#          "name": null
#        }
#      ]
#    }

match过滤器会为_每一个_匹配项返回一个对象。 下面这个例子演示了用"g"标志找出所有元音。

"Goodbye Mars" | match("[aeiou]"; "g")
# => { "offset": 1, "length": 1, "string": "o", "captures": [] }
#    { "offset": 2, "length": 1, "string": "o", "captures": [] }
#    { "offset": 6, "length": 1, "string": "e", "captures": [] }
#    { "offset": 9, "length": 1, "string": "a", "captures": [] }
捕获的子字符串

与match过滤器类似,如果匹配成功,capture过滤器会返回一个对象。

STRING | capture(REGEX)
STRING | capture(REGEX; FLAGS)
STRING | capture([REGEX, FLAGS])

返回的对象是命名捕获的映射。

"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# => {
#      "project": "JIRAISSUE",
#      "issue_num": "1234"
#    }
只取子字符串

scan过滤器类似于带"g"标志的match。

STRING | scan(REGEX)
STRING | scan(REGEX; FLAGS)

scan会输出一个子字符串_流_。

"Goodbye Mars" | scan("[aeiou]")
# => "o"
#    "o"
#    "e"
#    "a"

使用[...]数组构造器来捕获这些子字符串。

"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]
拆分字符串

如果你知道字符串中想要保留的部分,就使用match或scan。 如果你知道想要丢弃的部分,就使用split。

STRING | split(REGEX; FLAGS)
Caution

只接收一个参数的split过滤器会把它的实参当作固定字符串。

要在split中使用正则表达式,你必须提供第 2 个实参;使用空字符串也可以。

下面是一个按任意空白字符拆分字符串的例子。

"first   second           third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
替换

sub和gsub过滤器可以转换输入字符串,把输入中匹配的部分替换为替换字符串。

如果只想替换第一个匹配项,使用sub。 如果想替换所有匹配项,使用gsub。

STRING | sub(REGEX; REPLACEMENT)
STRING | sub(REGEX; REPLACEMENT; FLAGS)
STRING | gsub(REGEX; REPLACEMENT)
STRING | gsub(REGEX; REPLACEMENT; FLAGS)
"Goodnight kittens. Goodnight mittens." | sub("night"; " morning")
# => "Good morning kittens. Goodnight mittens."

"Goodnight kittens. Goodnight mittens." | gsub("night"; " morning")
# => "Good morning kittens. Good morning mittens."

替换文本可以引用匹配到的子字符串;使用命名捕获和字符串插值。

"Some 3-letter acronyms: gnu, csv, png"
| gsub( "\\b(?<tla>[[:alpha:]]{3})\\b";     # find words 3 letters long
        "\(.tla | ascii_upcase)" )          # upper-case the match
# => "Some 3-letter acronyms: GNU, CSV, PNG"

标志

在上面所有过滤器中,FLAGS 是一个字符串,由零个或多个受支持的标志组成。

  • g - 全局搜索(查找所有匹配项,而不只是第一个)
  • i - 不区分大小写的搜索
  • m - 多行模式('.' 会匹配换行符)
  • n - 忽略空匹配
  • p - 同时启用 s 和 m 两种模式
  • s - 单行模式('^' -> '\A','$' -> '\Z')
  • l - 查找尽可能长的匹配
  • x - 扩展正则表达式格式(忽略空白和注释)

例如

"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")

# or with Extended formatting

"JIRAISSUE-1234" | capture("
                     (?<project>   \\w+ )  # the Jira project
                     -                     # followed by a hyphen
                     (?<issue_num> \\d+ )  # followed by digits
                   "; "x")

说明

你受雇于一家正在开发聊天机器人的公司,担任正则表达式专家。

它正处于非常初级的开发阶段。 你的任务是使用正则表达式来提升聊天机器人理解和生成自然语言的能力。

1. 检查有效的命令

聊天机器人不仅聪明,还是一位忠诚的助手。 要向聊天机器人提问,用户必须在命令的第一个位置说出“Chatbot”这个词。 关键字是大写还是小写都没有关系。 重要的是这个词出现的位置。

实现函数 is_valid_command,它帮助聊天机器人识别用户何时在发出命令。

"Chatbot, play a song from the 80's." | is_valid_command
# => true
"Hey Chatbot, where is the closest pharmacy?" | is_valid_command
# => false
"CHATBOT, do you have a solution for this challenge?" | is_valid_command
# => true

2. 移除加密的表情符号

聊天机器人很难理解人类如何用表情符号来表达情绪。 当聊天机器人收到用户消息时,每个表情符号都表示为字符串“emoji”后跟一个 id 编号。

实现 remove_emoji 方法,它接收一个字符串,并移除消息中所有的表情符号。

不含表情符号的行应当原样返回。 只需移除 emoji 字符串。 不要调整空白字符。

"I love playing videogames emoji3465 it's one of my hobbies" | remove_emoji
# => "I love playing videogames  it's one of my hobbies"

3. 检查有效的电话号码

在与聊天机器人交互的某个时刻,用户会提供一个电话号码。 聊天机器人只能拨打特定格式的号码。

实现 check_phone_number 函数。

如果号码有效,聊天机器人会回复一条消息,感谢用户并确认号码。 如果号码无效,聊天机器人会告知用户该电话号码无效。

期望的格式是(+NN) NNN-NNN-NNN,其中 N 是数字。

"chatbot my phone number is (+34) 659-771-594" | check_phone_number
# => "Thanks! Your phone number is OK."
"chatbot, call me at 659-771-594" | check_phone_number
# => "Oops, it seems like I can't reach out to 659-771-594."

4. 获取网站链接

聊天机器人是一款非常好奇的软件。 尽管它能上网搜索某个特定主题,但它还是喜欢问用户有哪些值得访问的网站,以便找到相关信息。

示例对话:

聊天机器人:嘿,用户,我想学习用 JavaScript 编程。你知道有什么不错的网站可以让我学习吗?

用户:我从 exercism.org 学到了很多,那里有很多很棒的内容。

实现函数 get_domains,它返回一个网站域名数组。

"I learned a lot from exercism.org and google.com" | get_domains
# => ["exercism.org", "google.com"]

5. 问候用户

有礼貌的聊天机器人会用名字称呼用户。 当用户自我介绍时,我们的聊天机器人会识别出他们的名字,并友好地打招呼。

编写函数 nice_to_meet_you。

如果输入字符串包含“My name is Someone.”,就捕获其中的名字,并返回字符串“Nice to meet you, Someone.”。

"My name is Jean-Luc" | nice_to_meet_you
# => "Nice to meet you, Jean-Luc"

6. 非常简单的 CSV 解析

抵挡不住“功能蔓延”的诱惑,我们要给聊天机器人添加一个 CSV 解析函数。

实现 parse_csv 函数,它接收一个字符串,并返回由解析结果字段组成的数组。 字段分隔符应该是“逗号加可选的空白”。

我们不必担心 CSV 格式的任何边界情况(例如字段中包含逗号)。

"first, second,third,   fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
通过 GitHub 编辑 链接将在新窗口或新标签页中打开
jq Exercism

准备好开始 正则聊天机器人 了吗?

注册 Exercism,借助 12 个概念75 个练习 和真人导师指导,学习并掌握 jq,全部免费。