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
1 | str_length("abc") |
根据位置信息提取或替换字符, 类似于substr()
1 | x <- c("abcdef","ghijk") |
重复字符串,不同于rep
1 | str_dup(x,c(2,3)) |
空白符
前后增加空白字符, str_pad()
1 | x <- c("abc", "defghi") |
移除空白字符, str_trim()
1 | x <- c(" b ", "c ", " d") |
更好的排版,让每一行的看起来一样长, str_wrap()
1 | nature <- c("Nature Methods' Points of Significance column ", |
大小写转换
- 全部大写 str_to_upper, 类似于基础R的toupper()
- 全部小写 str_to_lower, 类似于基础R的tolower()
- title形式 str_to_title
模式匹配
功能: decect, locate, extract, match, replace, split
测试数据:
1 | strings <- c( |
号码的正则形式:
1 | phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})" |
检测字符串是否符合特定模式, str_detect
1 | str_detect(strings, phone) |
提取字符串(全部内容), str_subset
1 | str_subset(strings, phone) |
统计匹配次数,str_count
1 | str_count(strings, phone) |
定位首个匹配的位置,str_locate(返回matrix), 或所有匹配的位置, str_locate_all(返回list)
1 | str_locate(strings, phone) |
str_extract提取首个匹配,返回字符串向量, str_extract_all()提取所有匹配,返回list
1 | str_extract(strings, phone) |
str_match提取首个匹配中()分组内的内容, str_match_all()则是全部,因此是list
1 | str_match(strings, phone) |
str_replace()替代第一个匹配, str_replace_all替代所有匹配
1 | str_replace(strings, phone, "XXX-XXX-XXXX") |
str_split_fixed()返回固定数目,全部拆分,str_split()可变拆分
1 | str_split_fixed("a-b-c", "-") |
4类字符串描述引擎
1. 默认是正则表达式`vignette(“regular-expression”)
2. 逐byte固定匹配, fixed()
3. locale-sensive 字符匹配, coll()
4. 字符边界分析, boundary()
正则表达式练习
基本匹配
最简单的模式就是匹配某个完整的字符
1 | x <- c("apple", "banana", "pear") |
如果需要忽略大小写, ignore_case =TRUE
1 | bananas <- c("banana", "Banana", "BANANA") |
可以用点.
匹配任意字符,但是默认不包括\n
,需要用dotall=TURE开启
1 | str_detect("\nX\n", ".X.") |
转义(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
1 | str_detect(c("abc","def","ghi"), "abc|def") |
分组
匹配grey或gray
1 | 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()
:格式化输出,下面举例
1 | sprintf("%f", pi) |
toupper()
: 大写转换tolower()
: 小写转换substr()
: 提取或替换一个字符串向量的子串
1 | x <- "abcde" |
上面都是一些普通的函数,很好理解,下面都是一些和正则表达式相关的函数,如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结尾的字符,
1 | test <- c("Python", "Perl", "PHP", "JAVA", "C", "C++") |
其中grep()
默认是返回下标,如果设置value=TRUE,则返回字符串,grepl()
返回是否配对的逻辑判断, regexpr
则是返回匹配范围,如果不匹配结果是-1,gregexpr
和前者功能一致,只不过返回的是列表形式。
注:忽略大小写ignore.case = TRUE
现在我想把C++
替换成C--
。我先试着找到C++
1 | > grep("\+\+",test) |
什么情况,为什么\+
不能把+
这个元字符转义?难不成+
在R里面不是元字符?我测试下
1 | grep("++",test,value=TRUE) |
啊!看来+
还是元字符,难道是\
叛变革命了,我试试看。
1 | > grep("\23","test\23",value=TRUE) |
看来\
是主要任务是把非元字符转义,如果想把元字符转义成普通字符,只能是\\
元字符
1 | grep("\\+\\+",test,value=TRUE) |
回到我们之前的替换任务sub只对第一个匹配进行替换,gsub对所有匹配替换。
1 | sub("\\+\\+","--",test) |
最后还可以用strsplit
对字符串进行分割,返回的是一个列表
1 | x <- c(as = "asfef", qu = "qwerty", "yuiop[", "b", "stuff.blah.yech") |