2011-01-23 17 views
12

R ha una pratica funzione tar() multipiattaforma che può contenere i file tar e gzip. Sembra che questa funzione sia stata progettata per tarare intere directory. Speravo di usare questa funzione per tar e comprimere un sottoinsieme di una directory o un singolo file. Non riesco a farlo, comunque. Mi aspettavo il seguente creare un tar della un singolo file CSV nella directory di lavoro corrente:utilizzando la funzione tar interna di R su file specifici

tar("tst.tgz", "myCsv.csv", compression="gzip") 

Quindi è possibile utilizzare solo la funzione() tar sulle directory?

Mi sono temporaneamente aggirato creando una directory temporanea, copiando i miei file e quindi tarando l'intera directory temporanea. Ma speravo in una soluzione un po 'più semplice. Ciò non richiederebbe la copia dei file, che richiede parecchio tempo per i file di grandi dimensioni.

+2

Se si dispone di un solo file, perché non utilizzare gzfile? – hadley

+0

voglio davvero fare di più ... ma voglio iniziare con uno –

risposta

10

Non penso sia possibile mentre descrivi JD. L'argomento files viene passato all'argomento path di list.files e, di conseguenza, funziona inserendo i file nelle directory, non in singoli file.

Se siete disposti a modificare una funzione interna, tar() può essere fatto per fare quello che vuoi, armeggiando con la chiamata a list.files() all'interno tar(). Un po 'di giocherellando ha prodotto la funzione tar2() qui sotto, che ha argomenti extra per controllare cosa restituisce list.files(). Usando questa funzione, siamo in grado di ottenere quello che vuoi tramite una chiamata in questo modo:

tar2("foo.tar", path = ".", pattern = "bar.csv", recursive = FALSE, 
    full.names = FALSE, all.files = FALSE) 

Il all.files = FALSE è probabile ridondante a meno che non hai nascosto i file con nomi contenenti "bar.csv".

recursive = FALSE Il bit recursive = FALSE interrompe semplicemente la ricerca della funzione ovunque tranne la directory corrente, il che sembra ciò che si desidera e velocizza la ricerca se la directory di lavoro ha molti file e sottocartelle.

Il bit full.names = FALSE è chiave. Se questo se TRUE, list.files() restituisce il nome file corrispondente come "./bar.csv", quale tar() si attiverebbe in una cartella all'interno del tarball. Se lo impostiamo su FALSE, list.files() restituisce "bar.csv", così otteniamo un archivio tar con un singolo file CSV come richiesto.

Se si dispone di file con nomi simili e si desidera solo trovare il nome del file specificato, piolo all'interno del modello con il ^ e $, ad esempio:

tar2("foo.tar", path = ".", pattern = "^bar.csv$", recursive = FALSE, 
    full.names = FALSE, all.files = FALSE) 

Ecco l'modificato tar() funzione tar2():

tar2 <- function (tarfile, files = NULL, compression = c("none", "gzip", 
    "bzip2", "xz"), compression_level = 6, tar = Sys.getenv("tar"), 
    pattern = NULL, all.files = TRUE, recursive = TRUE, full.names = TRUE) 
{ 
    if (is.character(tarfile)) { 
     TAR <- tar 
     if (nzchar(TAR) && TAR != "internal") { 
      flags <- switch(match.arg(compression), none = "cf", 
       gzip = "zcf", bzip2 = "jcf", xz = "Jcf") 
      cmd <- paste(TAR, flags, shQuote(tarfile), paste(shQuote(files), 
       collapse = " ")) 
      return(invisible(system(cmd))) 
     } 
     con <- switch(match.arg(compression), none = file(tarfile, 
      "wb"), gzip = gzfile(tarfile, "wb", compress = compression_level), 
      bzip2 = bzfile(tarfile, "wb", compress = compression_level), 
      xz = xzfile(tarfile, "wb", compress = compression_level)) 
     on.exit(close(con)) 
    } 
    else if (inherits(tarfile, "connection")) 
     con <- tarfile 
    else stop("'tarfile' must be a character string or a connection") 
    files <- list.files(files, recursive = recursive, all.files = all.files, 
     full.names = full.names, pattern = pattern) 
    bf <- unique(dirname(files)) 
    files <- c(bf[!bf %in% c(".", files)], files) 
    for (f in unique(files)) { 
     info <- file.info(f) 
     if (is.na(info$size)) { 
      warning(gettextf("file '%s' not found", f), domain = NA) 
      next 
     } 
     header <- raw(512L) 
     if (info$isdir && !grepl("/$", f)) 
      f <- paste(f, "/", sep = "") 
     name <- charToRaw(f) 
     if (length(name) > 100L) { 
      if (length(name) > 255L) 
       stop("file path is too long") 
      s <- max(which(name[1:155] == charToRaw("/"))) 
      if (is.infinite(s) || s + 100 < length(name)) 
       stop("file path is too long") 
      warning("storing paths of more than 100 bytes is not portable:\n ", 
       sQuote(f), domain = NA) 
      prefix <- name[1:(s - 1)] 
      name <- name[-(1:s)] 
      header[345 + seq_along(prefix)] <- prefix 
     } 
     header[seq_along(name)] <- name 
     header[101:107] <- charToRaw(sprintf("%07o", info$mode)) 
     uid <- info$uid 
     if (!is.null(uid) && !is.na(uid)) 
      header[109:115] <- charToRaw(sprintf("%07o", uid)) 
     gid <- info$gid 
     if (!is.null(gid) && !is.na(gid)) 
      header[117:123] <- charToRaw(sprintf("%07o", gid)) 
     size <- ifelse(info$isdir, 0, info$size) 
     header[137:147] <- charToRaw(sprintf("%011o", as.integer(info$mtime))) 
     if (info$isdir) 
      header[157L] <- charToRaw("5") 
     else { 
      lnk <- Sys.readlink(f) 
      if (is.na(lnk)) 
       lnk <- "" 
      header[157L] <- charToRaw(ifelse(nzchar(lnk), "2", 
       "0")) 
      if (nzchar(lnk)) { 
       if (length(lnk) > 100L) 
        stop("linked path is too long") 
       header[157L + seq_len(nchar(lnk))] <- charToRaw(lnk) 
       size <- 0 
      } 
     } 
     header[125:135] <- charToRaw(sprintf("%011o", as.integer(size))) 
     header[258:262] <- charToRaw("ustar") 
     header[264:265] <- charToRaw("0") 
     s <- info$uname 
     if (!is.null(s) && !is.na(s)) { 
      ns <- nchar(s, "b") 
      header[265L + (1:ns)] <- charToRaw(s) 
     } 
     s <- info$grname 
     if (!is.null(s) && !is.na(s)) { 
      ns <- nchar(s, "b") 
      header[297L + (1:ns)] <- charToRaw(s) 
     } 
     header[149:156] <- charToRaw(" ") 
     checksum <- sum(as.integer(header))%%2^24 
     header[149:154] <- charToRaw(sprintf("%06o", as.integer(checksum))) 
     header[155L] <- as.raw(0L) 
     writeBin(header, con) 
     if (info$isdir || nzchar(lnk)) 
      next 
     inf <- file(f, "rb") 
     for (i in seq_len(ceiling(info$size/512L))) { 
      block <- readBin(inf, "raw", 512L) 
      writeBin(block, con) 
      if ((n <- length(block)) < 512L) 
       writeBin(raw(512L - n), con) 
     } 
     close(inf) 
    } 
    block <- raw(512L) 
    writeBin(block, con) 
    writeBin(block, con) 
    invisible(0L) 
} 
+0

