R語言的字符操作

R語言主要擅長于數值向量和矩陣操作,但是讓他去做字符串操作也可以。

字符串的基本操作類型:

  • 查找和替換
  • 大小寫轉換
  • 字符數統計
  • 字符串連接和拆分

就我所知,有兩套處理函數,一套是Hadley大神的stringr,一套是R自帶的。

stringr使用指南

stringr函數主要分為四類:

  1. 字符操作:操作字符向量中的單個字符 str_length, str_sub, str_dup
  2. 添加,移除和操作空白符 str_pad, str_trim, str_wrap
  3. 大小寫轉換處理 str_to_lower, str_to_upper, str_to_title
  4. 模式匹配函數 str_detect, str_subset, str_count, str_locate, str_locate_all, str_match, str_match_all, str_replace, str_replace_all, str_split_fix, str_split, str_extract, str_extract_all

單個字符的處理

字符長度str_length,等價于nchar

str_length("abc")

根據位置信息提取或替換字符, 類似于substr()

x <- c("abcdef","ghijk")
# 第三個
str_sub(x,3,3)
# 第二個到倒數第二個
str_sub(x,2, -2)
# 替換
str_sub(x,3,3) <- X

重復字符串,不同于rep

str_dup(x,c(2,3))

空白符

  • 前后增加空白字符, str_pad()
x <- c("abc", "defghi")
str_pad(x, 10)
str_pad(x, 10, "both")
  • 移除空白字符, str_trim()
x <- c("   b   ", "c   ", "   d")
str_trim(x)
str_trim(x,left)
  • 更好的排版,讓每一行的看起來一樣長, str_wrap()
nature <- c("Nature Methods' Points of Significance column ",
"on statistics explains many key statistical and ","experimental design concepts. Other resources include an online",
" plotting tool and links to statistics guides from other publishers.")
cat(str_wrap(nature, width=40))

大小寫轉換

  • 全部大寫 str_to_upper, 類似于基礎R的toupper()
  • 全部小寫 str_to_lower, 類似于基礎R的tolower()
  • title形式 str_to_title

模式匹配

功能: decect, locate, extract, match, replace, split
測試數據:

strings <- c(
    "apple",
    "219 733 8965",
    "329-293-8753",
    "Work: 579-499-7527; Home: 543.355.3679"
)

號碼的正則形式:

phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})"
  • 檢測字符串是否符合特定模式, str_detect
str_detect(strings, phone)
  • 提取字符串(全部內容), str_subset
str_subset(strings, phone)
  • 統計匹配次數,str_count
str_count(strings, phone)
  • 定位首個匹配的位置,str_locate(返回matrix), 或所有匹配的位置, str_locate_all(返回list)
str_locate(strings, phone)
str_locate_all(strings, phone)
  • str_extract提取首個匹配,返回字符串向量, str_extract_all()提取所有匹配,返回list
str_extract(strings, phone)
str_extract_all(strings,phone)
str_extract_all(strings, phone, simplify=TRUE)

-str_match提取首個匹配中()分組內的內容, str_match_all()則是全部,因此是list

str_match(strings, phone)
str_match_all(strings, phone)
  • str_replace()替代第一個匹配, str_replace_all替代所有匹配
str_replace(strings, phone, "XXX-XXX-XXXX")
str_replace_all(strings, phone, "XXX-XXX-XXXX")
  • str_split_fixed()返回固定數目,全部拆分,str_split()可變拆分
str_split_fixed("a-b-c", "-")
str_spilt("a-b-c","-", n=2)

