Skip to content

Commit 4a91ed7

Browse files
pbackusdlang-bot
authored andcommitted
stdio: add readfln and File.readfln
Compared to readf, these functions provide a less error-prone way to read a single line of formatted input. Fixes #10370
1 parent 0d724aa commit 4a91ed7

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed

changelog/readfln.dd

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Added `readfln` and `File.readfln` to `std.stdio`
2+
3+
These functions read a single line of input and parse it using a format string.
4+
Unlike `readf`, they will not accidentally read multiple lines if the user
5+
forgets to include a line terminator in the format string—a common mistake for
6+
beginners.

std/stdio.d

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ $(TR $(TD Reading) $(TD
1818
$(MYREF chunks)
1919
$(MYREF lines)
2020
$(MYREF readf)
21+
$(MYREF readfln)
2122
$(MYREF readln)
2223
))
2324
$(TR $(TD Writing) $(TD
@@ -2094,6 +2095,85 @@ $(CONSOLE
20942095
"Unexpected '\\n' when converting from type LockingTextReader to type int");
20952096
}
20962097

2098+
/**
2099+
Reads a line from the file and parses it using $(REF formattedRead, std,format,read).
2100+
2101+
Params:
2102+
format = The $(MREF_ALTTEXT format string, std,format). When passed as a
2103+
compile-time argument, the string will be statically checked against the
2104+
argument types passed.
2105+
data = Items to be read.
2106+
2107+
Returns: Same as `formattedRead`: the number of variables filled. If the
2108+
input ends early, this number will be less that the number of variables
2109+
provided.
2110+
2111+
Example:
2112+
---
2113+
// sum_rows.d
2114+
void main()
2115+
{
2116+
import std.stdio;
2117+
auto f = File("input");
2118+
int a, b, c;
2119+
while (f.readfln("%d %d %d", a, b, c) == 3)
2120+
{
2121+
writeln(a + b + c);
2122+
}
2123+
}
2124+
---
2125+
$(CONSOLE
2126+
% cat << EOF > input
2127+
1 2 3
2128+
4 5 6
2129+
7 8 9
2130+
EOF
2131+
% rdmd sum_rows.d
2132+
6
2133+
15
2134+
24
2135+
)
2136+
*/
2137+
uint readfln(alias format, Data...)(auto ref Data data)
2138+
if (isSomeString!(typeof(format)))
2139+
{
2140+
import std.format : checkFormatException;
2141+
2142+
alias e = checkFormatException!(format, Data);
2143+
static assert(!e, e);
2144+
return this.readfln(format, data);
2145+
}
2146+
2147+
/// ditto
2148+
uint readfln(Data...)(scope const(char)[] format, auto ref Data data)
2149+
{
2150+
import std.format.read : formattedRead;
2151+
import std.string : stripRight;
2152+
2153+
string line = this.readln.stripRight("\r\n");
2154+
return formattedRead(line, format, data);
2155+
}
2156+
2157+
@system unittest
2158+
{
2159+
static import std.file;
2160+
2161+
auto deleteme = testFilename();
2162+
std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2163+
scope(exit) std.file.remove(deleteme);
2164+
string s;
2165+
auto f = File(deleteme);
2166+
f.readfln!"%s"(s);
2167+
assert(s == "hello", "["~s~"]");
2168+
f.readfln("%s", s);
2169+
assert(s == "world", "["~s~"]");
2170+
2171+
bool b1, b2;
2172+
f.readfln("%s", b1);
2173+
f.readfln("%s", b2);
2174+
assert(b1 == true && b2 == false);
2175+
}
2176+
20972177
/**
20982178
Returns a temporary file by calling $(CSTDIO tmpfile).
20992179
Note that the created file has no $(LREF name).*/
@@ -4489,6 +4569,70 @@ if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
44894569
}
44904570
}
44914571

4572+
/**
4573+
Reads a line from `stdin` and parses it using $(REF formattedRead, std,format,read).
4574+
4575+
Params:
4576+
format = The $(MREF_ALTTEXT format string, std,format). When passed as a
4577+
compile-time argument, the string will be statically checked against the
4578+
argument types passed.
4579+
data = Items to be read.
4580+
4581+
Returns: Same as `formattedRead`: the number of variables filled. If the
4582+
input ends early, this number will be less that the number of variables
4583+
provided.
4584+
4585+
Example:
4586+
---
4587+
// sum_rows.d
4588+
void main()
4589+
{
4590+
import std.stdio;
4591+
int a, b, c;
4592+
while (readfln("%d %d %d", a, b, c) == 3)
4593+
{
4594+
writeln(a + b + c);
4595+
}
4596+
}
4597+
---
4598+
$(CONSOLE
4599+
% cat << EOF > input
4600+
1 2 3
4601+
4 5 6
4602+
7 8 9
4603+
EOF
4604+
% rdmd sum_rows.d < input
4605+
6
4606+
15
4607+
24
4608+
)
4609+
*/
4610+
uint readfln(alias format, Data...)(auto ref Data data)
4611+
{
4612+
import std.format : checkFormatException;
4613+
4614+
alias e = checkFormatException!(format, Data);
4615+
static assert(!e, e);
4616+
return .readfln(format, data);
4617+
}
4618+
4619+
/// ditto
4620+
uint readfln(Data...)(scope const(char)[] format, auto ref Data data)
4621+
{
4622+
return stdin.readfln(format, data);
4623+
}
4624+
4625+
@system unittest
4626+
{
4627+
float f;
4628+
string s;
4629+
char c;
4630+
int n;
4631+
if (false) readfln("%f %s %c %d", f, s, c, n);
4632+
if (false) readfln!"%f %s %c %d"(f, s, c, n);
4633+
4634+
}
4635+
44924636
/*
44934637
* Convenience function that forwards to `core.sys.posix.stdio.fopen`
44944638
* (to `_wfopen` on Windows)

0 commit comments

Comments
 (0)