stringr包處理字符串(字符串長度、組合、取子集、正則表達式模式匹配)


使用stringr處理字符串

用於字符串處理的 stringr 包。stringr 不是tidyverse 核心 R 包的一部分,我們需要使用命令來加載它。

library(tidyverse)

library(stringr)

1.2 字符串基礎

可以使用單引號或雙引號來創建字符串。單引號和雙引號在 R 中沒有區別。

string1 <- "This is a string"

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

加號,這是一個續行符:

如果想要查看字符串的初始內容,可以使用 writelines() 函數:

x <- c("\"", "\\")

x

writeLines(x)

 

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

c("one", "two", "three")

 

1.2.1 字符串長度

str_length() 函數可以返回字符串中的字符數量:

str_length(c("a", "R for data science", NA))

 

1.2.2 字符串組合

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

str_c("x", "y")

 

str_c("x", "y", "z")

 

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

str_c("x", "y", sep = ", ")

 

如果想要將它們輸出為 "NA",可以使用 str_replace_na()

x <- c("abc", NA)

str_c("|-", x, "-|")

 

str_c("|-", str_replace_na(x), "-|")

 

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

str_c("prefix-", c("a", "b", "c"), "-suffix")

 

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

name <- "Hadley"

time_of_day <- "morning"

birthday <- FALSE

str_c(

 "Good ", time_of_day, " ", name,

 if (birthday) " and HAPPY BIRTHDAY",

 "."

)

 

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

str_c(c("x", "y", "z"), collapse = ", ")

 

1.2.3 字符串取子集

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

x <- c("Apple", "Banana", "Pear")

str_sub(x, 1, 3)

 

# 負數表示從后往前數

str_sub(x, -3, -1)

 

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

str_sub("a", 1, 5)

 

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

str_sub(x, 1, 1) <- str_to_lower(str_sub(x, 1, 1))

x

 

1.2.4 區域設置

str_to_lower() 函數將文本轉換為小寫,str_to_upper()函數將文本轉換為大寫。可以通過明確區域設置來選擇使用哪種規則。

# 土耳其語中有帶點和不帶點的兩個i,它們在轉換為大寫時是不同的:

str_to_upper(c("i", "ı"))

 

str_to_upper(c("i", "ı"), locale = "tr")

 

R 基礎包中的 order() sort() 函數使用當前區域設置對字符串進行排序。更強大的可以使用 str_sort() str_order() 函數,它們可以使用 locale 參數來進行區域設置:

x <- c("apple", "eggplant", "banana")

str_sort(x, locale = "en")  # 英語

 

str_sort(x, locale = "haw")  # 夏威夷語

 

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

我們通過 str_view() str_view_all() 函數來學習正則表達式。這兩個函數接受一個字符

向量和一個正則表達式,並顯示出它們是如何匹配的

1.3.1 基礎匹配

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

x <- c("apple", "banana", "pear")

str_view(x, "an")

 

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

str_view(x, ".a.")

 

如果 . 可以匹配任意字符,那么如何匹配字符 . 需要使用一個“轉義”符號來告訴正則表達式實際上就是要匹配 . 這個字符,而不是使用 . 來匹配其他字符。而且 \ 在字符串中也用作轉義字符,所以正則表達式 \. 的字符串形式應是 \\. :

# 要想建立正則表示式,我們需要使用\\

dot <- "\\."

# 實際上表達式本身只包含一個\

writeLines(dot)

 

# 這個表達式告訴R搜索一個.

str_view(c("abc", "a.c", "bef"), "a\\.c")

 

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

x <- "a\\b"

writeLines(x)

 

str_view(x, "\\\\")

 

1.3.3 錨點

有時我們需要在正則表達式中設置錨點,以便 R 從字符串的開頭或末尾進行匹配。我們可以設置兩種錨點。

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

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

x <- c("apple", "banana", "pear")

str_view(x, "^a")

 

str_view(x, "a$")

 

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

x <- c("apple pie", "apple", "apple cake")

str_view(x, "apple")

 

str_view(x, "^apple$")

 

1.3.5 字符類與字符選項

.  它可以匹配除換行符外的任意字符。還有其他 4 種常用的字符類

•  \d 可以匹配任意數字。

•  \s 可以匹配任意空白字符(如空格、制表符和換行符)

•  [abc] 可以匹配 ab c

•  [^abc] 可以匹配除 abc 外的任意字符。

要想創建包含 \d \s 的正則表達式,你需要在字符串中對 \ 進行轉義,因此需要輸入 "\\d" "\\s"abc|xyz 匹配的是 abc xyz

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

 

1.3.7 重復

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

•  ?0 次或 1 次。

•  +1 次或多次。

•  *0 次或多次。

x <- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII"

str_view(x, "CC?")

 

str_view(x, "CC+")

 

str_view(x, 'C[LX]+')

 

可以精確設置匹配的次數

•  {n}:匹配 n 次。

•  {n,}:匹配 n 次或更多次。

•  {,m}:最多匹配 m 次。

•  {n, m}:匹配 n m 次。