4類字符串描述引擎

  1. 默認是正則表達式`vignette("regular-expression")
  2. 逐byte固定匹配, fixed()
  3. locale-sensive 字符匹配, coll()
  4. 字符邊界分析, boundary()

正則表達式練習

基本匹配

最簡單的模式就是匹配某個完整的字符

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

如果需要忽略大小寫, ignore_case =TRUE

bananas <- c("banana", "Banana", "BANANA")
str_dectect(bananas, regrex("banana", ignore_case=TRUE))

可以用點.匹配任意字符,但是默認不包括\n,需要用dotall=TURE開啟

str_detect("\nX\n", ".X.")
str_detect("\nX\n", regex(".X.", dotall=TRUE))

轉義(R中一坑)

正則表達式中有一些是特殊字符,比如說剛才的頓號,因此為了匹配這些特殊字符,我們需要對其轉義。在Linux命令行里,轉義用的是\, 所以可以直接用\..

但是R里面的坑就出現了,我們用字符表示正則表達式,\ 在字符里被用作轉義符號。然后我們需要先把\轉義成字符,然后才能進一步轉義,\\.

如果要匹配\ ,就需要\\\\,不可以思議,難以釋懷,不知道被坑了多少次。

或者你用\Q...\E 類似于Python的 r'....', 原意匹配

特殊字符

\d: 任意數字, \D:任意非數字.
\s: 任意空白字符,\S:任意非空白字符
\w: 匹配單詞
\b: 匹配字符邊界, \B:非字符邊界
[abc], [a-z], [^abc], [^-]

在R里面,需要對""進行轉義,所以上面的\在R里都要寫成,\
下面是一些預編譯好的字符集,顧名思義

[:punct:]
[:alpha:]
[:lower:]
[:upper:]
[:digit:]
[:xdigit:]
[:alnum:]
[:cntrl:]
[:graph:]
[:print:]
[:space:]
[:blank:]

匹配abc或def

str_detect(c("abc","def","ghi"), "abc|def")

分組

匹配grey或gray

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

分組可以用\1, \2進行提取,

定位

^: 字符串開始, 如^a
: 字符串結束, 如a

如果字符串有多行,那么就需要regex(multiline=TRUE)。此時,
\A: 輸入開頭
\z: 輸入結尾
\Z: 頭尾

重復

  • ?: 0或1
  • +: 大于等于1
  • *: 大于等于0
  • {n}: n次
  • {n,m}: n到m次
  • {n,}: 大于那次

默認是貪婪模式, 在上述字符后添加"?" 則為非貪婪模式。

PS: 下面是R語言自帶的字符處理函數,我已經放棄他們了。

基礎R包函數

nchar(): 函數返回字符串長度
paste(), paste0(): 連接若干個字符串
sprintf():格式化輸出,下面舉例

sprintf("%f", pi)
sprintf("%.3f", pi)
sprintf("%1.0f", pi)
sprintf("%5.1f", pi)
sprintf("%05.1f", pi)
sprintf("%+f", pi)
sprintf("% f", pi)
sprintf("%-10f", pi) # left justified
sprintf("%e", pi)
sprintf("%E", pi)
sprintf("%g", pi)
sprintf("%g",   1e6 * pi) # -> exponential
sprintf("%.9g", 1e6 * pi) # -> "fixed"
sprintf("%G", 1e-6 * pi)

toupper(): 大寫轉換
tolower(): 小寫轉換
substr(): 提取或替換一個字符串向量的子串

x <- "abcde"
substr(x,1,2)
# ab
substr(x,1,2) <- 2333
# 233cde

上面都是一些普通的函數,很好理解,下面都是一些和正則表達式相關的函數,如grep, grepl, regexpr, gregexpr, sub, gsub, strsplit
因此必須介紹一下R語言的正則表達式寫法了。

  • R語言是用的擴展正則表達式(Extended Regular Expressions)
  • 元字符:\ | ( ) [ { ^ $ * + ?
  • 非元字符轉義后:\a as BEL, \e as ESC, \f as FF, \n as LF, \r as CR and \t as TAB
  • 一些定義字符集合[:alnum:], [:alpha:], [:blank:], [:cntrl:], [:digit:], [:graph:], [:lower:], [:print:], [:punct:], [:space:], [:upper:],[:xdigit:]
  • 找出“組”字符串
  • 默認是貪婪模式,可以通過用?改變為非貪婪模式

這些是基本知識,可以百度到每個字符的具體解釋,或者看文檔?regexp
不說基礎知識了,看下應用吧。我常用的操作一般是找到某個字符串,或者對字符串進行替換
比如說,我想找到所有以P開頭,且不是P結尾的字符,

test <- c("Python", "Perl", "PHP", "JAVA", "C", "C++")
grep("^P.*?[^P]$", test)
[1] 1 2
grep("^P.*?[^P]$", test,value=TRUE)
[1] "Python" "Perl"
grepl("^P.*?[^P]$", test)
[1]  TRUE  TRUE FALSE FALSE FALSE FALSE
regexpr("^P.*?[^P]$", test)
[1]  1  1 -1 -1 -1 -1
attr(,"match.length")
[1]  6  4 -1 -1 -1 -1
attr(,"useBytes")
> gregexpr("^P.*?[^P]$", test)
[[1]]
[1] 1
attr(,"match.length")
[1] 6
attr(,"useBytes")
[1] TRUE

[[2]]
[1] 1
attr(,"match.length")
[1] 4
attr(,"useBytes")
[1] TRUE

其中grep()默認是返回下標,如果設置value=TRUE,則返回字符串,grepl()返回是否配對的邏輯判斷, regexpr則是返回匹配范圍,如果不匹配結果是-1,gregexpr和前者功能一致,只不過返回的是列表形式。
:忽略大小寫ignore.case = TRUE

現在我想把C++替換成C--。我先試著找到C++

> grep("\+\+",test)
錯誤: 由""\+"開頭的字符串中存在'\+',但沒有這種逸出號

什么情況,為什么\+不能把+這個元字符轉義?難不成+在R里面不是元字符?我測試下

grep("++",test,value=TRUE)
Error in grep("++", test, value = TRUE) : 
  正規表現’++'不對,原因是'Invalid use of repetition operators'

啊!看來+還是元字符,難道是\ 叛變革命了,我試試看。

> grep("\23","test\23",value=TRUE)
[1] "test\023"
> grep("\\23","test\23",value=TRUE)
Error in grep("\\23", "test\023", value = TRUE) : 
  正規表現’\23'不對,原因是'Invalid back reference'

看來\ 是主要任務是把非元字符轉義,如果想把元字符轉義成普通字符,只能是\\元字符

grep("\\+\\+",test,value=TRUE)
[1] "C++"

回到我們之前的替換任務sub只對第一個匹配進行替換,gsub對所有匹配替換。

 sub("\\+\\+","--",test)
[1] "Python" "Perl"   "PHP"    "JAVA"   "C"      "C--" 

最后還可以用strsplit對字符串進行分割,返回的是一個列表

x <- c(as = "asfef", qu = "qwerty", "yuiop[", "b", "stuff.blah.yech")
strsplit(x, "e")
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容