《R數據科學》學習筆記|Note9:使用stringr處理字符串(上)

使用stringr處理字符串

本周內容較多且雜,故分成上下兩篇。正則表達式是從左到右來匹配一個字符串的。“Regular Expression”這個詞太長了,我們通常使用它的縮寫“regex”或者“regexp”。 正則表達式可以被用來替換字符串中的文本、驗證表單、基于模式匹配從一個字符串中提取字符串等等。關于正則表達式,GitHub上有個很好的項目,如果大家有興趣的話,可以點贊、在看,我這章之后更新項目筆記。

[TOC]

9.1 簡介

本章將介紹 R 中的字符串處理。包括字符串的基本工作原理,以及如何手工創建字符串,但本章的重點是正則表達式(regular expression,regexp)。正則表達式的用處非常大,字符串通常包含的是非結構化或半結構化數據,正則表達式可以用簡練的語言來描述字符串中的模式。

9.2 字符串基礎

可以使用單引號或雙引號來創建字符串。與其他語言不同,單引號和雙引號在 R 中沒有區別。我們推薦使用 ",除非你想要創建包含多個 " 的一個字符串:

string1 <- "This is a string"
string2 <- 'To put a "quote" inside a string, use single quotes'

如果忘記了結尾的引號,你會看到一個 +,這是一個續行符:

> "This is a string without a closing quote
+

如果遇到了這種情況,可以按 Esc 鍵,然后重新輸入。

如果想要在字符串中包含一個單引號雙引號,可以使用 \ 對其進行“轉義”:

double_quote <- "\"" # or '"'
single_quote <- '\'' # or "'"

這意味著,如果想要在字符串中包含一個反斜杠,就需要使用兩個反斜杠:\\

字符串的打印形式與其本身的內容不是相同的,因為打印形式中會顯示出轉義字 符。如果想要查看字符串的初始內容,可以使用 writelines() 函數:

x <- c("\"", "\\")
x
> [1] "\"" "\\"
writeLines(x)
> "
> \

還有其他幾種特殊字符。最常用的是換行符 \n制表符 \t,你可以使用 ?'"'?"'" 調 出幫助文件來查看完整的特殊字符列表。

多個字符串通常保存在一個字符向量中,使用 c() 函數來創建字符向量:

c("one", "two", "three")
> [1] "one" "two" "three"

9.2.1 字符串長度

stringr 中的函數比R基礎包中的字符串處理函數名稱更直觀,并且都是以 str_ 開頭的。例如,str_length() 函數可以返回字符串中的字符數量:

str_length(c("a", "R for data science", NA))
> [1] 1 18 NA

9.2.2 字符串組合

要想組合兩個或更多字符串,可以使用 str_c() 函數:

str_c("x", "y")
> [1] "xy"
str_c("x", "y", "z")
> [1] "xyz"

可以使用 sep 參數來控制字符串間的分隔方式:

str_c("x", "y", sep = ", ")
> [1] "x, y"

和多數 R 函數一樣,缺失值是可傳染的。如果想要將它們輸出為 "NA",可以使用 str_ replace_na()

x <- c("abc", NA)
str_c("|-", x, "-|")
> [1] "|-abc-|" NA
str_c("|-", str_replace_na(x), "-|")
> [1] "|-abc-|" "|-NA-|"

如以上代碼所示,str_c() 函數是向量化的,它可以自動循環短向量,使得其與最長的向量具有相同的長度:

str_c("prefix-", c("a", "b", "c"), "-suffix")
> [1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"

長度為 0 的對象會被無聲無息地丟棄。這與 if 結合起來特別有用:

name <- "Hadley"
time_of_day <- "morning"
birthday <- FALSE
str_c(
 "Good ", time_of_day, " ", name,
 if (birthday) " and HAPPY BIRTHDAY", # birthday 為True時,才會打印后面的語句
 "."
)
> [1] "Good morning Hadley."

要想將字符向量合并為字符串,可以使用 collapse() 函數:

str_c(c("x", "y", "z"), collapse = ", ")
> [1] "x, y, z"

9.2.3 字符串取子集

可以使用 str_sub() 函數來提取字符串的一部分。除了字符串參數外,str_sub() 函數中還 有 startend 參數,它們給出了子串的位置(包括 startend 在內):

x <- c("Apple", "Banana", "Pear")
str_sub(x, 1, 3) # 開始、結束的位置參數
> [1] "App" "Ban" "Pea"
# 負數表示從后往前數
str_sub(x, -3, -1)
> [1] "ple" "ana" "ear"

注意,即使字符串過短,str_sub() 函數也不會出錯,它將返回盡可能多的字符:

str_sub("a", 1, 5)
> [1] "a"

還可以使用 str_sub() 函數的賦值形式來修改字符串:

str_sub(x, 1, 1) <- str_to_lower(str_sub(x, 1, 1)) #函數將文本轉換為小寫
x
> [1] "apple" "banana" "pear"

9.3 使用正則表達式進行模式匹配

我們通過 str_view()str_view_all() 函數來學習正則表達式。這兩個函數接受一個字符向量和一個正則表達式,并顯示出它們是如何匹配的。

9.3.1 基礎匹配

最簡單的模式是精確匹配字符串 :

x <- c("apple", "banana", "pear")
str_view(x, "an")
10

另一個更復雜一些的模式是使用 .,它可以匹配任意字符(除了換行符):

str_view(x, ".a.")
11

但是,如果 . 可以匹配任意字符,那么如何匹配字符 . 呢?你需要使用一個“轉義”符號來告訴正則表達式實際上就是要匹配 . 這個字符,而不是使用 . 來匹配其他字符。和字符串一樣,正則表達式也使用反斜杠來去除某些字符的特殊含義。因此,如果要匹配 .,那么你需要的正則表達式就是 \.。遺憾的是,這樣做會帶來一個問題。因為我們使用字符串來表示正則表達 式,而且 \ 在字符串中也用作轉義字符,所以正則表達式 \. 的字符串形式應是 \\.

# 要想建立正則表示式,我們需要使用\\
dot <- "\\."
# 實際上表達式本身只包含一個\:
writeLines(dot)
#> \.
# 這個表達式告訴R搜索一個.
str_view(c("abc", "a.c", "bef"), "a\\.c")
12

如果 \ 在正則表達式中用作轉義字符,那么如何匹配 \ 這個字符呢?我們還是需要去除其特殊意義,建立形式為 \\ 的正則表達式。要想建立這樣的正則表達式,我們需要使用一個字符串,其中還需要對 \ 進行轉義。這意味著要想匹配字符 \,我們需要輸入 "\\\\"—— 你需要 4 個反斜杠來匹配 1 個反斜杠!

x <- "a\\b"
writeLines(x)
> a\b
str_view(x, "\\\\")
13

9.3.2 錨點

默認情況下,正則表達式會匹配字符串的任意部分。有時我們需要在正則表達式中設置錨點,以便 R 從字符串的開頭末尾進行匹配。我們可以設置兩種錨點:

  • ^ 從字符串開頭進行匹配。

  • $ 從字符串末尾進行匹配。

x <- c("apple", "banana", "pear")
str_view(x, "^a")
str_view(x, "a$")
14

[圖片上傳失敗...(image-8a207a-1614859299444)]

始于權力(^),終于金錢($

如果想要強制正則表達式匹配一個完整字符串,那么可以同時設置 ^$ 這兩個錨點:

x <- c("apple pie", "apple", "apple cake")
str_view(x, "apple")
16
str_view(x, "^apple$")
17

還可以使用 \b 來匹配單詞間的邊界。例如,為了避免匹配到 summarizesummaryrowsum 等,我們會使用 \bsum\b 進行搜索。

9.3.3 字符類與字符選項

很多特殊模式可以匹配多個字符。我們已經介紹過 .,它可以匹配除換行符外的任意字符。 還有其他 4 種常用的字符類。

  • \d 可以匹配任意數字
  • \s 可以匹配任意空白字符(如空格、制表符和換行符)。
  • [abc] 可以匹配 a、b 或 c
  • [^abc] 可以匹配除 a、b、c 外的任意字符。

牢記,要想創建包含 \d\s 的正則表達式,你需要在字符串中對 \ 進行轉義,因此需 要輸入 "\\d" 或 "\\s"。

你還可以使用字符選項創建多個可選的模式。例如,abc|d..f 可以匹配 abcdeaf。注 意,因為 | 的優先級很低,所以 abc|xyz 匹配的是 abcxyz,而不是 abcyzabxyz。與 數學表達式一樣,如果優先級讓人感到困惑,那么可以使用括號讓其表達得更清晰一些:

str_view(c("grey", "gray"), "gr(e|a)y")
18

9.3.4 重復

正則表達式的另一項強大功能是,其可以控制一個模式能夠匹配多少次。

  • ?:0 次或 1 次。
  • +: 1 次或多次。
  • *:0 次或多次。
x <- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII"
str_view(x, "CC?")
22
str_view(x, "CC+")
20
str_view(x, 'C[LX]+')
21

注意,這些運算符的優先級非常高,因此使用 colou?r 既可以匹配 color,也可以匹配 colour。這意味著很多時候需要使用括號,比如 bana(na)+

還可以精確設置匹配的次數。

  • {n}:匹配 n 次。
  • {n,}:匹配 n 次或更多次。
  • {,m}:最多匹配 m 次。
  • {n, m}:匹配 nm 次。
str_view(x, "C{2}")
str_view(x, "C{2}")
str_view(x, "C{2}")
#大家自行觀察

默認的匹配方式是“貪婪的”:正則表達式會匹配盡量長的字符串。通過在正則表達式后面添加一個 ?,你可以將匹配方式更改為“懶惰的”,即匹配盡量短的字符串。

str_view(x, 'C{2,3}?')
23
str_view(x, 'C[LX]+?')
24

9.3.5 分組與回溯引用

前面學習了括號可以用于消除復雜表達式中的歧義。括號還可以定義“分組”, 你可以通過回溯引用(如 \1\2 等)來引用這些分組。例如,以下的正則表達式可以找出名稱中有重復的一對字母的所有水果:

fruit <- c("banana","coconut","cocumber","jujube","papaya","salal berry")
str_view(fruit, "(..)\\1", match = TRUE)
25
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容