scan
scan(file = "", what = double(), nmax = -1, n = -1, sep = "", ...) scan() reads data values from a file, connection, or inline string into R as a vector (or, when what is a list, as a list of vectors, one per field). It is the low-level workhorse that read.table() and read.csv() are built on top of, and it gives you direct control over the separator, field types, line skipping, and comments.
Reach for scan() when the data is not rectangular: a stream of one number per line, a column to extract without dragging a header, a file where you only want the first 1,000 records, or a mix of these shapes. For ordinary rectangular CSV or TSV files, read.csv(), data.table::fread(), and readr::read_csv() are faster and handle the edge cases for you.
Syntax
scan(
file = "",
what = double(),
nmax = -1,
n = -1,
sep = "",
quote = if (identical(sep, "\n")) "" else "'\"",
dec = ".",
skip = 0,
nlines = 0,
na.strings = "NA",
flush = FALSE,
fill = FALSE,
strip.white = FALSE,
quiet = FALSE,
blank.lines.skip = TRUE,
multi.line = TRUE,
comment.char = "",
allowEscapes = FALSE,
fileEncoding = "",
encoding = "unknown",
text,
skipNul = FALSE
)
The default file = "" reads from the console in an interactive session, or from stdin in a script. Passing text = "..." is the easiest way to exercise the parser without creating a real file on disk.
A first read
Before the table of arguments, here is the smallest useful call. One line of whitespace-separated numbers in, a typed vector out:
scan(text = "1 2 3 4 5")
# [1] 1 2 3 4 5
class(scan(text = "1 2 3 4 5"))
# [1] "numeric"
The rest of this page unpacks the arguments and shows the patterns the help page glosses over.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
file | character or connection | "" | File path (tilde-expanded, supports compressed files and URLs), connection, or "" to read from the console. Use "stdin" in scripts. |
what | atomic prototype or list of prototypes | double() | Type to read. double(), integer(), character(), logical(), complex(), raw(), or list(int, chr, num) for columnar input. |
nmax | integer | -1 | Maximum number of data values to read. -1 means no limit. |
n | integer | -1 | Historical alias for nmax. New code should prefer nmax. |
sep | character (1 byte) | "" | Field separator. The empty default accepts any whitespace. Must be exactly one byte. |
quote | character | "" if sep == "\n", else "'\"" | Quoting characters. Single-byte ASCII. |
dec | character (1 byte) | "." | Decimal-point character for numeric fields. |
skip | integer | 0 | Lines to skip before reading values. |
nlines | integer | 0 | Positive value caps the number of lines of data to read. |
na.strings | character vector | "NA" | Tokens interpreted as NA. Blank fields are missing for numeric, integer, logical, and complex types. |
flush | logical | FALSE | If TRUE, advance to the end of the line after the last requested field. Required for one record per line. |
fill | logical | FALSE | If TRUE, pad short lines with empty fields. |
strip.white | logical or logical vector | FALSE | Strip leading/trailing whitespace from character fields. Recycled if length 1. |
quiet | logical | FALSE | If FALSE, prints a one-line summary of items read. |
blank.lines.skip | logical | TRUE | Ignore blank lines (still counted toward skip and nlines). |
multi.line | logical | TRUE | When what is a list, allow a single record to span multiple lines. Set to FALSE (with flush = TRUE) for one record per line. |
comment.char | character | "" | Character that starts a comment; the rest of the line is discarded. The empty default disables comments. |
allowEscapes | logical | FALSE | If TRUE, process C-style escapes (\n, \t, \040, …) in character fields. |
fileEncoding | character | "" | Encoding of a file on disk. Ignored when reading from a connection. |
encoding | character | "unknown" | Marks character strings as "latin1" or "UTF-8". Does not re-encode. |
text | character | — | Inline string to read from. Use text = "..." instead of a file path. |
skipNul | logical | FALSE | If TRUE, ignore embedded NUL bytes instead of terminating the field with a warning. |
Return value
If what is atomic, scan() returns a vector of that type. The common prototypes are double(), integer(), character(), and logical(). If what is a list, the return is a list of the same length, one vector per field, with names inherited from the list. Put NULL in the list to skip a column entirely.
Examples
Read a vector from inline text
The hello world of scan(). With no what argument, you get a numeric vector of whitespace-separated tokens.
scan(text = "1 2 3 4 5")
# [1] 1 2 3 4 5
By default, scan() reads whitespace-separated tokens as doubles. If your data is text rather than numbers, pass what = character() to keep the values as strings. For whole numbers, use what = integer(). For TRUE/FALSE values, use what = logical(). The output type is just whatever prototype you pass to what.
scan(text = "alpha beta gamma", what = character())
# [1] "alpha" "beta" "gamma"
Read from a file, skip a header line
The skip argument is the standard way to drop a title or metadata line before the actual data starts. The example writes a tempfile so it runs in a clean session without leaving anything behind.
tmp <- tempfile()
writeLines(c("TITLE extra line", "2 3 5 7", "11 13 17"), tmp)
scan(tmp, skip = 1, quiet = TRUE)
# [1] 2 3 5 7 11 13 17
scan(tmp, skip = 1, nlines = 1) # only the line "2 3 5 7"
# [1] 2 3 5 7
unlink(tmp)
Read columnar data with what = list(...)
When you want one record per row of input, pass a list of prototypes to what. Each element of the input row becomes the next value in the matching output vector. Setting sep = "," tells scan() to split on commas instead of whitespace.
scan(text = "1,2,3\n4,5,6\n7,8,9",
what = list(integer(), integer(), integer()),
sep = ",")
# $integer
# [1] 1 4 7
#
# $integer
# [1] 2 5 8
#
# $integer
# [1] 3 6 9
Skip a column with NULL
Drop a field you do not care about by putting NULL in the matching slot of the list. scan() reads the column, then discards it.
scan(text = "1,alpha,3.14\n4,beta,2.72",
what = list(integer(), NULL, double()),
sep = ",")
# $integer
# [1] 1 4
#
# $double
# [1] 3.14 2.72
One record per line, S-compatible
The default multi.line = TRUE lets a single record span multiple lines, which differs from S. For one record per line, set multi.line = FALSE, flush = TRUE, and pick a sep (often "\n").
scan(text = "1 alice\n2 bob\n3 carol",
what = list(integer(), character()),
flush = TRUE, multi.line = FALSE,
strip.white = TRUE, sep = "\n")
# $integer
# [1] 1 2 3
#
# $character
# [1] "alice" "bob" "carol"
Custom NA strings, comments, and European decimals
scan() parses numbers, comments, and missing-value markers in a single pass. Combine dec = "," with comment.char = "#" to read continental-European CSV with a header line.
scan(text = "# header\n1,5\n2,5\nNA,0\n3,5",
sep = ",", dec = ",",
na.strings = "NA", comment.char = "#", quiet = TRUE)
# [1] 1.5 2.5 NA 3.5
Common patterns
Always pass nmax (or nlines) when reading large inputs. The internal buffer reallocates in powers of two, so memory can roughly triple if you let scan() grow it freely.
Building inputs with sprintf() or paste() and reading them back with scan(text = ...) is a clean way to exercise parsing code without writing fixture files. You get the same code path as reading from a real file, but the test is self-contained.
For UTF-8 (or Latin-1) sources, set the encoding when you open the connection. The fileEncoding and encoding arguments on scan() are only useful when you also pass a plain file path. On a pre-opened connection, they have no effect.
con <- file("notes.txt", encoding = "UTF-8")
scan(con, what = character(), quiet = TRUE)
close(con)
Gotchas
sepis a separator, not a terminator. Reading withsep = "\n"on a file that ends in a newline produces a trailing empty field. UsereadLines()for whole lines verbatim.sepmust be a single byte. Multi-byte separators raise an error. Usesep = ",", notsep = ", ".multi.line = TRUEis the R default butFALSEin S. Combine withflush = TRUEto force S-style one-record-per-line reading.- Empty numeric fields are always
NA. Empty character fields are""unlessna.stringscontains"". - Console input has a 4095-byte line cap. For longer lines, read from a file or a
textConnection. - Embedded NUL bytes terminate the current field with a warning. Pass
skipNul = TRUEto ignore them, or usereadBin()for binary data.
scan() vs alternatives
| Task | Use this, not scan |
|---|---|
| Rectangular CSV or TSV with a header | read.csv, data.table::fread, readr::read_csv |
| Read a file line-by-line as character | readLines() |
| Read fixed-width records | read.fwf |
| Write a vector to a file, one item per line | writeLines() |
| Read binary numeric data | readBin() |
| Build a vector from literals | c() |
scan() tokenises by whitespace (or whatever sep you set) and returns typed values. readLines() keeps the original line structure and returns a character vector. Pick scan() when you want numbers or a known mix of column types; pick readLines() when the line boundaries are the point.
Putting it together
The snippet below reads a small log file, skips the comment line, parses two named columns, and returns a list of typed vectors. Each piece of the call is something you have already seen in the table above.
log_path <- tempfile()
writeLines(c(
"# request log",
"GET 200",
"POST 201",
"GET 404",
"DELETE 500"
), log_path)
scan(
log_path,
what = list(method = character(), status = integer()),
comment.char = "#",
skip = 1,
quiet = TRUE
)
# $method
# [1] "GET" "POST" "GET" "DELETE"
#
# $status
# [1] 200 201 404 500
unlink(log_path)
See also
- readLines() — read whole lines into a character vector.
- c() — build a vector from the values
scan()returns. - list() — the return shape when
whatis a list of column prototypes.