rguides

file()

file(description = "", open = "", blocking = TRUE, encoding = getOption("encoding"), raw = FALSE, method = getOption("url.method", "default"))

file() returns a connection object that wraps a file path, a URL, or a special stream such as the system clipboard. Functions like readLines(), writeLines(), scan(), cat(), readBin(), and writeBin() accept the connection and use it for reading or writing, which is the same machinery that runs when you hand them a path string directly. Working with the explicit connection gives you a handle you can open, rewind, append to, and close on demand.

Signature

file(description = "", open = "", blocking = TRUE,
     encoding = getOption("encoding"), raw = FALSE,
     method = getOption("url.method", "default"))

Parameters

ParameterTypeDefaultDescription
descriptioncharacter""Path (tilde-expanded), an http:// / https:// / ftp:// / file:// URL, "" for a temp file, or "clipboard". "stdin" refers to the C-level stdin of the R process.
opencharacter""Mode string (see Modes below). The default leaves the connection closed.
blockinglogicalTRUEWhether the connection blocks on I/O.
encodingcharactergetOption("encoding")Encoding of the stream on disk. "" or "native.enc" means the native locale encoding with no re-encoding.
rawlogicalFALSEUse the raw interface. For character devices this skips the compressed-file magic-byte sniff and asserts the connection is non-seekable.
methodcharactergetOption("url.method", "default")One of default, internal, wininet, libcurl. Only relevant when description is a URL.

Return value

A connection object of class c("file", "connection") that you can pass to readLines(), writeLines(), scan(), cat(), readBin(), writeBin(), and friends. The handle starts closed unless you pass a non-empty open argument, or open it later with the open() generic.

Opening and closing

By default the connection is not opened, which surprises most people the first time. Passing description alone gives you a handle on a file that is not yet open, so readLines(file("data.txt")) fails with an error rather than reading your file. Two ways out of this:

# Option A: open the handle right away
con <- file("data.txt", "r")
readLines(con)
close(con)

# Option B: open later with the generic
con <- file("data.txt")
open(con, "r")
readLines(con)
close(con)

Most read or write helpers will open a closed connection for the duration of the call, then close it again, which is the path of least resistance for one-shot reads. The catch: when you build a connection yourself, R will not close it for you. Garbage collection eventually closes it with a warning, and any buffered output that has not been flushed can be lost.

The safe pattern inside a function is on.exit(close(con), add = TRUE), which guarantees the close runs whether the function returns normally or throws an error:

read_all <- function(path) {
  con <- file(path, "r")
  on.exit(close(con), add = TRUE)
  readLines(con)
}

close() destroys the connection, not just releases the file. After close(con) you cannot reuse the object. Build a new one with file() if you need to read or write again.

Reading and writing through a connection

The classic write-then-read round trip uses cat() to write and readLines() to read, with the connection threaded through both:

# Write
con <- file("ex.data", "w")
cat("TITLE extra line", "2 3 5 7", "", "11 13 17",
    file = con, sep = "\n")
cat("One more line\n", file = con)
close(con)

# Read it back in two chunks
con <- file("ex.data", "r")
readLines(con, n = 3)
# [1] "TITLE extra line" "2 3 5 7"          ""
readLines(con)
# [1] "11 13 17"      "One more line"
close(con)

The same handle pattern works for binary data. The text-versus-binary mode flag only changes behaviour on Windows, where text mode translates between LF and CRLF; on Linux and macOS the distinction usually does not matter. readBin() and writeBin() always need a binary mode, though, because they move raw bytes rather than characters. The block below writes three doubles, closes the handle, reopens the same path in "rb" mode, and pulls them back as a numeric vector of length three:

con <- file("nums.bin", "wb")
writeBin(c(1.5, 2.5, 3.5), con)
close(con)

con <- file("nums.bin", "rb")
readBin(con, what = "double", n = 3)
# [1] 1.5 2.5 3.5
close(con)

seek() works on a file connection, which is the trick behind the temp-file scratch pattern: write, rewind, read back. The catch is that seek() is unreliable on text-mode files on Windows, so use binary mode or re-open the file when you need precise positioning.

Modes

open is a mode string. The text-versus-binary distinction only changes behaviour on Windows, where text mode translates between LF and CRLF. Read or write helpers such as readBin(), writeBin(), load(), and save() need a binary mode.

ModeMeaning
"r" / "rt"Open for reading in text mode
"w" / "wt"Open for writing in text mode, truncating the file
"a" / "at"Open for appending in text mode
"rb"Open for reading in binary mode
"wb"Open for writing in binary mode, truncating the file
"ab"Open for appending in binary mode
"r+", "r+b"Open for reading and writing
"w+", "w+b"Open for reading and writing, truncating first
"a+", "a+b"Open for reading and appending

URLs opened through file() are read-only. Plain file connections support reading and writing. truncate(con) is only defined for file connections, and pushBack() only works on text-mode input connections.

