1. 一个可用的纯正则表达式解决方案(又名编辑#2)
这个任务可以完全使用正则表达式来完成(非常感谢@Mike Samuel)
首先,我们建立一个表情符号的数据库:
(emots <- as.character(outer(c(":", ";", ":-", ";-"),
+ c(")", "(", "]", "[", "D", "o", "O", "P", "p"), stri_paste)))
一个示例输入文本:
text <- ":) ;P :] :) ;D :( LOL :) I've been to... the (grocery) st{o}re :P :-) --- and the salesperson said: Oh boy!"
一个帮助函数,用于转义一些特殊字符,以便它们可以在正则表达式模式中使用(使用
stringi 包):
library(stringi)
escape_regex <- function(r) {
stri_replace_all_regex(r, "\\(|\\)|\\[|\\]", "\\\\$0")
}
匹配表情符号的正则表达式:
(regex1 <- stri_c("(", stri_c(escape_regex(emots), collapse="|"), ")"))
## [1] "(:\\)|;\\)|:-\\)|;-\\)|:\\(|;\\(|:-\\(|;-\\(|:\\]|;\\]|:-\\]|;-\\]|:\\[|;\\[|:-\\[|;-\\[|:D|;D|:-D|;-D|:o|;o|:-o|;-o|:O|;O|:-O|;-O|:P|;P|:-P|;-P|:p|;p|:-p|;-p)"
现在,正如@Mike Samuel在下面建议的那样,我们只需要匹配
(表情符号)|标点符号
(注意表情符号在一个捕获组中)然后用捕获组1的结果替换匹配项(所以如果它是一个表情符号,我们有replacement=
this emoticon,如果它是一个标点符号,则有replacement=
nothing)。这将起作用,因为在ICU Regex(即
stri_replace_all_regex
使用的正则表达式引擎)中,与
|
交替使用是
贪婪和左倾斜的:表情符号将比标点字符先匹配。
stri_replace_all_regex(text, stri_c(regex1, "|\\p{P}"), "$1")
顺便提一下,如果您只想要去掉选定的一组字符,请使用例如[.,]
而不是上面的[\\p{P}]
。
2. 正则表达式解决方案提示 - 我的第一个(不明智)尝试(也称为原始答案)
我的第一个想法(主要是出于“历史原因”而留下),是通过使用前瞻和后顾来解决这个问题,但是 - 如您所见 - 那远非完美。
要删除所有不跟随)
、(
、D
、X
、8
、[
或]
的:
和;
,请使用负向后顾:
stri_replace_all_regex(text, "[:;](?![)P(DX8\\[\\]])", "")
现在我们可以添加一些旧式的表情符号(带鼻子的,例如
:-)
,
;-D
等)。
stri_replace_all_regex(text, "[:;](?![-]?[)P(DX8\\[\\]])", "")
现在是连字符的删除(负向先行断言和先行断言)。
stri_replace_all_regex(text, "[:;](?![-]?[)P(DX8\\[\\]])|(?!<[:;])[-](?![)P(DX8\\[\\]])", "")
## [1] ":) :8 ;P :] :) ;D :( LOL :) I've been to... the grocery store :P :-) and the salesperson said Oh boy!"
当然,首先您应该建立自己的表情符号数据库(保留原样)和标点符号数据库(删除)。正则表达式高度依赖于这两个集合,因此很难添加新的表情符号 --- 这绝对不值得尝试(可能会让您头痛)。
3. 第二次尝试(更易读的正则表达式,称为Edit#1)
另一方面,如果您对复杂的正则表达式过敏,请尝试这种方法。这种方法有一些“教学效益” - 我们可以完全了解以下每个步骤中正在执行的操作:
- 定位
text
中的所有表情符号;
- 定位
text
中的所有标点符号;
- 查找不是表情符号的标点字符的位置;
- 移除第3步中定位到的字符。
一个示例输入文本 - 仅1个字符串 - 通用案例留作练习 ;)
text <- ":) ;P :] :) ;D :( LOL :) I've been to... the (grocery) st{o}re :P :-) --- and the salesperson said: Oh boy!"
一个帮助函数,可以转义一些特殊字符,以便在正则表达式中使用:
escape_regex <- function(r) {
library("stringi")
stri_replace_all_regex(r, "\\(|\\)|\\[|\\]", "\\\\$0")
}
匹配表情符号的正则表达式:
(regex1 <- stri_c("(", stri_c(escape_regex(emots), collapse="|"), ")"))
## [1] "(:\\)|;\\)|:-\\)|;-\\)|:\\(|;\\(|:-\\(|;-\\(|:\\]|;\\]|:-\\]|;-\\]|:\\[|;\\[|:-\\[|;-\\[|:D|;D|:-D|;-D|:o|;o|:-o|;-o|:O|;O|:-O|;-O|:P|;P|:-P|;-P|:p|;p|:-p|;-p)"
找到所有表情符号的起始和结束位置(即找到第一个 OR 第二个 OR ... 表情符号):
where_emots <- stri_locate_all_regex(text, regex1)[[1]]
print(where_emots)
定位所有的标点符号字符(这里\\p{P}
是代表标点符号字符的Unicode字符类):
where_punct <- stri_locate_all_regex(text, "\\p{P}")[[1]]
print(where_punct)
由于某些标点符号出现在表情符号中,我们不应该将它们暂时移除:
which_punct_omit <- sapply(1:nrow(where_punct), function(i) {
any(where_punct[i,1] >= where_emots[,1] &
where_punct[i,2] <= where_emots[,2]) })
where_punct <- where_punct[!which_punct_omit,]
print(where_punct)
每个标点符号肯定只包含1个字符,因此始终
where_punct[,1]==where_punct[,2]
。
现在是最后一部分。如您所见,
where_punct[,1]
包含要删除的字符位置。在我看来,最简单的方法(无需循环)是将字符串转换为UTF-32(每个字符 == 1个整数),删除不需要的元素,然后再转换回文本表示形式:
text_tmp <- stri_enc_toutf32(text)[[1]]
print(text_tmp) # here - just ASCII codes...
## [1] 58 41 32 59 80 32 58 93 32 58....
text_tmp <- text_tmp[-where_punct[,1]] # removal, but be sure that where_punct is not empty!
结果是:
stri_enc_fromutf32(text_tmp)
在这里。