學習軌道
/
R
R
/
練習
/
日誌等級
日誌等級

日誌等級

學習練習

簡介

背景

字串是引號之間的一段文字。 目前任何受支援的 R 版本都會以 UTF-8 編碼字串,因此世上大多數語言都能獲得支援。

mystring <- "some text"
mystring
#> [1] "some text"
typeof(mystring)
#> [1] "character"

可惜的是,對熟悉其他語言的程式設計師來說,這套術語可能會造成混淆。

雖然type是character,但 R 並不會區分單一字母(在其他幾種語言裡稱為char,但在 R 裡_不是_)和長字串。 兩者都是單一項目,而且可以互換地放在單引號' '或雙引號" "裡。

風格指南建議使用雙引號,但如果你想在字串內使用雙引號而不將它們跳脫時,則是例外。

str2 <- 'String with "quotes"'  # convenience format
str2
#> [1] "String with \"quotes\""    # standard format

因此,character vector就是由字串組成的向量,每個字串都是單一元素。

s <- c("some", "strings")
s
#> [1] "some"    "strings"
length(s)
#> [1] 2
length(s[1])  # the number of vector elements, not the string length
#> [1] 1

nchar(s[1])  # the number of characters in the string "some"
#> [1] 4

在上面的例子中,length(s[1])等同於length("some"):它算的是_向量元素_的數量,而不是_字串長度_。

操作單一字串顯然是可行的,就像上面例子中的nchar()一樣,但需要一些特定的函式,這些會在下面討論。

字串函式

在 base R 裡,大多數事情都_做得到_,但許多程式設計師會覺得字串操作的支援相當有限又令人困惑。

幸運的是,有tidyverse套件,直覺、有彈性又自我一致得多。

base R

仍有大量舊有程式碼使用舊式字串函式。 這包括 Exercism 上大多數社群解法。

此外,對於小型任務,使用內建函式而不是匯入大型套件可能更方便。 R 軌道的維護者在撰寫範例解法來驗證練習時,往往採用這種做法。

因此,了解一些舊做法仍然值得學習。

nchar

回傳字串中的字元數。

nchar("string")
#> [1] 6

nchar("Hrōðgār") # a person in Beowulf, written in Old English
#> [1] 7

「字元」究竟代表什麼意思,這個(複雜!)主題超出了我們的範圍。 只要留意上面例子中的幾點就好:

  • 雖然內部是以 C 實作,但 R 的字串並不是以\0結尾(那樣會讓長度加 1)。
  • 多位元組的 Unicode 字元會如你所預期或希望的那樣處理。「Hrōðgār」有 7 個字元(也稱為「runes」或「code points」),但需要 10 個位元組來儲存。
paste 和 paste0

將任意數量的字串合併成單一字串。

paste可以選擇性地指定分隔符號:預設是空格。

paste0(v)等同於paste(v, sep = "")。

paste("bits", "of", "a", "string")
#> [1] "bits of a string"
paste0("bits", "of", "a", "string")
#> [1] "bitsofastring"
sprintf

為了更精細地控制字串的組合,R 從 C 複製了sprintf函式(以及幾種後續的語言:Wikipedia 列出了大約 30 種)。

r <- 5.3
pi <- 3.14159
sprintf("A circle of radius %.1f has area %.2f", r, pi * r^2)
#> [1] "A circle of radius 5.3 has area 88.25"
trimws

開頭和/或結尾的空白可以用trimws()移除:

s <- "    messy string   "
trimws(s)
#> [1] "messy string"

有個which = "left"(或 "right")參數可以避免同時修剪兩端。

substr

作用於單一字串,並回傳兩個(含端點)界限之間的子字串。

substr("Exercism", 5, 7)
#> [1] "cis"
strsplit

依照分隔符號字串,把字串分割成一個 R 陣列。 分隔符號是必填的(沒有預設值),但可以是空字串。

strsplit("R, Python, Julia", ", ")
[[1]]
#> [1] "R"      "Python" "Julia" 

strsplit("Exercism", "")
[[1]]
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"

陣列會在另一篇 概念 中介紹。 如果你還沒讀到課程大綱的那個部分,現在只要知道unlist()函式(在這個情況裡)會把陣列轉換成向量就好。

unlist(strsplit("Exercism", ""))
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"

Tidyverse

寬容一點來說,我們可以說舊式字串函式並不是 R 能提供的最好東西(比較不寬容的看法也是有的)。

為了改善這個情況,stringr套件自 2009 年以來持續開發,如今是 Tidyverse 集合的重要一員。

用library(stringr)或library(tidyverse)將它匯入你的命名空間。

一些主要功能包括:

  • 所有函式名稱都有str_前綴,並力求讓用途清楚。
  • 引數的順序力求自我一致,把輸入資料當作第一個引數。
  • 輸入可以是單一字串,也可以是複雜的資料結構。函式會盡量配合,做出最合理的處理。

和所有 Tidyverse 套件一樣,stringr有良好的說明文件,通常由套件作者撰寫。

岔題:管線快速入門

使用管線|>來串接函式的做法,會在課程大綱的其他部分更詳細地討論。

現在只要知道以下幾點就很有幫助:

  • 管線左邊的結果會成為右邊函式的第一個引數(這裡的「右邊」會忽略可有可無的換行)。
  • 其餘的引數可以一併放進函式呼叫裡。
  • 在 R 裡,我們一定要把管線接到函式_呼叫_,而不只是_名稱_,所以括號一律是必要的(這點和其他一些語言不同)。

