diff --git a/std/stdio.d b/std/stdio.d index 5d888d11826..af3d4b8356f 100644 --- a/std/stdio.d +++ b/std/stdio.d @@ -1658,7 +1658,8 @@ may throw $(D StdioException) on I/O error. Note: Each $(D front) will not persist after $(D popFront) is called, so the caller must copy its contents (e.g. by -calling $(D to!string)) if retention is needed. +calling $(D to!string)) if retention is needed. Alternatively use +$(LREF byLineCopy) instead. Params: Char = Character type for each line, defaulting to $(D char). @@ -1720,6 +1721,118 @@ the contents may well have changed). return ByLine!(Char, Terminator)(this, keepTerminator, terminator); } + private struct ByLineCopy(Char, Terminator) + { + private: + import std.typecons; + + /* Ref-counting stops the source range's ByLineCopyImpl + * from getting out of sync after the range is copied, e.g. + * when accessing range.front, then using std.range.take, + * then accessing range.front again. */ + alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator), + RefCountedAutoInitialize.no); + Impl impl; + + public: + this(File f, KeepTerminator kt, Terminator terminator) + { + impl = Impl(f, kt, terminator); + } + + @property bool empty() + { + return impl.refCountedPayload.empty; + } + + @property immutable(Char)[] front() + { + return impl.refCountedPayload.front; + } + + void popFront() + { + impl.refCountedPayload.popFront(); + } + } + + private struct ByLineCopyImpl(Char, Terminator) + { + ByLineImpl!(Char, Terminator) impl; + bool gotFront; + immutable(Char)[] line; + + public: + this(File f, KeepTerminator kt, Terminator terminator) + { + impl = ByLineImpl!(Char, Terminator)(f, kt, terminator); + } + + @property bool empty() + { + return impl.empty; + } + + @property front() + { + if (!gotFront) + line = impl.front.idup; + return line; + } + + void popFront() + { + impl.popFront(); + gotFront = false; + } + } + +/** +Returns an input range set up to read from the file handle one line +at a time. Each line will be newly allocated. + +The element type for the range will be $(D immutable(Char)[]). Range +primitives may throw $(D StdioException) on I/O error. + +Params: +Char = Character type for each line, defaulting to $(D char). +keepTerminator = Use $(D KeepTerminator.yes) to include the +terminator at the end of each line. +terminator = Line separator ($(D '\n') by default). + +Example: +---- +import std.algorithm, std.stdio; +// Print sorted lines of a file. +void main() +{ + auto sortedLines = File("file.txt") // Open for reading + .byLineCopy() // Read persistent lines + .array() // into an array + .sort(); // then sort them + foreach (line; sortedLines) + writeln(line); +} +---- +See_Also: +$(XREF file,readText) +*/ + auto byLineCopy(Terminator = char, Char = char) + (KeepTerminator keepTerminator = KeepTerminator.no, + Terminator terminator = '\n') + if (isScalarType!Terminator) + { + return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator); + } + +/// ditto + auto byLineCopy(Terminator, Char = char) + (KeepTerminator keepTerminator, Terminator terminator) + if (is(Unqual!(ElementEncodingType!Terminator) == Char)) + { + return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator); + } + unittest { static import std.file; @@ -1740,7 +1853,7 @@ the contents may well have changed). f.detach(); assert(!f.isOpen); - void test(Terminator)(string txt, string[] witness, + void test(Terminator)(string txt, in string[] witness, KeepTerminator kt, Terminator term, bool popFirstLine = false) { import std.conv : text; @@ -1767,8 +1880,11 @@ the contents may well have changed). assert(i == witness.length, text(i, " != ", witness.length)); // Issue 11830 - auto walkedLength = File(deleteme).byLine.walkLength; + auto walkedLength = File(deleteme).byLine(kt, term).walkLength; assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length)); + + // test persistent lines + assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort()); } KeepTerminator kt = KeepTerminator.no; @@ -1832,6 +1948,12 @@ the contents may well have changed). file.detach(); assert(!file.isOpen); } + + unittest + { + static assert(is(typeof(File("").byLine.front) == char[])); + static assert(is(typeof(File("").byLineCopy.front) == string)); + } template byRecord(Fields...) {