You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在R中高效逐块读取多个大压缩文件(避免重复解压)

高效逐块读取多个大压缩TSV文件的R方案

问题核心

原代码每次读取chunk时都会重新打开并解压整个文件、跳过已读行,导致大量重复解压操作,效率极低。解决思路是为每个文件保留持续的解压流/文件连接,让读取操作从上次结束的位置继续,避免重复解压。


方案1:利用data.table+持久化文件连接

通过gzfile()打开压缩文件的持久化连接,fread可以直接从连接读取,连接会自动记录当前读取位置,无需每次手动设置skip。

完整代码示例

library(data.table)

inFiles <- c("file1.tsv.gz", "file2.tsv.gz", "file3.tsv.gz")
chunksize <- 1000

# 1. 为每个文件创建持久化压缩连接并读取列名
file_conns <- lapply(inFiles, function(f) {
  conn <- gzfile(f, open = "rt")
  # 读取表头作为列名(若文件无表头则跳过此步,手动指定col_names)
  col_names <- strsplit(readLines(conn, n = 1), "\t")[[1]]
  list(conn = conn, col_names = col_names)
})

# 2. 循环逐块读取所有文件的对应chunk
repeat {
  # 从每个连接读取一个chunk
  data_chunks <- lapply(file_conns, function(info) {
    chunk <- fread(info$conn, nrows = chunksize, sep = "\t", col.names = info$col_names)
    # 读取到空数据时关闭连接
    if (nrow(chunk) == 0) {
      close(info$conn)
    }
    chunk
  })
  
  # 检查所有chunk是否为空,为空则终止循环
  all_empty <- all(sapply(data_chunks, function(x) nrow(x) == 0))
  if (all_empty) break
  
  # --------------------------
  # 在这里处理当前批次的chunk数据
  # 示例:data_chunks[[1]]对应file1的当前chunk,data_chunks[[2]]对应file2的当前chunk
  # --------------------------
}

优势

  • 每个压缩文件仅解压一次,读取速度大幅提升
  • 连接自动记录读取位置,无需手动计算跳过行数
  • 兼容data.table的高效读取特性

方案2:利用readr的分块读取API处理多文件

readr::read_delim_chunked支持从连接分块读取,可针对每个文件创建独立连接,实现同步读取所有文件的对应chunk,或逐个文件处理完整内容。

同步读取所有文件对应chunk的示例

library(readr)
library(purrr)

inFiles <- c("file1.tsv.gz", "file2.tsv.gz", "file3.tsv.gz")
chunksize <- 1000

# 创建每个文件的连接和列名信息
file_info <- map(inFiles, function(f) {
  conn <- gzfile(f, open = "rt")
  col_names <- read_delim(conn, delim = "\t", n_max = 0)$col_names
  list(conn = conn, col_names = col_names)
})

repeat {
  # 读取所有文件的当前chunk
  data_chunks <- map(file_info, function(info) {
    chunk <- read_delim(info$conn, delim = "\t", n_max = chunksize, col_names = info$col_names)
    if (nrow(chunk) == 0) close(info$conn)
    chunk
  })
  
  all_empty <- all(map_lgl(data_chunks, ~nrow(.x) == 0))
  if (all_empty) break
  
  # --------------------------
  # 处理当前批次的chunk数据
  # --------------------------
}

逐个文件处理所有chunk的示例

如果不需要同步读取,而是处理完一个文件的所有chunk再处理下一个:

library(readr)

# 定义chunk处理逻辑
process_chunk <- function(chunk) {
  # 替换为你的实际处理代码,比如统计、清洗等
  cat("处理了", nrow(chunk), "行数据\n")
}

inFiles <- c("file1.tsv.gz", "file2.tsv.gz", "file3.tsv.gz")
chunksize <- 1000

# 遍历每个文件分块处理
walk(inFiles, function(f) {
  conn <- gzfile(f, open = "rt")
  # 读取列名
  col_names <- read_delim(conn, delim = "\t", n_max = 0)$col_names
  # 重置连接到文件开头
  seek(conn, where = 0)
  
  # 分块读取并处理当前文件
  read_delim_chunked(
    conn,
    delim = "\t",
    callback = DataFrameCallback$new(process_chunk),
    chunk_size = chunksize,
    col_names = col_names
  )
  close(conn)
})

关键注意事项

  • 处理完成后务必关闭文件连接,避免系统资源泄漏
  • 若文件无表头,需手动指定col_names,跳过读取表头的步骤
  • 针对其他压缩格式(如bz2、xz),可替换gzfile()为bzfile()、xzfile()

内容的提问来源于stack exchange,提问作者Alex Petty

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 13:40:22