下面的例子示範了這個語法。 個別的函式之後會再討論。

library(stringr)

"Monday-25-February" |> 
  str_split("-") |>     
  unlist() |>               # [1] "Monday"   "25"       "February"
  str_sub(1, 3) |>          # [1] "Mon" "25"  "Feb"
  str_to_upper() |>         # [1] "MON" "25"  "FEB"
  str_flatten(collapse = ", ")
#> [1] "MON, 25, FEB"
管理長度

str_length會計算碼點(類似nchars)。

還有各種其他函式會處理空白,包括str_trim()和str_squish()。 這通常是任何資料科學專案開始時的重要清理工作。

# The first 3 kernels in JuPyteR notebooks:
str_length(c("Julia", "Python", "R"))           #> [1] 1 6 5
#> [1] 5 6 1
str_pad(c("Julia", "Python", "R"), 8, "right")  # add spaces if necessary
#> [1] "Julia   " "Python  " "R       "

str_trim("Julia    Python    R    ")   # remove leading & trailing whitespace
#> [1] "Julia    Python    R"
str_squish("Julia   Python  R    ")  # trim, collapse multiple spaces 
#> [1] "Julia Python R"
取字串子集

str_sub(string, start, end)函式會回傳從start到end(含端點)的子字串,而str_sub_all()可以作用於多個字串。

它非常有彈性。 start和end分別預設為第一個和最後一個字元。 它們可以是索引的向量。 和向量索引比起來,令人樂見的是,負數會從字串尾端往回數,就像 Python 那樣。

s <- "abcdefgh"
str_sub(s, 2, 4)
#> [1] "bcd"
str_sub(s, 5)
#> [1] "efgh"
str_sub(s, -3, -2)
#> [1] "fg"
str_sub(s, c(1, 4), c(2, 5))
#> [1] "ab" "de"
str_sub(s, c(1, 4, 6), c(2, 5, -1))
#> [1] "ab"  "de"  "fgh"

s |> str_sub(2, 4) # these functions are designed for pipes
#> [1] "bcd"
串接字串

str_flatten會把字元向量轉換成單一字串。 collapse引數會插入每個元素之間,預設為""。

c("R", "Julia", "Python") |> str_flatten()
#> [1] "RJuliaPython"
c("R", "Julia", "Python") |> str_flatten(collapse = " - ")
#> [1] "R - Julia - Python"

str_c會把多個字元向量轉換成單一的字元向量,並視需要重複使用元素。

str_c(LETTERS[1:8], 1:8, sep = ":")
#> [1] "A:1" "B:2" "C:3" "D:4" "E:5" "F:6" "G:7" "H:8"

str_glue為 R 帶來簡單的字串插值。 大括號{ }內的運算式會被解讀,並替換成文字表示。

r <- 5.3
pi <- 3.14159
str_glue("A circle of radius {r} has area {pi * r^2}")
A circle of radius 5.3 has area 88.2472631
分割字串

R 一個很常見的用途,就是把雜亂的資料整理成一致的格式以便分析。 為了幫助做到這點,有幾種把文字分割成子字串的方法。 大多超出了目前的範圍。

str_split_1最簡單,它會把單一字串轉換成片段組成的向量,並在某個指定的模式處分割(沒有預設模式)。

s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R"      "Julia"  "Python"
變更字串

有些語言(主要是歐洲語言)會區分大寫和小寫字母。 有一組stringr函式可以依地區設定的方式,在大寫和小寫之間互相轉換:

  • str_to_lower()
  • str_to_upper()
  • str_to_title():每個單字的第一個字母轉為大寫
  • str_to_sentence():句子的第一個字母轉為大寫
str_to_upper("elixir")    # defaults to locale = "en"
#> [1] "ELIXIR"
str_to_upper("ελληνικά")  # infers Greek
#> [1] "ΕΛΛΗΝΙΚΆ"

str_to_title("the cat sat on the mat")
#> [1] "The Cat Sat On The Mat"
str_to_sentence("the cat sat on the mat")
#> [1] "The cat sat on the mat"

若要根據模式比對來替換子字串,str_replace()會改變模式第一次出現的地方,str_replace_all()則會改變所有出現的地方。

若要根據字元索引來替換子字串,str_sub() 有一種賦值形式。

 s <- "abcde"
str_sub(s, 3, 4) <- "yz"
s
#> [1] "abyze"

說明

在這個練習中,你會處理日誌行。

每一行日誌都是一個格式如下的字串:"[<LEVEL>]: <MESSAGE>"。

日誌等級有三種:

  • INFO
  • WARNING
  • ERROR

你有三個任務,每個任務都會拿到一行日誌,並要你對它做點處理。

你可以自由使用 base R 的舊式函式,或是stringr套件,兩者都能寫出簡短可行的解法。

1. 從日誌行取得訊息

實作message函式,回傳日誌行的訊息:

message("[ERROR]: Invalid operation")
#> "Invalid operation"

開頭或結尾的空白都應該被移除:

message("[WARNING]:  Disk almost full\r\n")
#> "Disk almost full"

2. 從日誌行取得日誌等級

實作log_level函式,回傳日誌行的日誌等級,而且要以小寫回傳:

log_level("[ERROR]: Invalid operation")
#> "error"

3. 重新格式化日誌行

實作reformat函式,重新格式化日誌行,把訊息放在前面,並在後面用小括號加上日誌等級:

reformat("[INFO]: Operation completed")
#> "Operation completed (info)"
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
R Exercism

準備好開始 日誌等級 了嗎?

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