Ho avuto l'impressione che stavo cercando di fare tar() fare qualcosa che non è destinato a fare. Grazie per la conferma;) –

+0

@JD Long Si può fare modificando la funzione 'tar()' per accettare argomenti diversi. Vedi la mia risposta aggiornata per una soluzione. –

+0

che dovrebbe essere una patch. Accidenti, ben fatto, Gavin. –

1

Cosa succede se si fornisce a files= il vettore di carattere richiesto, ad esempio eseguendo list.files() (o il suo sinonimo dir()) con uno schema adatto? Mi pare che l'aiuto è chiaro che vi:

Arguments:

tarfile: The pathname of the tarfile: tilde expansion (see ‘path.expand’) will be performed. Alternatively, a connection that can be used for binary writes.

files: A character vector offilepaths to be archived: the default is to archive all files under the current directory.

+0

sì, avrei dovuto essere più prolisso nella mia domanda. Ho iniziato con questo ... Permettetemi di modificare la domanda per il caso semplice di un singolo file, che è quello che ho iniziato a chiedere, ma poi passò al caso a carattere jolly a metà domanda. –

3

Questo è stato rivisto di nuovo semplificando ulteriormente il codice tar1. Inoltre, tar1 può ora produrre un file senza una directory o più file senza una directory. Essenzialmente abbiamo bisogno di aggirare un bug nell'uso di list.files da parte di R in tar e lo facciamo ridefinendo lo list.files utilizzato da tar1.

La linea che manipola l'ambiente di tar in realtà crea una copia di tar il cui ambiente è l'ambiente all'interno tar1 in modo che quando l'copiato tar è gestito sembra prima lì per list.files. Se non avessimo fatto una copia di tar con un nuovo ambiente, sarebbe stato utilizzato lo list.files nella base di R, ignorando la nostra ridefinizione.

Sotto tar1 è una variazione del comando tar che produce un file tar i cui componenti hanno un livello (vale a dire un singolo file o un insieme di file senza directory). Si presume che tutti i file siano nella directory corrente.

Seguendo la definizione di tar1, testiamo creando due file e creando un archivio con il primo di questi file e quindi con entrambi i file.

# tar a single file 
tar1 <- function(...) { 
    list.files <- function(...) ..1 
    environment(tar) <- environment() 
    tar(...) 
} 

# test - first create test files, then some test runs of tar1 
cat("a", file = "a.csv") 
cat("b", file = "a.csv") 

tar1("tst.tgz", "a.csv", "gzip") 
tar1("tst2.tgz", Sys.glob("*.csv"), "gzip") 
+0

Funziona sul mio sistema. Ho aggiunto l'output al post per mostrarlo. –

+0

@JD A lungo semplificato la sua Q per provare a tar un singolo file csv dichiarato. –

+0

Ho rivisto la risposta come discusso nella prima frase di risposta. –

Problemi correlati