str_view(x, "C{2}")   #匹配2次

 

str_view(x, "C{2,}")

 

str_view(x, "C{2,3}")

 

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

str_view(x, 'C{2,3}?')

 

str_view(x, 'C[LX]+?')

 

1.3.9 分組與回溯引用

括號還可以定義分組,你可以通過回溯引用(如 \1\2 等)來引用這些分組。

str_view(fruit, "(..)\\1", match = TRUE)

 

1.4 工具

1.4.1 匹配檢測

要想確定一個字符向量能否匹配一種模式,可以使用 str_detect() 函數

x <- c("apple", "banana", "pear")

str_detect(x, "e")

 

sum(str_detect(words, "^t")) # 有多少個以t開頭的常用單詞?

 

mean(str_detect(words, "[aeiou]$")) # 以元音字母結尾的常用單詞的比例是多少?

 

找出不包含元音字母的所有單詞:

# 找出至少包含一個元音字母的所有單詞,然后取反

no_vowels_1 <- !str_detect(words, "[aeiou]")

# 找出僅包含輔音字母(非元音字母)的所有單詞

no_vowels_2 <- str_detect(words, "^[^aeiou]+$")

identical(no_vowels_1, no_vowels_2)

 

str_detect() 函數的一種常見用法是選取出匹配某種模式的元素。你可以通過邏輯取子集

方式來完成這種操作,也可以使用便捷的 str_subset() 包裝器函數:

words[str_detect(words, "x$")]   #匹配最后一個字符x

 

str_subset(words, "x$")

 

字符串通常會是數據框的一列,此時我們可以使用 filter 操作:

df <- tibble(

 word = words,

 i = seq_along(word)   # 從參數word的長度中提取長度

)

df %>%

 filter(str_detect(words, "x$"))

 

str_detect() 函數的一種變體是 str_count(),返回字符串中匹配的數量:

x <- c("apple", "banana", "pear")

str_count(x, "a")

 

# 平均來看,每個單詞中有多少個元音字母?

mean(str_count(words, "[aeiou]"))

 

str_count() 可以同 mutate() 函數一同使用:

df %>%

 mutate(   # 增加列

 vowels = str_count(word, "[aeiou]"),   #統計單詞中元音字符的數量

 consonants = str_count(word, "[^aeiou]")   #統計單詞中不是元音字符的數量

 )

 

匹配從來不會重疊。例如,在 "abababa" 中,模式 "aba" 會匹配多少次?正則表達式會告訴你是 2 次,而不是 3 次:

str_count("abababa", "aba")

 

str_view_all("abababa", "aba")

 

1.4.3 提取匹配內容

stringr::sentences

length(sentences)

 

head(sentences)

 

假設我們想要找出包含一種顏色的所有句子,首先,我們需要創建一個顏色名稱向量,然后將其轉換成一個正則表達式:

colors <- c(   #顏色向量

 "red", "orange", "yellow", "green", "blue", "purple"

)

color_match <- str_c(colors, collapse = "|")  

color_match

 

以選取出包含一種顏色的句子,再從中提取出顏色

has_color <- str_subset(sentences, color_match)   #篩選出含有顏色的句子

matches <- str_extract(has_color, color_match)  #提取顏色

head(matches)   #展示

 

str_extract() 只提取第一個匹配。我們可以先選取出具有多於一種匹配的所有句子

more <- sentences[str_count(sentences, color_match) > 1]   #篩選出不止一個顏色的句子

str_view_all(more, color_match)   #提取句子中所有的顏色

 

str_extract(more, color_match)

 

要想得到所有匹配,可以使用 str_extract_all() 函數,它會返回一個列表:

str_extract_all(more, color_match)  

 

1.4.5 分組匹配

先進行一種啟發式實驗,找出跟在 a the 后面的所有單詞。

noun <- "(a|the) ([^ ]+)"

has_noun <- sentences %>%

 str_subset(noun) %>%

 head(10)

has_noun %>%

 str_extract(noun)

 

str_extract() 函數可以給出完整匹配;str_match() 函數則可以給出每個獨立分組。str_match() 返回的是一個矩陣,其中一列是完整匹配,后面的列是每個分組的匹配:

has_noun %>%

 str_match(noun)

 

如果數據是保存在 tibble 中的,那么使用 tidyr::extract() 會更容易,要求為每個分組提供一個名稱,以作為新列放在 tibble 中:

tibble(sentence = sentences) %>%

 tidyr::extract(

 sentence, c("article", "noun"), "(a|the) ([^ ]+)",

 remove = FALSE

 )

1.4.7 替換匹配內容

str_replace() str_replace_all() 函數可以使用新字符串替換匹配內容。最簡單的應用是使用固定字符串替換匹配內容:

x <- c("apple", "pear", "banana")

str_replace(x, "[aeiou]", "-")   # 使用符號 - 替換元音字符 aeiou

 

str_replace_all(x, "[aeiou]", "-")

 

通過提供一個命名向量,使用 str_replace_all() 函數可以同時執行多個替換:

x <- c("1 house", "2 cars", "3 people")