truncate() cuts a writable file at the current position, which is the right tool when you want to keep only the bytes already written:

# Truncate to the first 5 bytes ("hello")
con <- file("out.bin", "wb")
writeBin(charToRaw("hello world"), con)
seek(con, where = 5)
truncate(con)
close(con)
# File now contains just "hello" (5 bytes)

URLs, clipboard, and stdin

A description that begins with http://, https://, ftp://, or file:// is forwarded to url(), so file("https://example.com/data.csv", "r") is equivalent to url("https://example.com/data.csv", "r"). On recent R, the method argument is the only knob that matters here: method = "default" uses the internal handler for file:// URLs and libcurl for everything else, and libcurl is the only way to get https:// and ftps:// working on most platforms. ftp:// support in the internal handler was deprecated in R 4.1.1 and removed in R 4.2.0.

A remote read works exactly like a local one: open the handle, hand it to the parser, close:

# Reading a remote CSV through file()
con <- file("https://example.com/data.csv", "r")
df <- read.csv(con)
close(con)

The system clipboard works through the same handle. On Windows and macOS you can read and write it directly; on Linux you need an X-selection tool such as xclip configured before file("clipboard") will succeed. A paste-and-read round trip:

# Paste the clipboard into a character vector
con <- file("clipboard", "r")
text <- readLines(con)
close(con)

A few special description values are worth knowing:

  • "" (the default) on a file connection opens a temporary file in "w+" mode and unlinks it from the filesystem immediately. This is the right scratch space when you want to write, rewind, and read back without leaving a file on disk.
  • "clipboard" reads or writes the system clipboard. On Windows and macOS this works out of the box. On Linux you need an X-selection tool such as xclip configured.
  • "stdin" is the C-level stdin of the R process, which is what you want when reading piped input from Rscript in a non-interactive session.

A temp-file scratch round trip:

con <- file("", "w+")
writeLines(c("x", "y", "z"), con)
seek(con, where = 0)
readLines(con)
# [1] "x" "y" "z"
close(con)

State, seek, and flush

A handful of generics round out the connection API and earn their place in everyday work. isOpen() reports whether the handle is currently in use. isIncomplete() returns TRUE when the last read on a pipe or socket finished without a trailing newline, so it is the right test before deciding whether to call readLines() again. seek() rewinds a file connection to a byte offset, flush() forces a write buffer to disk before the connection closes, and showConnections() lists every open handle in the session. A short tour of all five:

# isOpen / isIncomplete / seek on a read handle
con <- file("ex.data", "r")
isOpen(con)               # TRUE
readLines(con)            # read to EOF
isIncomplete(con)         # FALSE — file ends with a newline
seek(con, where = 0)      # rewind to start
close(con)

# flush: commit a write buffer to disk before close
con <- file("out.txt", "w")
writeLines(c("first", "second"), con)
flush(con)                # bytes hit disk now, even before close
close(con)

# showConnections: list every open handle in this session
showConnections()

isOpen() reports state, isIncomplete() is the right tool after readLines() on a pipe or socket (where the final line may not have a trailing newline), seek() rewinds or jumps to a byte offset, flush() commits a write buffer to disk, and showConnections() lists every open handle in the session. seek() is unreliable on text-mode files on Windows, so prefer binary mode when you need precise positioning.

Encoding

encoding names the encoding of the stream on disk, not of your R strings. When you write through a file connection, R converts your strings to the native locale encoding before writing, regardless of encoding (unless you pass useBytes = TRUE). On a UTF-8 native system (R 4.2 and later on Windows included), this just works. On older non-UTF-8 Windows, the fix is to open the connection with the source encoding and let the connection layer re-encode:

con <- file("notes.txt", encoding = "latin1")
lines <- readLines(con)
close(con)

For the underlying conversion machinery, see iconv().

Common mistakes

A file() result is closed by default. Pass a mode or call open(). Otherwise readLines(file("data.txt")) will not work, and the error message does not always make the cause obvious.

close() destroys the object. After close(con) the connection is gone, so you cannot re-open it. Build a new one with file().

Buffering loses output on a missing close. Output written through a file connection is not on disk until you flush() or close(), and a hard exit or a process kill before the close means lost data. Always close() before relying on the file elsewhere, and use on.exit(close(con), add = TRUE) inside functions.

Mixing text and binary reads on the same connection is undefined. If you need both, open the connection twice with different modes.

raw = TRUE disables the compressed-file sniff. Leave it FALSE when you actually want R to detect a .gz or .bz2 file.

method = "internal" does not handle https:// on recent R. If you get “unsupported URL scheme”, switch to method = "libcurl".

See also

  • readLines(), the most common way to read text through a file connection
  • writeLines(), the inverse, for writing text through a file connection
  • on.exit(), the standard on.exit(close(con), add = TRUE) cleanup pattern for connections opened in functions