использование внутренней функции tar R для определенных файлов - PullRequest
13 голосов
/ 23 января 2011

R имеет удобную кроссплатформенную функцию tar (), которая может tar и gzip файлы.Кажется, эта функция была разработана для подбора целых каталогов.Я надеялся использовать эту функцию для сжатия и сжатия подмножества каталога или одного файла.Однако я не могу этого сделать.Я ожидал, что следующее смоделирует единственный файл csv в текущем рабочем каталоге:

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

Так можно ли использовать функцию tar () только для каталогов?Мы временно обошли это, создав временный каталог, скопировав мои файлы, а затем скопировав весь временный каталог.Но я надеялся на более простое решение.Это не потребует копирования файлов, что отнимает много времени для больших файлов.

Ответы [ 3 ]

10 голосов
/ 23 января 2011

Я не думаю, что это возможно, когда вы описываете JD.Аргумент files передается аргументу path в list.files, и в результате он работает путем заархивирования файлов в каталогах, а не отдельных файлов.

Если вы готовы редактировать внутреннюю функциюtar() можно заставить делать то, что вы хотите, играя с list.files() внутри tar().Немного возни сгенерировал приведенную ниже функцию tar2(), которая имеет дополнительные аргументы для управления тем, что возвращает list.files().Используя эту функцию, мы можем добиться того, что вы хотите, с помощью вызова, подобного этому:

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

all.files = FALSE, вероятно, избыточен, если у вас нет скрытых файлов с именами, содержащими "bar.csv".

Бит recursive = FALSE просто останавливает поиск функции в любом месте, кроме текущего каталога, который выглядит так, как вам нужно, и ускоряет поиск, если в рабочем каталоге много файлов и подпапок.

full.names = FALSEбит это ключ.Если это, если TRUE, list.files() возвращает совпадающее имя файла как "./bar.csv", которое tar() будет помещаться в папку внутри архива.Если мы установим значение FALSE, list.files() вернет "bar.csv", поэтому мы получим тарбол с одним CSV-файлом в соответствии с запросом.

Если у вас есть файлы с похожими именами и вы хотите найти только указанныеимя файла, отметьте его внутри шаблона с помощью ^ и $, например:

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

Вот модифицированная функция tar() как 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)
}
3 голосов
/ 23 января 2011

Это было снова пересмотрено, чтобы упростить код tar1.Также tar1 теперь может выводить один файл без каталога или несколько файлов без каталога.По сути, нам нужно обойти ошибку при использовании list.files для R в tar, и мы делаем это путем переопределения list.files, используемого tar1.

Строка, которая управляет средой tar, фактически создает копию tar, среда которой является средой в пределах tar1, поэтому при запуске скопированного tar она сначала ищет там list.files.Если бы мы не сделали копию tar с новой средой, то она использовала бы list.files в основании R, игнорируя наше переопределение.

Ниже tar1 - это вариант tar команда, которая создает файл tar, компоненты которого имеют один уровень (то есть один файл или набор файлов без каталогов).Предполагается, что все файлы находятся в текущем каталоге.

Следуя определению tar1, мы проверяем его, создав два файла и создав архив с первым из этих файлов, а затем с обоими этими файлами.

# 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")
1 голос
/ 23 января 2011

Что произойдет, если вы дадите files= вектор символов, который он запрашивает, скажем, запустив list.files() (или его синоним dir()) с подходящим шаблоном? Мне кажется помощь там понятна:

Аргументы:

tarfile: путь к файлу tarfile: расширение тильды (см. «Path.expand» будет выполнено. В качестве альтернативы соединение, которое можно использовать для двоичной записи.

files: символьные векторные пути, подлежащие архивированию: по умолчанию архивировать все файлы в текущем каталоге.

...