R中字符向量的快速转义/去除

Jer*_*oen 9 regex r escaping gsub

要在json中编码字符串,需要使用反斜杠转义几个保留字符,并且每个字符串都需要用双引号括起来.目前,该jsonlite包使用deparse基R中的函数实现了这个:

deparse_vector <- function(x) {
  stopifnot(is.character(x))
  vapply(x, deparse, character(1), USE.NAMES=FALSE)
}
Run Code Online (Sandbox Code Playgroud)

这样做的诀窍:

test <- c("line\nline", "foo\\bar", "I said: \"hi!\"")
cat(deparse_vector(test))
Run Code Online (Sandbox Code Playgroud)

然而deparse,对于大型载体来说,结果却很慢.另一种实现是gsub单独为每个角色:

deparse_vector2 <- function(x) {
  stopifnot(is.character(x))
  if(!length(x)) return(x)
  x <- gsub("\\", "\\\\", x, fixed=TRUE)
  x <- gsub("\"", "\\\"", x, fixed=TRUE)
  x <- gsub("\n", "\\n", x, fixed=TRUE)
  x <- gsub("\r", "\\r", x, fixed=TRUE)
  x <- gsub("\t", "\\t", x, fixed=TRUE)
  x <- gsub("\b", "\\b", x, fixed=TRUE)
  x <- gsub("\f", "\\f", x, fixed=TRUE)
  paste0("\"", x, "\"")
}
Run Code Online (Sandbox Code Playgroud)

这有点快,但也不多,也有点难看.什么是更好的方法来做到这一点?(最好没有额外的依赖)

脚本可用于比较实现:

> system.time(out1 <- deparse_vector1(strings))
   user  system elapsed 
  6.517   0.000   6.523 
> system.time(out2 <- deparse_vector2(strings))
   user  system elapsed 
  1.194   0.000   1.194 
Run Code Online (Sandbox Code Playgroud)

had*_*ley 7

这是Winston代码的C++版本.它简单得多,因为你可以有效地发展std::string.它也不太可能崩溃,因为Rcpp会为你处理内存管理.

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
std::string escape_one(std::string x) {
  std::string out = "\"";

  int n = x.size();
  for (int i = 0; i < n; ++i) {
    char cur = x[i];

    switch(cur) {
      case '\\': out += "\\\\"; break;
      case '"':  out += "\\\""; break;
      case '\n': out += "\\n";  break;
      case '\r': out += "\\r";  break;
      case '\t': out += "\\t";  break;
      case '\b': out += "\\b";  break;
      case '\f': out += "\\f";  break;
      default:     out += cur;
    }
  }

  out += '"';

  return out;
}

// [[Rcpp::export]]
CharacterVector escape_chars(CharacterVector x) {
  int n = x.size();
  CharacterVector out(n);

  for (int i = 0; i < n; ++i) {
    String cur = x[i];
    out[i] = escape_one(cur);
  }

  return out;
}
Run Code Online (Sandbox Code Playgroud)

在您的基准测试中,deparse_vector2(strings)需要0.8秒,并escape_chars(strings)需要0.165秒.


wch*_*wch 5

我不知道用R代码做更快的方法,但是我确实决定尝试在C中实现它,包含在一个名为R的函数中deparse_vector3.这很粗糙(而且我远不是专业的C程序员),但它似乎适用于您的示例:https://gist.github.com/wch/e3ec5b20eb712f1b22b2

在我的系统(Mac,R 3.1.1)上,deparse_vector2速度比测试速度快deparse_vector5倍,这比测试中的5倍差.

我的deparse_vector3功能只比快3倍deparse_vector2.可能还有改进的余地.

> system.time(out1 <- deparse_vector1(strings))
   user  system elapsed 
  8.459   0.009   8.470 
> system.time(out2 <- deparse_vector2(strings))
   user  system elapsed 
  0.368   0.007   0.374 
> system.time(out3 <- deparse_vector3(strings))
   user  system elapsed 
  0.120   0.001   0.120 
Run Code Online (Sandbox Code Playgroud)

但我认为这不会正确处理非ASCII字符编码.以下是R源代码处理方式的示例:https://github.com/wch/r-source/blob/trunk/src/main/grep.c#L704-L739

编辑:这似乎处理UTF-8确定,虽然我可能在测试中遗漏了一些东西.