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
| Parameter | Type | Default | Description |
|---|---|---|---|
con | a connection object or a character string | stdin() | A file path. Internally passed to file(), so tilde expansion happens. URLs ("https://...") and paths to .gz, .bz2, or .xz files are handled transparently. |
n | integer | -1L | Maximum number of lines to read. Negative values mean “read to the end of the connection.” |
ok | logical | TRUE | Whether 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. |
warn | logical | TRUE | Warn when a text file is missing a final newline, or when the input contains embedded NUL bytes. |
encoding | character | "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. |
skipNul | logical | FALSE | If 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:
| Function | Reads from | Returns | Use it for |
|---|---|---|---|
readLines() | File, URL, or connection | Character vector, one line per element | Generic text, logs, line-oriented input |
readline() | The terminal | Single string | Interactive prompts only |
scan() | File or connection | Typed vector (whitespace-separated) | Numeric columns and similar structured data |
readBin() | File or connection | Raw byte vector | Binary files, UTF-16, raw streams |
readr::read_lines() | File path | Character vector (tidyverse) | Tibble workflows and readr consistency |
readr::read_file() | File path | Single string | Small files where line structure does not matter |
writeLines() | n/a (writes) | n/a | The 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