rguides

readLines()

readLines(con = stdin(), n = -1L, ok = TRUE, warn = TRUE, encoding = "unknown", skipNul = FALSE)

readLines() reads text lines from a file, URL, or open connection in R. It returns a character vector with one element per line, line terminators stripped, and it works the same way in scripts as it does at the console. Compressed files with the extensions .gz, .bz2, and .xz are read transparently without any extra setup.

Syntax

readLines(con = stdin(), n = -1L, ok = TRUE, warn = TRUE,
          encoding = "unknown", skipNul = FALSE)

Parameters

ParameterTypeDefaultDescription
cona connection object or a character stringstdin()A file path. Internally passed to file(), so tilde expansion happens. URLs ("https://...") and paths to .gz, .bz2, or .xz files are handled transparently.
ninteger-1LMaximum number of lines to read. Negative values mean “read to the end of the connection.”
oklogicalTRUEWhether it is acceptable to hit end-of-file before n > 0 lines are read. If FALSE, an error is raised instead of a short result.
warnlogicalTRUEWarn when a text file is missing a final newline, or when the input contains embedded NUL bytes.
encodingcharacter"unknown"Declared encoding to mark the resulting strings as "latin1", "UTF-8", or "bytes". This does not re-encode the input bytes. To actually convert encoding, set it on the connection instead.
skipNullogicalFALSEIf TRUE, embedded NUL bytes are silently skipped instead of terminating the current line and producing a warning.

Return value

A character vector. Its length equals the number of lines read, and each element is one line with its terminator (LF, CRLF, or CR) stripped. When encoding is set to "latin1" or "UTF-8", the resulting strings carry that declared encoding; with the default "unknown", the encoding is left as-is.

A quick demonstration of the stripping behaviour: a file with a mix of CRLF and LF terminators comes back as plain strings, and length() of the result equals the number of records, not the number of bytes.

# Mixed line terminators are all stripped
fil <- tempfile()
cat("first\r\nsecond\nthird", file = fil)
readLines(fil)
# [1] "first"  "second" "third"
unlink(fil)

Examples

Read every line of a file

The simplest case: pass a path, get a character vector back. The first call is the one you will write in nearly every script that reads plain text, so it is worth running it end to end before reaching for any of the more elaborate options shown later in this article.

fil <- tempfile(fileext = ".data")
cat("TITLE extra line", "2 3 5 7", "", "11 13 17", file = fil, sep = "\n")
readLines(fil, n = -1)
# [1] "TITLE extra line" "2 3 5 7"          ""                "11 13 17"
unlink(fil)

Notice that the blank line in the input becomes an empty string in the result, and the vector is one element longer than the number of non-empty lines.

Read at most N lines

Setting n caps the read. This is the standard way to peek at the header of a log file or a CSV before deciding how to parse the rest.

# Read just the first three lines
readLines("logfile.txt", n = 3)
# [1] "2026-06-15 10:00:00 INFO  Starting"
# [2] "2026-06-15 10:00:01 INFO  Loaded 1243 records"
# [3] "2026-06-15 10:00:02 WARN  Missing field 'email' on row 17"

Stream a large file in chunks

Passing an open connection rather than a path keeps the connection alive between calls, which lets you walk a big file in fixed-size chunks without loading it all into memory.

con <- file("big.txt", "r")
chunk <- readLines(con, n = 1000)         # first 1000 lines
# ... process chunk ...
next_chunk <- readLines(con, n = 1000)   # continues from current position
close(con)

This pattern is the right tool when a single line might be tens of megabytes long, or when the file is bigger than the available RAM.

Read a UTF-8 file and mark the encoding

encoding does not re-encode bytes. To mark the resulting strings as UTF-8, set the encoding on the connection itself.

con <- file("notes.txt", encoding = "UTF-8")
lines <- readLines(con)
Encoding(lines)
# [1] "UTF-8" "UTF-8" "UTF-8"
close(con)

If you have a legacy encoding like "Latin-1" or "UCS-2LE", the same trick works: open the connection with the source encoding and readLines() will convert to UTF-8 and mark the strings accordingly.

