• R语言
  • base R中匹配backslash的问题

最近做一些数据清洗工作时候,碰到一个困惑的地方,请大家答疑解惑 :

我想在形如"x\ty\a"这样的字符串里,把"a"前面的backslash给匹配出来移除掉,
诚然,对于这样一个具体的字符,使用gsub('\\a', 'a', 'x\ty\a')就可以实现。

但是如果是针对一类backslash,后面跟的不是确定的像'a'这样的字符,想把这类backslash移除该怎么办?
例如字符串(print在console中长这个样子):"这是一段伪造的字符:\1. 测试一;\2. 测试二;\三. 测试三;\四. 测试四"。

当然这个伪造的字符串可能都无法在R中定义,但是有时候从中文文本中读取内容时,确实会有这种畸形的字符串存在。因为是一个非常大的文本文件,当时也没有去确认对应的原始字符到底是什么样子。

所以其实总结下来有下面几个问题和困惑:

  1. 什么样的原始字符才会导致例子中的畸形字符串?
  2. 该怎么样去除例子中的backslash?
  3. "\\"这种正则表达式会被认为匹配双引号,且字符串没封闭,base R能实现python中的raw字符吗?

我觉得你的这几个问题在R for data science第2版14.2章节 创建字符串 以及 15.4.1章节 转义字符 里基本全都能找到答案。

我们使用的是“字符串”,用它来表示“表达式”。“字符串”的内容里都是些基本的字符、符号,因此需要特殊的转义字符的形式,才能表达出丰富的“表达式”。

  1. 反斜杠由于有转义的特殊的含义,因此一个字面意义上的反斜线,需要用’\\‘这样的字符串来表示,你直接print出来的会是内部存储的字符串,但是可以用stringr::str_view来显示它真正的含义(表达式?)
library(stringr)
# 如果想在自己的代码中直接写出测试字符串,那么要用“\\”
tmp <- "这是一段伪造的字符:\\1. 测试一;\\2. 测试二;\\三. 测试三;\\四. 测试四"
print(tmp)
str_view(tmp)    # 但是实际的含义其实是单个的反斜杠
  1. 明白了这一点之后,你就知道字面意义上的单个反斜杠(“表达式”中的单个反斜杠),需要在字符串中写作“\\”存储下来。但同时,你在向R以字符串的形式输入匹配特征时,由于反斜杠是一个转义字符,那么为了能“表达”两个字面意义上的反斜杠(我们需要匹配的特征是存储的字符串中的“\\”,它表达了这个存储的字符串在此处是一个字面意义上的反斜杠),每个反斜杠都需要再被转义,所以实际输入的字符串是“\\\\”,即四个反斜杠
str_replace_all(tmp, pattern = "\\\\", replacement = "")    #将字符串含义中的单独反斜杠,全部“删去”,即替换为“”
gsub("\\\\", "", x = tmp)    # gsub的使用也是同理的

当然,我这是从R中自己创建这个测试字符串,所以需要把单独的反斜杠修改成双反斜杠。但是你如果把原始的这个字符串保存为一个txt文件,再读取进R中,那么潜在保存的数据会自动是双反斜杠的。

# str_test.txt 文件中保存了一行文本:这是一段伪造的字符:\1. 测试一;\2. 测试二;\三. 测试三;\四. 测试四
tmp2 <- readLines(con = "str_test.txt")
print(tmp2)

关于raw string,R 4.0开始支持,在R for data science中也有提到一些例子

tricky <- r"(double_quote <- "\"" # or '"'
single_quote <- '\'' # or "'")"
str_view(tricky)
#> [1] │ double_quote <- "\"" # or '"'
#>     │ single_quote <- '\'' # or "'"

最后回到你一开始的例子,如果一个R中的字符串,它print出来的是“x\ty\a”的话,其实的含义是x,制表符(\t),y和一个圆点(\a),这个可以用str_view验证下。所以你的gsub('\\a', 'a', 'x\ty\a')的含义并不是把a前面的反斜杠删掉了,而是将圆点替换为字符a。

    fenguoerbian 谢谢回复,反斜杠的含义和escape我是了解的。可能我表达的不太清楚,其实问题不在于加一个反斜杠。或者说加了一个反斜杠如"这是一段伪造的字符:\\1. 测试一;\\2. 测试二;\\三. 测试三;\\四. 测试四",在我自己的console中打印出来就是带双反斜杠的,而不是例子中的单反斜杠。

    换句话说,在双反斜杠的世界中,一切都是自洽的,定义字符时候用双反斜杠,打印时候也正常显示双反斜杠,匹配时候也加以考虑,这是一个正常的世界。
    但是忽然出现了一个畸形的字符,它打印出来就是单反斜杠的,然后把它拿来作为匹配的pattern项,即便使用fixed=TRUE的选项,也会导致各种表达式不规范错误,整个“世界”乱掉了,而我想做的是移除这个畸形的字符里面畸形的单反斜杠,在正常世界里的工具此时各种报错,而且结果也不是我预期的结果,让我感到困惑。

    出现这种异常,我结合数据想象了一下,应该是用户在某系统填写信息的时候,使用了各种奇怪的格式和制表符号,这些内容被存在了某字段内,而数据库数据又被导出为csv到了我这里,我在读取时候就产生了这种畸形的格式。

    当然stringr可能会解决这个问题,不过尴尬的是不方便安装package,所以才考虑用 base R来解决问题。

    我还是回头整理下,把具体例子和报错发上来供大家分析,谢谢!

      holydudu

      匹配本身和是不是用stringr关系不大,gsub也是一样的。

      你最初的关于'x\ty\a'的例子,并不是你把a前面的单独的反斜杠删去了,而是把一个圆点(写作\a)替换为了a。我还是有点好奇你所说的,打印出来是单个的反斜杠且还不是转义字符的例子的。