str_replace_all(x, c("1" = "one", "2" = "two", "3" = "three"))

 

可以使用回溯引用來插入匹配中的分組(沒看懂)

sentences %>%

 str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") %>%

 head(5)

 

1.4.9 拆分

str_split() 函數可以將字符串拆分為多個片段。例如,我們可以將句子拆分成單詞:

sentences %>%

 head(5) %>%

 str_split(" ")

 

因為字符向量的每個分量會包含不同數量的片段,所以 str_split() 會返回一個列表。如果你拆分的是長度為 1 的向量,那么只要簡單地提取列表的第一個元素即可:

"a|b|c|d" %>%

 str_split("\\|") %>%

 .[[1]]

 

可以通過設置 simplify = TRUE 返回一個矩陣:

sentences %>%

 head(5) %>%

 str_split(" ", simplify = TRUE)

 

可以設定拆分片段的最大數量:

fields <- c("Name: Hadley", "Country: NZ", "Age: 35")

fields %>% str_split(": ", n = 2, simplify = TRUE)

 

可以通過字母、行、句子和單詞邊界(boundary() 函數)來拆分字符串

x <- "This is a sentence. This is another sentence."

str_view_all(x, boundary("word"))

 

str_split(x, " ")[[1]]   #顯示第1個列表內容[[1]]

 

str_split(x, boundary("word"))[[1]]

 

1.4.11 定位匹配內容

str_locate() str_locate_all() 函數可以給出每個匹配的開始位置和結束位置。以使用 str_locate() 函數找出匹配的模式,然后使用 str_sub() 函數來提取或修改匹配的內容。

1.5 其他類型的模式

當使用一個字符串作為模式時,R 會自動調用 regex() 函數對其進行包裝:

# 正常調用:

str_view(fruit, "nana")

# 上面形式是以下形式的簡寫

str_view(fruit, regex("nana"))

 

可以使用 regex() 函數的其他參數來控制具體的匹配方式

1ignore_case = TRUE 既可以匹配大寫字母,也可以匹配小寫字母,它總是使用當前的區

域設置:

bananas <- c("banana", "Banana", "BANANA")

str_view(bananas, "banana")

 

str_view(bananas, regex("banana", ignore_case = TRUE))

 

2multiline = TRUE 可以使得 ^ $ 從每行的開頭和末尾開始匹配,而不是從完整字符串

的開頭和末尾開始匹配

x <- "Line 1\nLine 2\nLine 3"

str_extract_all(x, "^Line")[[1]]

 

str_extract_all(x, regex("^Line", multiline = TRUE))[[1]]

 

3comments = TRUE 可以讓你在復雜的正則表達式中加入注釋和空白字符,以便更易理解。匹配時會忽略空格和 # 后面的內容。如果想要匹配一個空格,你需要對其進行轉義:"\\ "

 

phone <- regex("

 \\(? # 可選的開括號

 (\\d{3}) # 地區編碼

 [)- ]? # 可選的閉括號、短划線或空格

 (\\d{3}) # 另外3個數字

 [ -]? # 可選的空格或短划線

 (\\d{3}) # 另外3個數字

 ", comments = TRUE)

str_match("514-791-8141", phone)

 

4dotall = TRUE 可以使得 . 匹配包括 \n 在內的所有字符。

fixed() 函數可以按照字符串的字節形式進行精確匹配,它會忽略正則表達式中的所有特殊字符,並在非常低的層次上進行操作。

library(microbenchmark)

microbenchmark::microbenchmark(

 fixed = str_detect(sentences, fixed("the")),

 regex = str_detect(sentences, "the"),

 times = 20)

coll() 函數使用標准排序規則來比較字符串,這在進行不區分大小寫的匹配時是非常有效的。缺點是速度比較慢。 # 這意味着在進行不區分大小寫的匹配時,還是需要知道不同規則之間的區別:

i <- c("I", "İ", "i", "ı")

i

str_subset(i, coll("i", ignore_case = TRUE))

 

str_subset(

 i,

 coll("i", ignore_case = TRUE, locale = "tr")   # locale 參數,以確定使用哪種規則來比較字符

)

 

fixed() regex() 函數中都有 ignore_case 參數,但都無法選擇區域設置,它們總是使用默認的區域設置。查看默認區域設置

stringi::stri_locale_info()

 

1.6 正則表達式的其他應用

apropos() 函數可以在全局環境空間中搜索所有可用對象。當不能確切想起函數名稱時,這個函數特別有用:

apropos("replace")

 

dir() 函數可以列出一個目錄下的所有文件。dir() 函數的 patten 參數可以是一個正則

表達式,此時它只返回與這個模式相匹配的文件名。例如返回當前目錄中的所有 R Markdown 文件:

head(dir(pattern = "\\.Rmd$"))

 

1.7 stringi

stringr 建立於 stringi 的基礎之上,與 stringr 不同,stringi 的設計思想是盡量全面,幾乎包含了我們可以用到的所有函數:stringi 中有 234 個函數,而stringr 中只有 42 個。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM