如何使用data.table :: fread读取未加引号的额外\ r \ n

时间:2016-12-27 17:09:43

标签: r csv data.table

我必须处理的数据包含带有一些额外\ r字符的非加引号文本。文件很大(500MB),大量(> 600),并且不能更改导出。数据可能看起来像

  

A,B,C

     

嗒嗒,A,1

     

布卢,一个\ R,B

     

blee,C,d

  1. 如何使用data.table fread来处理这个问题?
  2. 是否有更好的R读取CSV功能,这同样具有高性能?
  3. 摄制

    library(data.table)
    csv<-"A,B,C\r\n
          blah,a,1\r\n
          bloo,a\r,b\r\n
          blee,c,d\r\n"
    fread(csv)
    
      

    fread(csv)出错:     当检测到来自0点的类型时,预期的sep(',')但新行,EOF(或其他非打印字符)结束字段1:         布卢,一

    高级重复

    简单的复制可能太微不足道了,无法给出规模感......

    samplerecs<-c("blah,a,1","bloo,a\r,b","blee,c,d")
    randomcsv<-paste0(c("A,B,C",rep(samplerecs,2000000)))
    write(randomcsv,file = "sample.csv")
    
    # Naive approach
    fread("sample.csv")
    
    # Akrun's approach with needing text read first
    fread(gsub("\r\n|\r", "", paste0(randomcsv,collapse="\r\n")))
    #>Error in file.info(input) :  file name conversion problem -- name too long?
    
    # Julia's approach with needing text read first
    readr::read_csv(gsub("\r\n|\r", "", paste0(randomcsv,collapse="\r\n")))
    #> Error: C stack usage  48029706 is too close to the limit
    

4 个答案:

答案 0 :(得分:4)

继续@ dirk-eddelbuettel&amp; @nrussell的建议,解决这个问题的方法是预处理文件。处理器也可以在fread()中调用,但在这里它是以单独的步骤执行的:

samplerecs<-c("blah,a,1","bloo,a\r,b","blee,c,d")
randomcsv<-paste0(c("A,B,C",rep(samplerecs,2000000)))
write(randomcsv,file = "sample.csv")
# Remove errant `\r`'s with tr - shown here is the Windows R solution
shell("C:/Rtools/bin/tr.exe -d '\\r' < sample.csv > sampleNEW.csv")
fread("sampleNEW.csv")

答案 1 :(得分:2)

我们可以尝试gsub

fread(gsub("\r\n|\r", "", csv))
#      A B C
#1: blah a 1
#2: bloo a b
#3: blee c d

答案 2 :(得分:1)

如果您愿意,也可以使用tidyverse软件包执行此操作。

> library(readr)
> library(stringr)
> read_csv(str_replace_all(csv, "\r", ""))
# A tibble: 3 × 3
      A     B     C
  <chr> <chr> <chr>
1  blah     a     1
2  bloo     a     b
3  blee     c     d

答案 3 :(得分:1)

如果你确实想在R中完成它,你可以尝试使用连接。只要连接保持打开,它就会从其先前的位置开始读/写。当然,这意味着打开和关闭连接的负担落在你身上。

在以下代码中,文件由块处理:

library(data.table)

input_csv <- "sample.csv"
in_conn <- file(input_csv)
output_csv <- "out.csv"
out_conn <- file(output_csv, "w+")
open(in_conn)

chunk_size <- 1E6
return_pattern <- "(?<=^|,|\n)([^,]*(?<!\n)\r(?!\n)[^,]*)(?=,|\n|$)"

buffer <- ""

repeat {
  new_chars <- readChar(in_conn, chunk_size)
  buffer <- paste0(buffer, new_chars)
  while (grepl("[\r\n]$", buffer, perl = TRUE)) {
    next_char <- readChar(in_conn, 1)
    buffer <- paste0(buffer, next_char)
    if (!length(next_char))
      break
  }
  chunk <- gsub("(.*)[,\n][^,\n]*$", "\\1", buffer, perl = TRUE)
  buffer <- substr(buffer, nchar(chunk) + 1, nchar(buffer))
  cleaned <- gsub(return_pattern, '"\\1"', chunk, perl = TRUE)
  writeChar(cleaned, out_conn, eos = NULL)
  if (!length(new_chars))
    break
}

writeChar('\n', out_conn, eos = NULL)

close(in_conn)
close(out_conn)

result <- fread(output_csv)

过程:

  • 如果一个块以\r\n结尾,则会添加另一个字符,直到它没有。
  • 引号括在{a}附近的\r的值 \n
  • 已清理的块将添加到另一个文件的末尾。
  • 冲洗并重复。

此代码通过假设sample.csv中的任何字段都没有引用来简化问题。它不是特别快,但不是非常慢。 chunk_size的较大值应减少I / O操作所花费的时间。如果用于此玩具示例之外的任何内容,我强烈建议将其包装在tryCatch(...)调用中,以确保文件在之后关闭。