Skip to content

Commit 7119419

Browse files
Merge pull request #20203 from JuliaLang/sk/chompreadline
readline(s), eachline: make `chomp=true` a keyword arg
2 parents 06fa32c + abbd76c commit 7119419

34 files changed

+185
-104
lines changed

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ Breaking changes
4646

4747
This section lists changes that do not have deprecation warnings.
4848

49+
* `readline`, `readlines` and `eachline` return lines without line endings by default.
50+
You *must* use `readline(s, chomp=false)`, etc. to get the old behavior where lines
51+
returned include trailing end-of-line character(s). ([#19944])
52+
4953
* `String`s no longer have a `.data` field (as part of a significant performance
5054
improvement). Use `Vector{UInt8}(str)` to access a string as a byte array.
5155
However, allocating the `Vector` object has overhead. You can also use

base/LineEdit.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf
211211
seek(buf, 0)
212212
moreinput = true # add a blank line if there is a trailing newline on the last line
213213
while moreinput
214-
l = readline(buf)
214+
l = readline(buf, chomp=false)
215215
moreinput = endswith(l, "\n")
216216
# We need to deal with on-screen characters, so use strwidth to compute occupied columns
217217
llength = strwidth(l)
@@ -549,7 +549,7 @@ end
549549
function edit_kill_line(s::MIState)
550550
buf = buffer(s)
551551
pos = position(buf)
552-
killbuf = readline(buf)
552+
killbuf = readline(buf, chomp=false)
553553
if length(killbuf) > 1 && killbuf[end] == '\n'
554554
killbuf = killbuf[1:end-1]
555555
char_move_left(buf)

base/REPL.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef)
205205
interrupted = false
206206
while true
207207
try
208-
line *= readline(repl.terminal)
208+
line *= readline(repl.terminal, chomp=false)
209209
catch e
210210
if isa(e,InterruptException)
211211
try # raise the debugger if present
@@ -337,7 +337,7 @@ An editor may have converted tabs to spaces at line """
337337

338338
function hist_getline(file)
339339
while !eof(file)
340-
line = readline(file)
340+
line = readline(file, chomp=false)
341341
isempty(line) && return line
342342
line[1] in "\r\n" || return line
343343
end
@@ -995,7 +995,7 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef)
995995
if have_color
996996
print(repl.stream, input_color(repl))
997997
end
998-
line = readline(repl.stream)
998+
line = readline(repl.stream, chomp=false)
999999
if !isempty(line)
10001000
ast = Base.parse_input_line(line)
10011001
if have_color

base/client.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ parse_input_line(s::AbstractString) = parse_input_line(String(s))
216216
function parse_input_line(io::IO)
217217
s = ""
218218
while !eof(io)
219-
s = s*readline(io)
219+
s *= readline(io, chomp=false)
220220
e = parse_input_line(s)
221221
if !(isa(e,Expr) && e.head === :incomplete)
222222
return e

base/deprecated.jl

+3
Original file line numberDiff line numberDiff line change
@@ -1770,4 +1770,7 @@ eval(LibGit2, quote
17701770
repository(x)
17711771
end
17721772
end)
1773+
1774+
@deprecate EachLine(stream, ondone) EachLine(stream, ondone=ondone)
1775+
17731776
# End deprecations scheduled for 0.6

base/io.jl

+57-32
Original file line numberDiff line numberDiff line change
@@ -170,24 +170,48 @@ The text is assumed to be encoded in UTF-8.
170170
readuntil(filename::AbstractString, args...) = open(io->readuntil(io, args...), filename)
171171

172172
"""
173-
readline(stream::IO=STDIN)
174-
readline(filename::AbstractString)
173+
readline(stream::IO=STDIN; chomp::Bool=true)
174+
readline(filename::AbstractString; chomp::Bool=true)
175+
176+
Read a single line of text from the given I/O stream or file (defaults to `STDIN`).
177+
When reading from a file, the text is assumed to be encoded in UTF-8. Lines in the
178+
input end with `'\\n'` or `"\\r\\n"` or the end of an input stream. When `chomp` is
179+
true (as it is by default), these trailing newline characters are removed from the
180+
line before it is returned. When `chomp` is false, they are returned as part of the
181+
line.
182+
"""
183+
function readline(filename::AbstractString; chomp::Bool=true)
184+
open(filename) do f
185+
readline(f, chomp=chomp)
186+
end
187+
end
175188

176-
Read a single line of text, including a trailing newline character (if one is reached before
177-
the end of the input), from the given I/O stream or file (defaults to `STDIN`).
178-
When reading from a file, the text is assumed to be encoded in UTF-8.
179-
"""
180-
readline(filename::AbstractString) = open(readline, filename)
189+
function readline(s::IO=STDIN; chomp::Bool=true)
190+
line = readuntil(s, 0x0a)
191+
i = length(line)
192+
if !chomp || i == 0 || line[i] != 0x0a
193+
return String(line)
194+
elseif i < 2 || line[i-1] != 0x0d
195+
return String(resize!(line,i-1))
196+
else
197+
return String(resize!(line,i-2))
198+
end
199+
end
181200

182201
"""
183-
readlines(stream::IO)
184-
readlines(filename::AbstractString)
202+
readlines(stream::IO=STDIN; chomp::Bool=true)
203+
readlines(filename::AbstractString; chomp::Bool=true)
185204
186-
Read all lines of an I/O stream or a file as a vector of strings.
187-
The text is assumed to be encoded in UTF-8.
205+
Read all lines of an I/O stream or a file as a vector of strings. Behavior is
206+
equivalent to saving the result of reading `readline` repeatedly with the same
207+
arguments and saving the resulting lines as a vector of strings.
188208
"""
189-
readlines(filename::AbstractString) = open(readlines, filename)
190-
209+
function readlines(filename::AbstractString; chomp::Bool=true)
210+
open(filename) do f
211+
readlines(f, chomp=chomp)
212+
end
213+
end
214+
readlines(s::IO=STDIN; chomp::Bool=true) = collect(eachline(s, chomp=chomp))
191215

192216
## byte-order mark, ntoh & hton ##
193217

@@ -454,9 +478,6 @@ function readuntil(s::IO, t::AbstractString)
454478
return String(take!(out))
455479
end
456480

457-
readline() = readline(STDIN)
458-
readline(s::IO) = readuntil(s, '\n')
459-
460481
"""
461482
readchomp(x)
462483
@@ -520,35 +541,39 @@ readstring(filename::AbstractString) = open(readstring, filename)
520541
type EachLine
521542
stream::IO
522543
ondone::Function
523-
EachLine(stream) = EachLine(stream, ()->nothing)
524-
EachLine(stream, ondone) = new(stream, ondone)
544+
chomp::Bool
545+
546+
EachLine(stream::IO=STDIN; ondone::Function=()->nothing, chomp::Bool=true) =
547+
new(stream, ondone, chomp)
525548
end
526549

527550
"""
528-
eachline(stream::IO)
529-
eachline(filename::AbstractString)
551+
eachline(stream::IO=STDIN; chomp::Bool=true)
552+
eachline(filename::AbstractString; chomp::Bool=true)
530553
531-
Create an iterable object that will yield each line from an I/O stream or a file.
532-
The text is assumed to be encoded in UTF-8.
554+
Create an iterable `EachLine` object that will yield each line from an I/O stream
555+
or a file. Iteration calls `readline` on the stream argument repeatedly with
556+
`chomp` passed through, determining whether trailing end-of-line characters are
557+
removed. When called with a file name, the file is opened once at the beginning of
558+
iteration and closed at the end. If iteration is interrupted, the file will be
559+
closed when the `EachLine` object is garbage collected.
533560
"""
534-
eachline(stream::IO) = EachLine(stream)
535-
function eachline(filename::AbstractString)
561+
eachline(stream::IO=STDIN; chomp::Bool=true) = EachLine(stream, chomp=chomp)
562+
563+
function eachline(filename::AbstractString; chomp::Bool=true)
536564
s = open(filename)
537-
EachLine(s, ()->close(s))
565+
EachLine(s, ondone=()->close(s), chomp=chomp)
538566
end
539567

540568
start(itr::EachLine) = nothing
541-
function done(itr::EachLine, nada)
542-
if !eof(itr.stream)
543-
return false
544-
end
569+
function done(itr::EachLine, ::Void)
570+
eof(itr.stream) || return false
545571
itr.ondone()
546572
true
547573
end
548-
next(itr::EachLine, nada) = (readline(itr.stream), nothing)
549-
eltype(::Type{EachLine}) = String
574+
next(itr::EachLine, ::Void) = (readline(itr.stream, chomp=itr.chomp), nothing)
550575

551-
readlines(s=STDIN) = collect(eachline(s))
576+
eltype(::Type{EachLine}) = String
552577

553578
iteratorsize(::Type{EachLine}) = SizeUnknown()
554579

base/iostream.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -222,16 +222,16 @@ take!(s::IOStream) =
222222
ccall(:jl_take_buffer, Vector{UInt8}, (Ptr{Void},), s.ios)
223223

224224
function readuntil(s::IOStream, delim::UInt8)
225-
ccall(:jl_readuntil, Array{UInt8,1}, (Ptr{Void}, UInt8, UInt8), s.ios, delim, 0)
225+
ccall(:jl_readuntil, Array{UInt8,1}, (Ptr{Void}, UInt8, UInt8, UInt8), s.ios, delim, 0, 0)
226226
end
227227

228228
# like readuntil, above, but returns a String without requiring a copy
229229
function readuntil_string(s::IOStream, delim::UInt8)
230-
ccall(:jl_readuntil, Ref{String}, (Ptr{Void}, UInt8, UInt8), s.ios, delim, 1)
230+
ccall(:jl_readuntil, Ref{String}, (Ptr{Void}, UInt8, UInt8, UInt8), s.ios, delim, 1, false)
231231
end
232232

233-
function readline(s::IOStream)
234-
ccall(:jl_readuntil, Ref{String}, (Ptr{Void}, UInt8, UInt8), s.ios, '\n', 1)
233+
function readline(s::IOStream; chomp::Bool=true)
234+
ccall(:jl_readuntil, Ref{String}, (Ptr{Void}, UInt8, UInt8, UInt8), s.ios, '\n', 1, chomp)
235235
end
236236

237237
function readbytes_all!(s::IOStream, b::Array{UInt8}, nb)

base/libgit2/callbacks.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ function authenticate_ssh(creds::SSHCredentials, libgit2credptr::Ptr{Ptr{Void}},
9999
else
100100
# In encrypted private keys, the second line is "Proc-Type: 4,ENCRYPTED"
101101
open(privatekey) do f
102-
passphrase_required = (readline(f); chomp(readline(f)) == "Proc-Type: 4,ENCRYPTED")
102+
readline(f)
103+
passphrase_required = readline(f) == "Proc-Type: 4,ENCRYPTED"
103104
end
104105
end
105106

base/libgit2/utils.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function prompt(msg::AbstractString; default::AbstractString="", password::Bool=
3131
Base.getpass(msg)
3232
else
3333
print(msg)
34-
chomp(readline(STDIN))
34+
readline()
3535
end
3636
isempty(uinput) ? default : uinput
3737
end

base/markdown/Common/block.jl

+10-10
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function hashheader(stream::IO, md::MD)
6161
return false
6262

6363
if c != '\n' # Empty header
64-
h = readline(stream) |> strip
64+
h = strip(readline(stream))
6565
h = match(r"(.*?)( +#+)?$", h).captures[1]
6666
buffer = IOBuffer()
6767
print(buffer, h)
@@ -76,11 +76,11 @@ end
7676
function setextheader(stream::IO, md::MD)
7777
withstream(stream) do
7878
eatindent(stream) || return false
79-
header = readline(stream) |> strip
79+
header = strip(readline(stream))
8080
header == "" && return false
8181

8282
eatindent(stream) || return false
83-
underline = readline(stream) |> strip
83+
underline = strip(readline(stream))
8484
length(underline) < 3 && return false
8585
u = underline[1]
8686
u in "-=" || return false
@@ -108,7 +108,7 @@ function indentcode(stream::IO, block::MD)
108108
buffer = IOBuffer()
109109
while !eof(stream)
110110
if startswith(stream, " ") || startswith(stream, "\t")
111-
write(buffer, readline(stream))
111+
write(buffer, readline(stream, chomp=false))
112112
elseif blankline(stream)
113113
write(buffer, '\n')
114114
else
@@ -139,10 +139,10 @@ function footnote(stream::IO, block::MD)
139139
else
140140
ref = match(regex, str).captures[1]
141141
buffer = IOBuffer()
142-
write(buffer, readline(stream))
142+
write(buffer, readline(stream, chomp=false))
143143
while !eof(stream)
144144
if startswith(stream, " ")
145-
write(buffer, readline(stream))
145+
write(buffer, readline(stream, chomp=false))
146146
elseif blankline(stream)
147147
write(buffer, '\n')
148148
else
@@ -174,7 +174,7 @@ function blockquote(stream::IO, block::MD)
174174
empty = true
175175
while eatindent(stream) && startswith(stream, '>')
176176
startswith(stream, " ")
177-
write(buffer, readline(stream))
177+
write(buffer, readline(stream, chomp=false))
178178
empty = false
179179
end
180180
empty && return false
@@ -229,7 +229,7 @@ function admonition(stream::IO, block::MD)
229229
buffer = IOBuffer()
230230
while !eof(stream)
231231
if startswith(stream, " ")
232-
write(buffer, readline(stream))
232+
write(buffer, readline(stream, chomp=false))
233233
elseif blankline(stream)
234234
write(buffer, '\n')
235235
else
@@ -305,7 +305,7 @@ function list(stream::IO, block::MD)
305305
newline = false
306306
if startswith(stream, " "^indent)
307307
# Indented text that is part of the current list item.
308-
print(buffer, readline(stream))
308+
print(buffer, readline(stream, chomp=false))
309309
else
310310
matched = startswith(stream, regex)
311311
if isempty(matched)
@@ -316,7 +316,7 @@ function list(stream::IO, block::MD)
316316
# Start of a new list item.
317317
count += 1
318318
count > 1 && pushitem!(list, buffer)
319-
print(buffer, readline(stream))
319+
print(buffer, readline(stream, chomp=false))
320320
end
321321
end
322322
end

base/markdown/GitHub/GitHub.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function fencedcode(stream::IO, block::MD)
3030
seek(stream, line_start)
3131
end
3232
end
33-
write(buffer, readline(stream))
33+
write(buffer, readline(stream, chomp=false))
3434
end
3535
return false
3636
end

base/markdown/GitHub/table.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ end
77

88
function parserow(stream::IO)
99
withstream(stream) do
10-
line = readline(stream) |> chomp
10+
line = readline(stream)
1111
row = split(line, r"(?<!\\)\|")
1212
length(row) == 1 && return
1313
row[1] == "" && shift!(row)

base/markdown/parse/util.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ function linecontains(io::IO, chars; allow_whitespace = true,
4646
eat = true,
4747
allowempty = false)
4848
start = position(io)
49-
l = readline(io) |> chomp
49+
l = readline(io)
5050
length(l) == 0 && return allowempty
5151

5252
result = allowempty
@@ -99,7 +99,7 @@ function startswith(stream::IO, r::Regex; eat = true, padding = false)
9999
@assert Base.startswith(r.pattern, "^")
100100
start = position(stream)
101101
padding && skipwhitespace(stream)
102-
line = chomp(readline(stream))
102+
line = readline(stream)
103103
seek(stream, start)
104104
m = match(r, line)
105105
m === nothing && return ""

base/multi.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -1628,9 +1628,9 @@ function redirect_worker_output(ident, stream)
16281628
if startswith(line, "\tFrom worker ")
16291629
# STDOUT's of "additional" workers started from an initial worker on a host are not available
16301630
# on the master directly - they are routed via the initial worker's STDOUT.
1631-
print(line)
1631+
println(line)
16321632
else
1633-
print("\tFrom worker $(ident):\t$line")
1633+
println("\tFrom worker $(ident):\t$line")
16341634
end
16351635
end
16361636
end

base/pkg/dir.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ end
6767

6868
function getmetabranch()
6969
try
70-
chomp(readline(joinpath(path(),"META_BRANCH")))
70+
readline(joinpath(path(),"META_BRANCH"))
7171
catch err
7272
META_BRANCH
7373
end

base/pkg/entry.jl

+1-2
Original file line numberDiff line numberDiff line change
@@ -608,8 +608,7 @@ function build!(pkgs::Vector, errs::Dict, seen::Set=Set())
608608
empty!(Base.DL_LOAD_PATH)
609609
append!(Base.DL_LOAD_PATH, $(repr(Base.DL_LOAD_PATH)))
610610
open("$(escape_string(errfile))", "a") do f
611-
for path_ in eachline(STDIN)
612-
path = chomp(path_)
611+
for path in eachline(STDIN)
613612
pkg = basename(dirname(dirname(path)))
614613
try
615614
info("Building \$pkg")

base/pkg/reqs.jl

-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ end
6666
function read(readable::Union{IO,Base.AbstractCmd})
6767
lines = Line[]
6868
for line in eachline(readable)
69-
line = chomp(line)
7069
push!(lines, ismatch(r"^\s*(?:#|$)", line) ? Comment(line) : Requirement(line))
7170
end
7271
return lines

0 commit comments

Comments
 (0)