學習軌道
/
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使用正規表達式,你必須提供第二個引數;使用空字串也沒關係。

以下範例會依任意空白切割字串。

"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")

說明

你受聘到一家正在開發 Chatbot 的公司,擔任正規表示式專家。

這項開發還處於非常初期的階段。 你的任務是運用正規表示式,提升 Chatbot 理解與產生自然語言的能力。

1. 檢查有效指令

Chatbot 不只聰明,還是個忠心的助手。 使用者必須把「Chatbot」這個詞放在指令的第一個位置,才能向 Chatbot 提問。 關鍵字是大寫還是小寫都沒關係。 重要的是這個詞出現的位置。

實作 is_valid_command 函式,協助 Chatbot 辨識使用者是否正在下指令。

"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. 移除加密的表情符號

Chatbot 很難理解人類如何用表情符號表達情緒。 Chatbot 收到使用者訊息時,每個表情符號都會以字串「emoji」加上一個 id 編號 來表示。

實作 remove_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. 檢查有效的電話號碼

在與 Chatbot 互動的某個時刻,使用者會提供電話號碼。 Chatbot 只能撥打特定格式的號碼。

實作 check_phone_number 函式。

如果號碼有效,Chatbot 會回覆一則訊息,感謝使用者並確認號碼。 如果號碼無效,Chatbot 會告知使用者該電話號碼無效。

預期的格式是 (+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. 取得網站連結

Chatbot 是一套非常好奇的軟體。 雖然它能上網搜尋特定主題,但它喜歡詢問使用者有哪些很酷的網站可以造訪,以找到相關資訊。

對話範例:

Chatbot:嘿,使用者,我想學怎麼用 JavaScript 寫程式。你知道有什麼很酷的網站可以讓我學習嗎?

使用者:我從 exercism.org 學到很多,那裡有很多很棒的資源。

實作 get_domains 函式,它會回傳網站網域的陣列。

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

5. 向使用者打招呼

有禮貌的 Chatbot 會用名字稱呼使用者。 當使用者自我介紹時,我們的 Chatbot 會偵測他們的名字,並以親切的問候回應。

撰寫 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 解析

抵擋不住「功能蔓延」的誘惑,我們要在 Chatbot 裡加一個 CSV 解析函式。

實作 parse_csv 函式,它接受一個字串,並回傳解析出的欄位陣列。 欄位分隔符號應該是「逗號加上選擇性的空白」。

我們不用擔心 CSV 格式的任何邊界情況(例如欄位中含有逗號)。

"first, second,third,   fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
jq Exercism

準備好開始 正規表達式聊天機器人 了嗎?

註冊 Exercism,透過 12 個概念75 個練習 和真人引導來學習並精通 jq,全部免費。