rguides

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

ParameterTypeDefaultDescription
filecharacter or connection""File path (tilde-expanded, supports compressed files and URLs), connection, or "" to read from the console. Use "stdin" in scripts.
whatatomic prototype or list of prototypesdouble()Type to read. double(), integer(), character(), logical(), complex(), raw(), or list(int, chr, num) for columnar input.
nmaxinteger-1Maximum number of data values to read. -1 means no limit.
ninteger-1Historical alias for nmax. New code should prefer nmax.
sepcharacter (1 byte)""Field separator. The empty default accepts any whitespace. Must be exactly one byte.
quotecharacter"" if sep == "\n", else "'\""Quoting characters. Single-byte ASCII.
deccharacter (1 byte)"."Decimal-point character for numeric fields.
skipinteger0Lines to skip before reading values.
nlinesinteger0Positive value caps the number of lines of data to read.
na.stringscharacter vector"NA"Tokens interpreted as NA. Blank fields are missing for numeric, integer, logical, and complex types.
flushlogicalFALSEIf TRUE, advance to the end of the line after the last requested field. Required for one record per line.
filllogicalFALSEIf TRUE, pad short lines with empty fields.
strip.whitelogical or logical vectorFALSEStrip leading/trailing whitespace from character fields. Recycled if length 1.
quietlogicalFALSEIf FALSE, prints a one-line summary of items read.
blank.lines.skiplogicalTRUEIgnore blank lines (still counted toward skip and nlines).
multi.linelogicalTRUEWhen what is a list, allow a single record to span multiple lines. Set to FALSE (with flush = TRUE) for one record per line.
comment.charcharacter""Character that starts a comment; the rest of the line is discarded. The empty default disables comments.
allowEscapeslogicalFALSEIf TRUE, process C-style escapes (\n, \t, \040, …) in character fields.
fileEncodingcharacter""Encoding of a file on disk. Ignored when reading from a connection.
encodingcharacter"unknown"Marks character strings as "latin1" or "UTF-8". Does not re-encode.
textcharacterInline string to read from. Use text = "..." instead of a file path.
skipNullogicalFALSEIf 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

  1. sep is a separator, not a terminator. Reading with sep = "\n" on a file that ends in a newline produces a trailing empty field. Use readLines() for whole lines verbatim.
  2. sep must be a single byte. Multi-byte separators raise an error. Use sep = ",", not sep = ", ".
  3. multi.line = TRUE is the R default but FALSE in S. Combine with flush = TRUE to force S-style one-record-per-line reading.
  4. Empty numeric fields are always NA. Empty character fields are "" unless na.strings contains "".
  5. Console input has a 4095-byte line cap. For longer lines, read from a file or a textConnection.
  6. Embedded NUL bytes terminate the current field with a warning. Pass skipNul = TRUE to ignore them, or use readBin() for binary data.

scan() vs alternatives

TaskUse this, not scan
Rectangular CSV or TSV with a headerread.csv, data.table::fread, readr::read_csv
Read a file line-by-line as characterreadLines()
Read fixed-width recordsread.fwf
Write a vector to a file, one item per linewriteLines()
Read binary numeric datareadBin()
Build a vector from literalsc()

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 what is a list of column prototypes.