diff --git a/changelog/readfln.dd b/changelog/readfln.dd new file mode 100644 index 000000000..c1f8afdfe --- /dev/null +++ b/changelog/readfln.dd @@ -0,0 +1,6 @@ +Added `readfln` and `File.readfln` to `std.stdio` + +These functions read a single line of input and parse it using a format string. +Unlike `readf`, they will not accidentally read multiple lines if the user +forgets to include a line terminator in the format string—a common mistake for +beginners. diff --git a/std/stdio.d b/std/stdio.d index 4734c1b71..82a13929f 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -18,6 +18,7 @@ $(TR $(TD Reading) $(TD $(MYREF chunks) $(MYREF lines) $(MYREF readf) + $(MYREF readfln) $(MYREF readln) )) $(TR $(TD Writing) $(TD @@ -2094,6 +2095,85 @@ $(CONSOLE "Unexpected '\\n' when converting from type LockingTextReader to type int"); } + /** + Reads a line from the file and parses it using $(REF formattedRead, std,format,read). + + Params: + format = The $(MREF_ALTTEXT format string, std,format). When passed as a + compile-time argument, the string will be statically checked against the + argument types passed. + data = Items to be read. + + Returns: Same as `formattedRead`: the number of variables filled. If the + input ends early, this number will be less that the number of variables + provided. + + Example: + --- + // sum_rows.d + void main() + { + import std.stdio; + auto f = File("input"); + int a, b, c; + while (f.readfln("%d %d %d", a, b, c) == 3) + { + writeln(a + b + c); + } + } + --- + $(CONSOLE +% cat << EOF > input +1 2 3 +4 5 6 +7 8 9 +EOF +% rdmd sum_rows.d +6 +15 +24 + ) + */ + uint readfln(alias format, Data...)(auto ref Data data) + if (isSomeString!(typeof(format))) + { + import std.format : checkFormatException; + + alias e = checkFormatException!(format, Data); + static assert(!e, e); + return this.readfln(format, data); + } + + /// ditto + uint readfln(Data...)(scope const(char)[] format, auto ref Data data) + { + import std.format.read : formattedRead; + import std.string : stripRight; + + string line = this.readln.stripRight("\r\n"); + return formattedRead(line, format, data); + } + + @system unittest + { + static import std.file; + + auto deleteme = testFilename(); + std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n"); + scope(exit) std.file.remove(deleteme); + string s; + auto f = File(deleteme); + f.readfln!"%s"(s); + assert(s == "hello", "["~s~"]"); + f.readfln("%s", s); + assert(s == "world", "["~s~"]"); + + bool b1, b2; + f.readfln("%s", b1); + f.readfln("%s", b2); + assert(b1 == true && b2 == false); + } + /** Returns a temporary file by calling $(CSTDIO tmpfile). Note that the created file has no $(LREF name).*/ @@ -4489,6 +4569,70 @@ if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) && } } +/** +Reads a line from `stdin` and parses it using $(REF formattedRead, std,format,read). + +Params: + format = The $(MREF_ALTTEXT format string, std,format). When passed as a + compile-time argument, the string will be statically checked against the + argument types passed. + data = Items to be read. + +Returns: Same as `formattedRead`: the number of variables filled. If the +input ends early, this number will be less that the number of variables +provided. + +Example: +--- +// sum_rows.d +void main() +{ + import std.stdio; + int a, b, c; + while (readfln("%d %d %d", a, b, c) == 3) + { + writeln(a + b + c); + } +} +--- +$(CONSOLE +% cat << EOF > input +1 2 3 +4 5 6 +7 8 9 +EOF +% rdmd sum_rows.d < input +6 +15 +24 +) +*/ +uint readfln(alias format, Data...)(auto ref Data data) +{ + import std.format : checkFormatException; + + alias e = checkFormatException!(format, Data); + static assert(!e, e); + return .readfln(format, data); +} + +/// ditto +uint readfln(Data...)(scope const(char)[] format, auto ref Data data) +{ + return stdin.readfln(format, data); +} + +@system unittest +{ + float f; + string s; + char c; + int n; + if (false) readfln("%f %s %c %d", f, s, c, n); + if (false) readfln!"%f %s %c %d"(f, s, c, n); + +} + /* * Convenience function that forwards to `core.sys.posix.stdio.fopen` * (to `_wfopen` on Windows)