Read a gzipped text file directly

readLines() dispatches on the file extension and decompresses on the fly. No need to call gzfile() manually for the common case.

lines <- readLines("logs/2026-06-15.log.gz")
length(lines)
# [1] 8421

The count is the number of newline-delimited records in the decompressed stream, not the number of bytes on disk, so it lines up with wc -l on the same file after gunzip -c. Use n first if you only need the header before paying for the full decompression.

Incomplete final line

Files that do not end with a newline produce a warning on blocking connections, and the final partial line is still returned.

fil <- tempfile("test")
cat("123\nabc", file = fil)        # no trailing \n
readLines(fil)
# [1] "123" "abc"
# Warning message:
# incomplete final line found on '...test...'

If you do not care about the warning, pass warn = FALSE.

Common patterns

Read from a URL. readLines() accepts an HTTP or HTTPS URL as con, which is handy for grabbing small public datasets or fixed text resources.

readLines("https://example.com/data.txt", n = 5, warn = FALSE)

Read piped stdin in a script. When you launch R with Rscript script.R < input.txt, the right way to read piped input is con = "stdin" (a string), not the default stdin() connection. The string form goes through the file machinery and works in non-interactive mode.

lines <- readLines("stdin", n = -1)

Build fixtures with cat() and read them back. During tests, you can create a small file inline with cat(), then read it back with readLines() to exercise your parsing code without needing a real fixture file.

fil <- tempfile()
cat("alpha\nbeta\ngamma\n", file = fil)
readLines(fil)
# [1] "alpha" "beta"  "gamma"
unlink(fil)

Count lines, including blanks. Because blank lines are preserved, length(readLines(path)) is a clean way to count total lines, including empty ones, in a text file.

Skip embedded NULs. Binary-ish files that contain stray \0 bytes will trip up readLines() with a warning per affected line. For best-effort reads, pass skipNul = TRUE, warn = FALSE.

readLines() vs alternatives

readline() is the singular sibling and reads exactly one line from the terminal in an interactive session. In a non-interactive script it returns "" immediately. The names look almost identical, and that is the single most common source of confusion between the two. If you want a one-line read from stdin inside a script, use readLines("stdin", n = 1).

scan() reads tokenised values (whitespace-separated by default) rather than whole lines, which is faster for numeric columns but does not preserve the original line structure.

readBin() is the right choice for actual binary data, including UTF-16 text and compressed streams you did not mean to treat as text.

readr::read_lines() is the tidyverse equivalent. It suppresses trailing newlines, takes a tidyverse-style n_max argument, and integrates naturally with readr’s other readers. Use it when you are already in a tibble workflow and want consistent behaviour across file formats.

writeLines() is the inverse, the same idioms for connections and encoding apply in both directions.

A side-by-side shows the difference clearly. Given a two-line file with whitespace-separated numbers, readLines() returns the lines verbatim and scan() tokenises them.

fil <- tempfile()
cat("1 2 3", "4 5 6", sep = "\n", file = fil)

readLines(fil)
# [1] "1 2 3" "4 5 6"
scan(fil, what = numeric(), quiet = TRUE)
# [1] 1 2 3 4 5 6
unlink(fil)

A quick reference table summarises the trade-offs at a glance:

FunctionReads fromReturnsUse it for
readLines()File, URL, or connectionCharacter vector, one line per elementGeneric text, logs, line-oriented input
readline()The terminalSingle stringInteractive prompts only
scan()File or connectionTyped vector (whitespace-separated)Numeric columns and similar structured data
readBin()File or connectionRaw byte vectorBinary files, UTF-16, raw streams
readr::read_lines()File pathCharacter vector (tidyverse)Tibble workflows and readr consistency
readr::read_file()File pathSingle stringSmall files where line structure does not matter
writeLines()n/a (writes)n/aThe inverse of readLines()

See also

  • writeLines(), the inverse: write a character vector to a file or connection
  • readline(), reads a single line from the terminal in an interactive session
  • cat(), write character data, handy for building fixtures that readLines() reads back