1 // Written in the D programming language.
4 $(SCRIPT inhibitQuickIndex = 1;)
7 $(TR $(TH Category) $(TH Symbols))
8 $(TR $(TD File handles) $(TD
17 $(TR $(TD Reading) $(TD
24 $(TR $(TD Writing) $(TD
32 $(MYREF KeepTerminator)
34 $(MYREF StdioException)
38 Standard I/O functions that extend $(LINK2 https://dlang.org/phobos/core_stdc_stdio.html, core.stdc.stdio). $(B core.stdc.stdio)
39 is $(D_PARAM public)ally imported when importing $(B std.stdio).
41 There are three layers of I/O:
43 $(LI The lowest layer is the operating system layer. The two main schemes are Windows and Posix.)
44 $(LI C's $(TT stdio.h) which unifies the two operating system schemes.)
45 $(LI $(TT std.stdio), this module, unifies the various $(TT stdio.h) implementations into
46 a high level package for D programs.)
49 Source: $(PHOBOSSRC std/stdio.d)
50 Copyright: Copyright The D Language Foundation 2007-.
51 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
52 Authors: $(HTTP digitalmars.com, Walter Bright),
53 $(HTTP erdani.org, Andrei Alexandrescu),
56 CSTDIO=$(HTTP cplusplus.com/reference/cstdio/$1/, $1)
63 The three layers have many terms for their data structures and types.
64 Here we try to bring some sanity to them for the intrepid code spelunker.
70 A Windows handle is an opaque object of type HANDLE.
71 The `HANDLE` for standard devices can be retrieved with
72 Windows `GetStdHandle()`.
76 file descriptor, aka fileno, aka fildes
78 An int from 0..`FOPEN_MAX`, which is an index into some internal data
80 0 is for `stdin`, 1 for `stdout`, 2 for `stderr`.
81 Negative values usually indicate an error.
87 A struct that encapsulates the C library's view of the operating system
88 files. A `FILE` should only be referred to via a pointer.
92 A field of `FILE` which is the Posix file descriptor for Posix systems, and
93 and an index into an array of file `HANDLE`s for Windows.
94 This array is how Posix behavior is emulated on Windows.
95 For Digital Mars C, that array is `__osfhnd[]`, and is initialized
96 at program start by the C runtime library.
97 In this module, they are typed as `fileno_t`.
99 `stdin`, `stdout`, `stderr`
101 Global pointers to `FILE` representing standard input, output, and error streams.
102 Being global means there are synchronization issues when multiple threads
103 are doing I/O on the same streams.
109 import core.stdc.stddef : wchar_t;
110 public import core.stdc.stdio;
111 import std.algorithm.mutation : copy;
112 import std.meta : allSatisfy;
113 import std.range : ElementEncodingType, empty, front, isBidirectionalRange,
114 isInputRange, isSomeFiniteCharInputRange, put;
115 import std.traits : isSomeChar, isSomeString, Unqual;
116 import std.typecons : Flag, No, Yes;
119 If flag `KeepTerminator` is set to `KeepTerminator.yes`, then the delimiter
120 is included in the strings returned.
122 alias KeepTerminator = Flag!"keepTerminator";
124 version (CRuntime_Microsoft)
127 else version (CRuntime_Glibc)
130 else version (CRuntime_Bionic)
132 version = GENERIC_IO;
134 else version (CRuntime_Musl)
136 version = GENERIC_IO;
138 else version (CRuntime_UClibc)
140 version = GENERIC_IO;
144 version = GENERIC_IO;
149 version = GENERIC_IO;
154 version = GENERIC_IO;
157 else version (WatchOS)
159 version = GENERIC_IO;
162 else version (FreeBSD)
164 version = GENERIC_IO;
166 else version (NetBSD)
168 version = GENERIC_IO;
170 else version (OpenBSD)
172 version = GENERIC_IO;
174 else version (DragonFlyBSD)
176 version = GENERIC_IO;
178 else version (Solaris)
180 version = GENERIC_IO;
184 static assert(0, "unsupported operating system");
187 // Character type used for operating system filesystem APIs
190 private alias FSChar = wchar;
194 private alias FSChar = char;
197 private alias fileno_t = int; // file descriptor, fildes, fileno
201 // core.stdc.stdio.fopen expects file names to be
202 // encoded in CP_ACP on Windows instead of UTF-8.
203 /+ Waiting for druntime pull 299
205 extern (C) nothrow @nogc FILE* _wfopen(scope const wchar* filename, scope const wchar* mode);
206 extern (C) nothrow @nogc FILE* _wfreopen(scope const wchar* filename, scope const wchar* mode, FILE* fp);
208 import core.sys.windows.basetsd : HANDLE;
213 static import core.sys.posix.stdio; // getdelim, flockfile
216 version (CRuntime_Microsoft)
218 private alias _FPUTC = _fputc_nolock;
219 private alias _FPUTWC = _fputwc_nolock;
220 private alias _FGETC = _fgetc_nolock;
221 private alias _FGETWC = _fgetwc_nolock;
222 private alias _FLOCK = _lock_file;
223 private alias _FUNLOCK = _unlock_file;
225 else version (CRuntime_Glibc)
227 private alias _FPUTC = fputc_unlocked;
228 private alias _FPUTWC = fputwc_unlocked;
229 private alias _FGETC = fgetc_unlocked;
230 private alias _FGETWC = fgetwc_unlocked;
231 private alias _FLOCK = core.sys.posix.stdio.flockfile;
232 private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
234 else version (GENERIC_IO)
241 static import core.stdc.wchar_;
243 pragma(mangle, fputc.mangleof) int _FPUTC(int c, _iobuf* fp);
244 pragma(mangle, core.stdc.wchar_.fputwc.mangleof) int _FPUTWC(wchar_t c, _iobuf* fp);
245 pragma(mangle, fgetc.mangleof) int _FGETC(_iobuf* fp);
246 pragma(mangle, core.stdc.wchar_.fgetwc.mangleof) int _FGETWC(_iobuf* fp);
251 private alias _FLOCK = core.sys.posix.stdio.flockfile;
252 private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
256 static assert(0, "don't know how to lock files on GENERIC_IO");
261 static assert(0, "unsupported C I/O system");
264 private extern (C) @nogc nothrow
266 pragma(mangle, _FPUTC.mangleof) int trustedFPUTC(int ch, _iobuf* h) @trusted;
267 pragma(mangle, _FPUTWC.mangleof) int trustedFPUTWC(wchar_t ch, _iobuf* h) @trusted;
270 //------------------------------------------------------------------------------
271 private struct ByRecordImpl(Fields...)
274 import std.typecons : Tuple;
278 Tuple!(Fields) current;
282 this(File f, string format)
286 this.format = format;
287 popFront(); // prime the range
290 /// Range primitive implementations.
291 @property bool empty()
297 @property ref Tuple!(Fields) front()
305 import std.conv : text;
306 import std.exception : enforce;
307 import std.format.read : formattedRead;
308 import std.string : chomp;
310 enforce(file.isOpen, "ByRecord: File must be open");
319 formattedRead(line, format, ¤t);
320 enforce(line.empty, text("Leftover characters in record: `",
326 template byRecord(Fields...)
328 auto byRecord(File f, string format)
330 return typeof(return)(f, format);
335 Encapsulates a `FILE*`. Generally D does not attempt to provide
336 thin wrappers over equivalent functions in the C standard library, but
337 manipulating `FILE*` values directly is unsafe and error-prone in
338 many ways. The `File` type ensures safe manipulation, automatic
339 file closing, and a lot of convenience.
341 The underlying `FILE*` handle is maintained in a reference-counted
342 manner, such that as soon as the last `File` variable bound to a
343 given `FILE*` goes out of scope, the underlying `FILE*` is
344 automatically closed.
351 void main(string[] args)
353 auto f = File("test.txt", "w"); // open for writing
357 auto g = f; // now g and f write to the same file
358 // internal reference count is 2
359 g.write(", ", args[1]);
360 // g exits scope, reference count decreases to 1
363 // f exits scope, reference count falls to zero,
364 // underlying `FILE*` is closed.
376 import core.atomic : atomicOp, atomicStore, atomicLoad;
377 import std.range.primitives : ElementEncodingType;
378 import std.traits : isScalarType, isArray;
379 enum Orientation { unknown, narrow, wide }
383 FILE * handle = null; // Is null iff this Impl is closed by another File
384 shared uint refs = uint.max / 2;
385 bool isPopened; // true iff the stream has been created by popen()
386 Orientation orientation;
389 private string _name;
391 package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted @nogc nothrow
393 import core.stdc.stdlib : malloc;
396 _p = cast(Impl*) malloc(Impl.sizeof);
399 import core.exception : onOutOfMemoryError;
400 onOutOfMemoryError();
402 initImpl(handle, name, refs, isPopened);
405 private void initImpl(FILE* handle, string name, uint refs = 1, bool isPopened = false) @nogc nothrow pure @safe
409 atomicStore(_p.refs, refs);
410 _p.isPopened = isPopened;
411 _p.orientation = Orientation.unknown;
416 Constructor taking the name of the file to open and the open mode.
418 Copying one `File` object to another results in the two `File`
419 objects referring to the same underlying file.
421 The destructor automatically closes the file as soon as no `File`
422 object refers to it anymore.
425 name = range or string representing the file _name
426 stdioOpenmode = range or string represting the open mode
427 (with the same semantics as in the C standard library
428 $(CSTDIO fopen) function)
430 Throws: `ErrnoException` if the file could not be opened.
432 this(string name, scope const(char)[] stdioOpenmode = "rb") @safe
434 import std.conv : text;
435 import std.exception : errnoEnforce;
437 this(errnoEnforce(_fopen(name, stdioOpenmode),
438 text("Cannot open file `", name, "' in mode `",
439 stdioOpenmode, "'")),
442 // MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422)
443 version (CRuntime_Microsoft)
445 setAppendWin(stdioOpenmode);
450 this(R1, R2)(R1 name)
451 if (isSomeFiniteCharInputRange!R1)
453 import std.conv : to;
454 this(name.to!string, "rb");
458 this(R1, R2)(R1 name, R2 mode)
459 if (isSomeFiniteCharInputRange!R1 &&
460 isSomeFiniteCharInputRange!R2)
462 import std.conv : to;
463 this(name.to!string, mode.to!string);
468 static import std.file;
469 import std.utf : byChar;
470 auto deleteme = testFilename();
471 auto f = File(deleteme.byChar, "w".byChar);
473 std.file.remove(deleteme);
481 this(this) @safe pure nothrow @nogc
484 assert(atomicLoad(_p.refs));
485 atomicOp!"+="(_p.refs, 1);
489 Assigns a file to another. The target of the assignment gets detached
490 from whatever file it was attached to, and attaches itself to the new
493 ref File opAssign(File rhs) @safe return
495 import std.algorithm.mutation : swap;
501 // https://issues.dlang.org/show_bug.cgi?id=20129
505 aa.require(0, File.init);
509 Detaches from the current file (throwing on failure), and then attempts to
510 _open file `name` with mode `stdioOpenmode`. The mode has the
511 same semantics as in the C standard library $(CSTDIO fopen) function.
513 Throws: `ErrnoException` in case of error.
515 void open(string name, scope const(char)[] stdioOpenmode = "rb") @trusted
517 resetFile(name, stdioOpenmode, false);
520 // https://issues.dlang.org/show_bug.cgi?id=20585
525 f.open("doesn't exist");
532 f.close(); // to check not crash here
535 private void resetFile(string name, scope const(char)[] stdioOpenmode, bool isPopened) @trusted
537 import core.stdc.stdlib : malloc;
538 import std.exception : enforce;
539 import std.conv : text;
540 import std.exception : errnoEnforce;
552 errnoEnforce(handle = _popen(name, stdioOpenmode),
553 "Cannot run command `"~name~"'");
557 errnoEnforce(handle = _fopen(name, stdioOpenmode),
558 text("Cannot open file `", name, "' in mode `",
559 stdioOpenmode, "'"));
564 assert(isPopened == false);
565 errnoEnforce(handle = _fopen(name, stdioOpenmode),
566 text("Cannot open file `", name, "' in mode `",
567 stdioOpenmode, "'"));
569 _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
570 initImpl(handle, name, 1, isPopened);
571 version (CRuntime_Microsoft)
573 setAppendWin(stdioOpenmode);
577 private void closeHandles() @trusted
580 import std.exception : errnoEnforce;
584 import core.sys.posix.stdio : pclose;
585 import std.format : format;
589 auto res = pclose(_p.handle);
590 errnoEnforce(res != -1,
591 "Could not close pipe `"~_name~"'");
598 auto handle = _p.handle;
600 // fclose disassociates the FILE* even in case of error (https://issues.dlang.org/show_bug.cgi?id=19751)
601 errnoEnforce(.fclose(handle) == 0,
602 "Could not close file `"~_name~"'");
606 version (CRuntime_Microsoft)
608 private void setAppendWin(scope const(char)[] stdioOpenmode) @safe
611 foreach (c; stdioOpenmode)
617 if (append && !update)
623 Reuses the `File` object to either open a different file, or change
624 the file mode. If `name` is `null`, the mode of the currently open
625 file is changed; otherwise, a new file is opened, reusing the C
626 `FILE*`. The function has the same semantics as in the C standard
627 library $(CSTDIO freopen) function.
629 Note: Calling `reopen` with a `null` `name` is not implemented
632 Throws: `ErrnoException` in case of error.
634 void reopen(string name, scope const(char)[] stdioOpenmode = "rb") @trusted
636 import std.conv : text;
637 import std.exception : enforce, errnoEnforce;
638 import std.internal.cstring : tempCString;
640 enforce(isOpen, "Attempting to reopen() an unopened file");
642 auto namez = (name == null ? _name : name).tempCString!FSChar();
643 auto modez = stdioOpenmode.tempCString!FSChar();
645 FILE* fd = _p.handle;
647 fd = _wfreopen(namez, modez, fd);
649 fd = freopen(namez, modez, fd);
651 errnoEnforce(fd, name
652 ? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'")
653 : text("Cannot reopen file in mode `", stdioOpenmode, "'"));
659 @safe unittest // Test changing filename
661 import std.exception : assertThrown, assertNotThrown;
662 static import std.file;
664 auto deleteme = testFilename();
665 std.file.write(deleteme, "foo");
666 scope(exit) std.file.remove(deleteme);
667 auto f = File(deleteme);
668 assert(f.readln() == "foo");
670 auto deleteme2 = testFilename();
671 std.file.write(deleteme2, "bar");
672 scope(exit) std.file.remove(deleteme2);
674 assert(f.name == deleteme2);
675 assert(f.readln() == "bar");
679 version (CRuntime_Microsoft) {} else // Not implemented
680 @safe unittest // Test changing mode
682 import std.exception : assertThrown, assertNotThrown;
683 static import std.file;
685 auto deleteme = testFilename();
686 std.file.write(deleteme, "foo");
687 scope(exit) std.file.remove(deleteme);
688 auto f = File(deleteme, "r+");
689 assert(f.readln() == "foo");
695 assert(f.name == deleteme);
697 assert(std.file.readText(deleteme) == "barbaz");
701 Detaches from the current file (throwing on failure), and then runs a command
702 by calling the C standard library function $(HTTP
703 pubs.opengroup.org/onlinepubs/7908799/xsh/popen.html, popen).
705 Throws: `ErrnoException` in case of error.
707 version (Posix) void popen(string command, scope const(char)[] stdioOpenmode = "r") @safe
709 resetFile(command, stdioOpenmode ,true);
713 First calls `detach` (throwing on failure), then attempts to
714 associate the given file descriptor with the `File`, and sets the file's name to `null`.
716 The mode must be compatible with the mode of the file descriptor.
718 Throws: `ErrnoException` in case of error.
720 fd = File descriptor to associate with this `File`.
721 stdioOpenmode = Mode to associate with this File. The mode has the same
722 semantics as in the POSIX library function $(HTTP
723 pubs.opengroup.org/onlinepubs/7908799/xsh/fdopen.html, fdopen)
724 and must be compatible with `fd`.
726 void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe
728 fdopen(fd, stdioOpenmode, null);
731 package void fdopen(int fd, scope const(char)[] stdioOpenmode, string name) @trusted
733 import std.exception : errnoEnforce;
734 import std.internal.cstring : tempCString;
736 auto modez = stdioOpenmode.tempCString();
739 version (CRuntime_Microsoft)
741 auto fp = _fdopen(fd, modez);
746 import core.sys.posix.stdio : fdopen;
747 auto fp = fdopen(fd, modez);
751 static assert(0, "no fdopen() available");
753 this = File(fp, name);
756 // Declare a dummy HANDLE to allow generating documentation
757 // for Windows-only methods.
758 version (StdDdoc) { version (Windows) {} else alias HANDLE = int; }
761 First calls `detach` (throwing on failure), and then attempts to
762 associate the given Windows `HANDLE` with the `File`. The mode must
763 be compatible with the access attributes of the handle. Windows only.
765 Throws: `ErrnoException` in case of error.
768 void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode);
771 void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode)
773 import core.stdc.stdint : intptr_t;
774 import std.exception : errnoEnforce;
775 import std.format : format;
777 // Create file descriptors from the handles
780 foreach (c; stdioOpenmode)
783 case 'r': mode |= _O_RDONLY; break;
784 case '+': mode &=~_O_RDONLY; break;
785 case 'a': mode |= _O_APPEND; break;
786 case 'b': mode |= _O_BINARY; break;
787 case 't': mode |= _O_TEXT; break;
788 case ',': break modeLoop;
792 auto fd = _open_osfhandle(cast(intptr_t) handle, mode);
794 errnoEnforce(fd >= 0, "Cannot open Windows HANDLE");
795 fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle));
799 /** Returns `true` if the file is opened. */
800 @property bool isOpen() const @safe pure nothrow
802 return _p !is null && _p.handle;
806 Returns `true` if the file is at end (see $(CSTDIO feof)).
808 Throws: `Exception` if the file is not opened.
810 @property bool eof() const @trusted pure
812 import std.exception : enforce;
814 enforce(_p && _p.handle, "Calling eof() against an unopened file.");
815 return .feof(cast(FILE*) _p.handle) != 0;
819 Returns the name last used to initialize this `File`, if any.
821 Some functions that create or initialize the `File` set the name field to `null`.
822 Examples include $(LREF tmpfile), $(LREF wrapFile), and $(LREF fdopen). See the
823 documentation of those functions for details.
825 Returns: The name last used to initialize this this file, or `null` otherwise.
827 @property string name() const @safe pure nothrow return
833 If the file is closed or not yet opened, returns `true`. Otherwise, returns
834 $(CSTDIO ferror) for the file handle.
836 @property bool error() const @trusted pure nothrow
838 return !isOpen || .ferror(cast(FILE*) _p.handle);
843 // https://issues.dlang.org/show_bug.cgi?id=12349
844 static import std.file;
845 auto deleteme = testFilename();
846 auto f = File(deleteme, "w");
847 scope(exit) std.file.remove(deleteme);
854 Detaches from the underlying file. If the sole owner, calls `close`.
856 Throws: `ErrnoException` on failure if closing the file.
858 void detach() @trusted
860 import core.stdc.stdlib : free;
863 scope(exit) _p = null;
865 if (atomicOp!"-="(_p.refs, 1) == 0)
867 scope(exit) free(_p);
874 static import std.file;
876 auto deleteme = testFilename();
877 scope(exit) std.file.remove(deleteme);
878 auto f = File(deleteme, "w");
883 assert(f._p.refs == 1);
888 If the file was closed or not yet opened, succeeds vacuously. Otherwise
889 closes the file (by calling $(CSTDIO fclose)),
890 throwing on error. Even if an exception is thrown, afterwards the $(D
891 File) object is empty. This is different from `detach` in that it
892 always closes the file; consequently, all other `File` objects
893 referring to the same handle will see a closed file henceforth.
895 Throws: `ErrnoException` on error.
897 void close() @trusted
899 import core.stdc.stdlib : free;
900 import std.exception : errnoEnforce;
902 if (!_p) return; // succeed vacuously
905 if (atomicOp!"-="(_p.refs, 1) == 0)
907 _p = null; // start a new life
909 if (!_p.handle) return; // Impl is closed by another File
911 scope(exit) _p.handle = null; // nullify the handle anyway
916 If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns
917 $(CSTDIO clearerr) for the file handle.
919 void clearerr() @safe pure nothrow
921 _p is null || _p.handle is null ||
922 .clearerr(_p.handle);
926 Flushes the C `FILE` buffers.
928 Calls $(CSTDIO fflush) for the file handle.
930 Throws: `Exception` if the file is not opened or if the call to `fflush` fails.
932 void flush() @trusted
934 import std.exception : enforce, errnoEnforce;
936 enforce(isOpen, "Attempting to flush() in an unopened file");
937 errnoEnforce(.fflush(_p.handle) == 0);
942 // https://issues.dlang.org/show_bug.cgi?id=12349
943 import std.exception : assertThrown;
944 static import std.file;
946 auto deleteme = testFilename();
947 auto f = File(deleteme, "w");
948 scope(exit) std.file.remove(deleteme);
951 assertThrown(f.flush());
955 Forces any data buffered by the OS to be written to disk.
956 Call $(LREF flush) before calling this function to flush the C `FILE` buffers first.
959 $(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx,
960 `FlushFileBuffers`) on Windows,
961 $(HTTP developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html,
962 `F_FULLFSYNC fcntl`) on Darwin and
963 $(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html,
964 `fsync`) on POSIX for the file handle.
966 Throws: `Exception` if the file is not opened or if the OS call fails.
970 import std.exception : enforce;
972 enforce(isOpen, "Attempting to sync() an unopened file");
976 import core.sys.windows.winbase : FlushFileBuffers;
977 wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed");
979 else version (Darwin)
981 import core.sys.darwin.fcntl : fcntl, F_FULLFSYNC;
982 import std.exception : errnoEnforce;
983 errnoEnforce(fcntl(fileno, F_FULLFSYNC, 0) != -1, "fcntl failed");
987 import core.sys.posix.unistd : fsync;
988 import std.exception : errnoEnforce;
989 errnoEnforce(fsync(fileno) == 0, "fsync failed");
994 Calls $(CSTDIO fread) for the
995 file handle. The number of items to read and the size of
996 each item is inferred from the size and type of the input array, respectively.
998 Returns: The slice of `buffer` containing the data that was actually read.
999 This will be shorter than `buffer` if EOF was reached before the buffer
1000 could be filled. If the buffer is empty, it will be returned.
1002 Throws: `ErrnoException` if the file is not opened or the call to `fread` fails.
1004 `rawRead` always reads in binary mode on Windows.
1006 T[] rawRead(T)(T[] buffer)
1008 import std.exception : enforce, errnoEnforce;
1012 enforce(isOpen, "Attempting to read from an unopened file");
1015 immutable fileno_t fd = .fileno(_p.handle);
1016 immutable mode = ._setmode(fd, _O_BINARY);
1017 scope(exit) ._setmode(fd, mode);
1019 immutable freadResult = trustedFread(_p.handle, buffer);
1020 assert(freadResult <= buffer.length); // fread return guarantee
1021 if (freadResult != buffer.length) // error or eof
1023 errnoEnforce(!error);
1024 return buffer[0 .. freadResult];
1032 static import std.file;
1034 auto testFile = std.file.deleteme();
1035 std.file.write(testFile, "\r\n\n\r\n");
1036 scope(exit) std.file.remove(testFile);
1038 auto f = File(testFile, "r");
1039 auto buf = f.rawRead(new char[5]);
1041 assert(buf == "\r\n\n\r\n");
1044 // https://issues.dlang.org/show_bug.cgi?id=24685
1045 static assert(!__traits(compiles, (File f) @safe { int*[1] bar; f.rawRead(bar[]); }));
1047 // https://issues.dlang.org/show_bug.cgi?id=21729
1050 import std.exception : assertThrown;
1054 assertThrown(f.rawRead(u));
1057 // https://issues.dlang.org/show_bug.cgi?id=21728
1060 static if (__traits(compiles, { import std.process : pipe; })) // not available for iOS
1062 import std.process : pipe;
1063 import std.exception : assertThrown;
1068 assertThrown(p.readEnd.rawRead(u));
1072 // https://issues.dlang.org/show_bug.cgi?id=13893
1075 import std.exception : assertNotThrown;
1079 assertNotThrown(f.rawRead(u));
1083 Calls $(CSTDIO fwrite) for the file
1084 handle. The number of items to write and the size of each
1085 item is inferred from the size and type of the input array, respectively. An
1086 error is thrown if the buffer could not be written in its entirety.
1088 `rawWrite` always writes in binary mode on Windows.
1090 Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fails.
1092 void rawWrite(T)(in T[] buffer)
1094 import std.conv : text;
1095 import std.exception : errnoEnforce;
1099 immutable fileno_t fd = .fileno(_p.handle);
1100 immutable oldMode = ._setmode(fd, _O_BINARY);
1102 if (oldMode != _O_BINARY)
1104 // need to flush the data that was written with the original mode
1105 ._setmode(fd, oldMode);
1106 flush(); // before changing translation mode ._setmode(fd, _O_BINARY);
1107 ._setmode(fd, _O_BINARY);
1112 if (oldMode != _O_BINARY)
1115 ._setmode(fd, oldMode);
1120 auto result = trustedFwrite(_p.handle, buffer);
1121 if (result == result.max) result = 0;
1122 errnoEnforce(result == buffer.length,
1123 text("Wrote ", result, " instead of ", buffer.length,
1124 " objects of type ", T.stringof, " to file `",
1131 static import std.file;
1133 auto testFile = std.file.deleteme();
1134 auto f = File(testFile, "w");
1135 scope(exit) std.file.remove(testFile);
1137 f.rawWrite("\r\n\n\r\n");
1139 assert(std.file.read(testFile) == "\r\n\n\r\n");
1143 Calls $(CSTDIO fseek)
1144 for the file handle to move its position indicator.
1147 offset = Binary files: Number of bytes to offset from origin.$(BR)
1148 Text files: Either zero, or a value returned by $(LREF tell).
1149 origin = Binary files: Position used as reference for the offset, must be
1150 one of $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio),
1151 $(REF_ALTTEXT SEEK_CUR, SEEK_CUR, core,stdc,stdio) or
1152 $(REF_ALTTEXT SEEK_END, SEEK_END, core,stdc,stdio).$(BR)
1153 Text files: Shall necessarily be
1154 $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio).
1156 Throws: `Exception` if the file is not opened.
1157 `ErrnoException` if the call to `fseek` fails.
1159 void seek(long offset, int origin = SEEK_SET) @trusted
1161 import std.conv : to, text;
1162 import std.exception : enforce, errnoEnforce;
1164 // Some libc sanitize the whence input (e.g. glibc), but some don't,
1165 // e.g. Microsoft runtime crashes on an invalid origin,
1166 // and Musl additionally accept SEEK_DATA & SEEK_HOLE (Linux extension).
1167 // To provide a consistent behavior cross platform, we use the glibc check
1168 // See also https://issues.dlang.org/show_bug.cgi?id=19797
1169 enforce(origin == SEEK_SET || origin == SEEK_CUR || origin == SEEK_END,
1170 "Invalid `origin` argument passed to `seek`, must be one of: SEEK_SET, SEEK_CUR, SEEK_END");
1172 enforce(isOpen, "Attempting to seek() in an unopened file");
1175 version (CRuntime_Microsoft)
1177 alias fseekFun = _fseeki64;
1182 alias fseekFun = fseek;
1186 else version (Posix)
1188 import core.sys.posix.stdio : fseeko, off_t;
1189 alias fseekFun = fseeko;
1191 errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0,
1192 "Could not seek in file `"~_name~"'");
1197 import std.conv : text;
1198 static import std.file;
1199 import std.exception;
1201 auto deleteme = testFilename();
1202 auto f = File(deleteme, "w+");
1203 scope(exit) { f.close(); std.file.remove(deleteme); }
1204 f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1206 assert(f.readln() == "hijklmnopqrstuvwxyz");
1208 version (CRuntime_Bionic)
1209 auto bigOffset = int.max - 100;
1211 auto bigOffset = cast(ulong) int.max + 100;
1213 assert(f.tell == bigOffset, text(f.tell));
1214 // Uncomment the tests below only if you want to wait for
1216 // f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1217 // f.seek(-3, SEEK_END);
1218 // assert(f.readln() == "xyz");
1220 assertThrown(f.seek(0, ushort.max));
1224 Calls $(CSTDIO ftell)
1225 for the managed file handle, which returns the current value of
1226 the position indicator of the file handle.
1228 Throws: `Exception` if the file is not opened.
1229 `ErrnoException` if the call to `ftell` fails.
1231 @property ulong tell() const @trusted
1233 import std.exception : enforce, errnoEnforce;
1235 enforce(isOpen, "Attempting to tell() in an unopened file");
1238 version (CRuntime_Microsoft)
1239 immutable result = _ftelli64(cast(FILE*) _p.handle);
1241 immutable result = ftell(cast(FILE*) _p.handle);
1243 else version (Posix)
1245 import core.sys.posix.stdio : ftello;
1246 immutable result = ftello(cast(FILE*) _p.handle);
1248 errnoEnforce(result != -1,
1249 "Query ftell() failed for file `"~_name~"'");
1256 import std.conv : text;
1257 static import std.file;
1259 auto testFile = std.file.deleteme();
1260 std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz");
1261 scope(exit) { std.file.remove(testFile); }
1263 auto f = File(testFile);
1264 auto a = new ubyte[4];
1266 assert(f.tell == 4, text(f.tell));
1270 Calls $(CSTDIO rewind) for the file handle.
1272 Throws: `Exception` if the file is not opened.
1276 import std.exception : enforce;
1278 enforce(isOpen, "Attempting to rewind() an unopened file");
1283 Calls $(CSTDIO setvbuf) for the file handle.
1285 Throws: `Exception` if the file is not opened.
1286 `ErrnoException` if the call to `setvbuf` fails.
1288 void setvbuf(size_t size, int mode = _IOFBF) @trusted
1290 import std.exception : enforce, errnoEnforce;
1292 enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1293 errnoEnforce(.setvbuf(_p.handle, null, mode, size) == 0,
1294 "Could not set buffering for file `"~_name~"'");
1298 Calls $(CSTDIO setvbuf) for the file handle.
1300 Throws: `Exception` if the file is not opened.
1301 `ErrnoException` if the call to `setvbuf` fails.
1303 void setvbuf(void[] buf, int mode = _IOFBF) @trusted
1305 import std.exception : enforce, errnoEnforce;
1307 enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1308 errnoEnforce(.setvbuf(_p.handle,
1309 cast(char*) buf.ptr, mode, buf.length) == 0,
1310 "Could not set buffering for file `"~_name~"'");
1316 import core.sys.windows.winbase : OVERLAPPED;
1317 import core.sys.windows.winnt : BOOL, ULARGE_INTEGER;
1318 import std.windows.syserror : wenforce;
1320 private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
1323 if (!start && !length)
1325 ULARGE_INTEGER liStart = void, liLength = void;
1326 liStart.QuadPart = start;
1327 liLength.QuadPart = length;
1328 OVERLAPPED overlapped;
1329 overlapped.Offset = liStart.LowPart;
1330 overlapped.OffsetHigh = liStart.HighPart;
1331 overlapped.hEvent = null;
1332 return F(windowsHandle, flags, 0, liLength.LowPart,
1333 liLength.HighPart, &overlapped);
1338 private int lockImpl(int operation, short l_type,
1339 ulong start, ulong length)
1341 import core.sys.posix.fcntl : fcntl, flock, off_t;
1342 import core.sys.posix.unistd : getpid;
1343 import std.conv : to;
1347 fl.l_whence = SEEK_SET;
1348 fl.l_start = to!off_t(start);
1349 fl.l_len = to!off_t(length);
1350 fl.l_pid = getpid();
1351 return fcntl(fileno, operation, &fl);
1356 Locks the specified file segment. If the file segment is already locked
1357 by another process, waits until the existing lock is released.
1358 If both `start` and `length` are zero, the entire file is locked.
1360 Locks created using `lock` and `tryLock` have the following properties:
1362 $(LI All locks are automatically released when the process terminates.)
1363 $(LI Locks are not inherited by child processes.)
1364 $(LI Closing a file will release all locks associated with the file. On POSIX,
1365 even locks acquired via a different `File` will be released as well.)
1366 $(LI Not all NFS implementations correctly implement file locking.)
1369 void lock(LockType lockType = LockType.readWrite,
1370 ulong start = 0, ulong length = 0)
1372 import std.exception : enforce;
1374 enforce(isOpen, "Attempting to call lock() on an unopened file");
1377 import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK;
1378 import std.exception : errnoEnforce;
1379 immutable short type = lockType == LockType.readWrite
1380 ? F_WRLCK : F_RDLCK;
1381 errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1,
1382 "Could not set lock for file `"~_name~"'");
1387 import core.sys.windows.winbase : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK;
1388 immutable type = lockType == LockType.readWrite ?
1389 LOCKFILE_EXCLUSIVE_LOCK : 0;
1390 wenforce(lockImpl!LockFileEx(start, length, type),
1391 "Could not set lock for file `"~_name~"'");
1394 static assert(false);
1398 Attempts to lock the specified file segment.
1399 If both `start` and `length` are zero, the entire file is locked.
1400 Returns: `true` if the lock was successful, and `false` if the
1401 specified file segment was already locked.
1403 bool tryLock(LockType lockType = LockType.readWrite,
1404 ulong start = 0, ulong length = 0)
1406 import std.exception : enforce;
1408 enforce(isOpen, "Attempting to call tryLock() on an unopened file");
1411 import core.stdc.errno : EACCES, EAGAIN, errno;
1412 import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK;
1413 import std.exception : errnoEnforce;
1414 immutable short type = lockType == LockType.readWrite
1415 ? F_WRLCK : F_RDLCK;
1416 immutable res = lockImpl(F_SETLK, type, start, length);
1417 if (res == -1 && (errno == EACCES || errno == EAGAIN))
1419 errnoEnforce(res != -1, "Could not set lock for file `"~_name~"'");
1425 import core.sys.windows.winbase : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK,
1426 LOCKFILE_FAIL_IMMEDIATELY;
1427 import core.sys.windows.winerror : ERROR_IO_PENDING, ERROR_LOCK_VIOLATION;
1428 immutable type = lockType == LockType.readWrite
1429 ? LOCKFILE_EXCLUSIVE_LOCK : 0;
1430 immutable res = lockImpl!LockFileEx(start, length,
1431 type | LOCKFILE_FAIL_IMMEDIATELY);
1432 if (!res && (GetLastError() == ERROR_IO_PENDING
1433 || GetLastError() == ERROR_LOCK_VIOLATION))
1435 wenforce(res, "Could not set lock for file `"~_name~"'");
1439 static assert(false);
1443 Removes the lock over the specified file segment.
1445 void unlock(ulong start = 0, ulong length = 0)
1447 import std.exception : enforce;
1449 enforce(isOpen, "Attempting to call unlock() on an unopened file");
1452 import core.sys.posix.fcntl : F_SETLK, F_UNLCK;
1453 import std.exception : errnoEnforce;
1454 errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1,
1455 "Could not remove lock for file `"~_name~"'");
1460 import core.sys.windows.winbase : UnlockFileEx;
1461 wenforce(lockImpl!UnlockFileEx(start, length),
1462 "Could not remove lock for file `"~_name~"'");
1465 static assert(false);
1471 static import std.file;
1472 auto deleteme = testFilename();
1473 scope(exit) std.file.remove(deleteme);
1474 auto f = File(deleteme, "wb");
1475 assert(f.tryLock());
1476 auto g = File(deleteme, "wb");
1477 assert(!g.tryLock());
1478 assert(!g.tryLock(LockType.read));
1480 f.lock(LockType.read);
1481 assert(!g.tryLock());
1482 assert(g.tryLock(LockType.read));
1490 static if (__traits(compiles, { import std.process : spawnProcess; }))
1492 static import std.file;
1493 auto deleteme = testFilename();
1494 scope(exit) std.file.remove(deleteme);
1496 // Since locks are per-process, we cannot test lock failures within
1497 // the same process. fork() is used to create a second process.
1498 static void runForked(void delegate() code)
1500 import core.sys.posix.sys.wait : waitpid;
1501 import core.sys.posix.unistd : fork, _exit;
1503 if ((child = fork()) == 0)
1510 assert(waitpid(child, &status, 0) != -1);
1511 assert(status == 0, "Fork crashed");
1515 auto f = File(deleteme, "w+b");
1519 auto g = File(deleteme, "a+b");
1520 assert(g.tryLock());
1522 assert(g.tryLock(LockType.read));
1525 assert(f.tryLock());
1528 auto g = File(deleteme, "a+b");
1529 assert(!g.tryLock());
1530 assert(!g.tryLock(LockType.read));
1534 f.lock(LockType.read);
1537 auto g = File(deleteme, "a+b");
1538 assert(!g.tryLock());
1539 assert(g.tryLock(LockType.read));
1548 Writes its arguments in text format to the file.
1550 Throws: `Exception` if the file is not opened.
1551 `ErrnoException` on an error writing to the file.
1553 void write(S...)(S args)
1555 import std.traits : isBoolean, isIntegral, isAggregateType;
1556 import std.utf : UTFException;
1557 auto w = lockingTextWriter();
1562 alias A = typeof(arg);
1563 static if (isAggregateType!A || is(A == enum))
1565 import std.format.write : formattedWrite;
1567 formattedWrite(w, "%s", arg);
1569 else static if (isSomeString!A)
1573 else static if (isIntegral!A)
1575 import std.conv : toTextRange;
1577 toTextRange(arg, w);
1579 else static if (isBoolean!A)
1581 put(w, arg ? "true" : "false");
1583 else static if (isSomeChar!A)
1589 import std.format.write : formattedWrite;
1591 // Most general case
1592 formattedWrite(w, "%s", arg);
1595 catch (UTFException e)
1597 /* Reset the writer so that it doesn't throw another
1598 UTFException on destruction. */
1599 w.highSurrogate = '\0';
1606 Writes its arguments in text format to the file, followed by a newline.
1608 Throws: `Exception` if the file is not opened.
1609 `ErrnoException` on an error writing to the file.
1611 void writeln(S...)(S args)
1617 Writes its arguments in text format to the file, according to the
1621 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
1622 When passed as a compile-time argument, the string will be statically checked
1623 against the argument types passed.
1624 args = Items to write.
1626 Throws: `Exception` if the file is not opened.
1627 `ErrnoException` on an error writing to the file.
1629 void writef(alias fmt, A...)(A args)
1630 if (isSomeString!(typeof(fmt)))
1632 import std.format : checkFormatException;
1634 alias e = checkFormatException!(fmt, A);
1635 static assert(!e, e);
1636 return this.writef(fmt, args);
1640 void writef(Char, A...)(in Char[] fmt, A args)
1642 import std.format.write : formattedWrite;
1644 formattedWrite(lockingTextWriter(), fmt, args);
1647 /// Equivalent to `file.writef(fmt, args, '\n')`.
1648 void writefln(alias fmt, A...)(A args)
1649 if (isSomeString!(typeof(fmt)))
1651 import std.format : checkFormatException;
1653 alias e = checkFormatException!(fmt, A);
1654 static assert(!e, e);
1655 return this.writefln(fmt, args);
1659 void writefln(Char, A...)(in Char[] fmt, A args)
1661 import std.format.write : formattedWrite;
1663 auto w = lockingTextWriter();
1664 formattedWrite(w, fmt, args);
1669 Read line from the file handle and return it as a specified type.
1671 This version manages its own read buffer, which means one memory allocation per call. If you are not
1672 retaining a reference to the read data, consider the `File.readln(buf)` version, which may offer
1673 better performance as it can reuse its read buffer.
1676 S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
1677 terminator = Line terminator (by default, `'\n'`).
1680 String terminators are not supported due to ambiguity with readln(buf) below.
1683 The line that was read, including the line terminator character.
1686 `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
1690 // Reads `stdin` and writes it to `stdout`.
1696 while ((line = stdin.readln()) !is null)
1701 S readln(S = string)(dchar terminator = '\n') @safe
1704 Unqual!(ElementEncodingType!S)[] buf;
1705 readln(buf, terminator);
1706 return (() @trusted => cast(S) buf)();
1711 import std.algorithm.comparison : equal;
1712 static import std.file;
1713 import std.meta : AliasSeq;
1715 auto deleteme = testFilename();
1716 std.file.write(deleteme, "hello\nworld\n");
1717 scope(exit) std.file.remove(deleteme);
1718 static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
1720 auto witness = [ "hello\n", "world\n" ];
1721 auto f = File(deleteme);
1724 while ((buf = f.readln!String()).length)
1726 assert(i < witness.length);
1727 assert(equal(buf, witness[i++]));
1729 assert(i == witness.length);
1735 static import std.file;
1736 import std.typecons : Tuple;
1738 auto deleteme = testFilename();
1739 std.file.write(deleteme, "cześć \U0002000D");
1740 scope(exit) std.file.remove(deleteme);
1741 uint[] lengths = [12,8,7];
1742 static foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
1744 immutable(C)[] witness = "cześć \U0002000D";
1745 auto buf = File(deleteme).readln!(immutable(C)[])();
1746 assert(buf.length == lengths[i]);
1747 assert(buf == witness);
1752 Read line from the file handle and write it to `buf[]`, including
1753 terminating character.
1755 This can be faster than $(D line = File.readln()) because you can reuse
1756 the buffer for each call. Note that reusing the buffer means that you
1757 must copy the previous contents if you wish to retain them.
1760 buf = Buffer used to store the resulting line data. buf is
1761 enlarged if necessary, then set to the slice exactly containing the line.
1762 terminator = Line terminator (by default, `'\n'`). Use
1763 $(REF newline, std,ascii) for portability (unless the file was opened in
1767 0 for end of file, otherwise number of characters read.
1768 The return value will always be equal to `buf.length`.
1770 Throws: `StdioException` on I/O error, or `UnicodeException` on Unicode
1775 // Read lines from `stdin` into a string
1776 // Ignore lines starting with '#'
1777 // Write the string to `stdout`
1785 while (stdin.readln(buf))
1797 This method can be more efficient than the one in the previous example
1798 because `stdin.readln(buf)` reuses (if possible) memory allocated
1799 for `buf`, whereas $(D line = stdin.readln()) makes a new memory allocation
1802 For even better performance you can help `readln` by passing in a
1803 large buffer to avoid memory reallocations. This can be done by reusing the
1804 largest buffer returned by `readln`:
1808 // Read lines from `stdin` and count words
1809 import std.array, std.stdio;
1820 if (line.length > buf.length)
1823 words += line.split.length;
1829 This is actually what $(LREF byLine) does internally, so its usage
1830 is recommended if you want to process a complete file.
1832 size_t readln(C)(ref C[] buf, dchar terminator = '\n') @safe
1833 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
1835 import std.exception : enforce;
1837 static if (is(C == char))
1839 enforce(_p && _p.handle, "Attempt to read from an unopened file.");
1840 if (_p.orientation == Orientation.unknown)
1842 import core.stdc.wchar_ : fwide;
1843 auto w = fwide(_p.handle, 0);
1844 if (w < 0) _p.orientation = Orientation.narrow;
1845 else if (w > 0) _p.orientation = Orientation.wide;
1847 return readlnImpl(_p.handle, buf, terminator, _p.orientation);
1851 string s = readln(terminator);
1858 import std.utf : codeLength;
1859 buf.length = codeLength!C(s);
1870 static import std.file;
1871 auto deleteme = testFilename();
1872 std.file.write(deleteme, "123\n456789");
1873 scope(exit) std.file.remove(deleteme);
1875 auto file = File(deleteme);
1876 char[] buffer = new char[10];
1877 char[] line = buffer;
1879 auto beyond = line.length;
1880 buffer[beyond] = 'a';
1881 file.readln(line); // should not write buffer beyond line
1882 assert(buffer[beyond] == 'a');
1885 // https://issues.dlang.org/show_bug.cgi?id=15293
1888 // @system due to readln
1889 static import std.file;
1890 auto deleteme = testFilename();
1891 std.file.write(deleteme, "a\n\naa");
1892 scope(exit) std.file.remove(deleteme);
1894 auto file = File(deleteme);
1898 file.readln(buffer, '\n');
1901 file.readln(line, '\n');
1904 file.readln(line, '\n');
1906 assert(line[0 .. 1].capacity == 0);
1910 size_t readln(C, R)(ref C[] buf, R terminator) @safe
1911 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
1912 isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
1914 import std.algorithm.mutation : swap;
1915 import std.algorithm.searching : endsWith;
1916 import std.range.primitives : back;
1918 auto last = terminator.back;
1923 if (!readln(buf2, last) || endsWith(buf2, terminator))
1942 static import std.file;
1943 import std.typecons : Tuple;
1945 auto deleteme = testFilename();
1946 std.file.write(deleteme, "hello\n\rworld\nhow\n\rare ya");
1947 scope(exit) std.file.remove(deleteme);
1948 foreach (C; Tuple!(char, wchar, dchar).Types)
1950 immutable(C)[][] witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
1951 auto f = File(deleteme);
1954 while (f.readln(buf, "\n\r"))
1956 assert(i < witness.length);
1957 assert(buf == witness[i++]);
1959 assert(buf.length == 0);
1964 * Reads formatted _data from the file using $(REF formattedRead, std,_format).
1966 * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
1967 * When passed as a compile-time argument, the string will be statically checked
1968 * against the argument types passed.
1969 * data = Items to be read.
1971 * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
1972 * this number will be less than the number of variables provided.
1979 auto f = File("input");
1989 % echo "1 2 3" > input
1996 uint readf(alias format, Data...)(auto ref Data data)
1997 if (isSomeString!(typeof(format)))
1999 import std.format : checkFormatException;
2001 alias e = checkFormatException!(format, Data);
2002 static assert(!e, e);
2003 return this.readf(format, data);
2007 uint readf(Data...)(scope const(char)[] format, auto ref Data data)
2009 import std.format.read : formattedRead;
2012 auto input = LockingTextReader(this);
2013 return formattedRead(input, format, data);
2019 static import std.file;
2021 auto deleteme = std.file.deleteme();
2022 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2023 scope(exit) std.file.remove(deleteme);
2025 auto f = File(deleteme);
2027 assert(s == "hello", "["~s~"]");
2029 assert(s == "world", "["~s~"]");
2032 f.readf("%s\n%s\n", b1, b2);
2033 assert(b1 == true && b2 == false);
2036 // backwards compatibility with pointers
2039 // @system due to readf
2040 static import std.file;
2042 auto deleteme = testFilename();
2043 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2044 scope(exit) std.file.remove(deleteme);
2046 auto f = File(deleteme);
2047 f.readf("%s\n", &s);
2048 assert(s == "hello", "["~s~"]");
2049 f.readf("%s\n", &s);
2050 assert(s == "world", "["~s~"]");
2052 // https://issues.dlang.org/show_bug.cgi?id=11698
2054 f.readf("%s\n%s\n", &b1, &b2);
2055 assert(b1 == true && b2 == false);
2058 // backwards compatibility (mixed)
2061 // @system due to readf
2062 static import std.file;
2064 auto deleteme = testFilename();
2065 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2066 scope(exit) std.file.remove(deleteme);
2068 auto f = File(deleteme);
2069 f.readf("%s\n%s\n", s1, &s2);
2070 assert(s1 == "hello");
2071 assert(s2 == "world");
2073 // https://issues.dlang.org/show_bug.cgi?id=11698
2075 f.readf("%s\n%s\n", &b1, b2);
2076 assert(b1 == true && b2 == false);
2079 // Nice error of std.stdio.readf with newlines
2080 // https://issues.dlang.org/show_bug.cgi?id=12260
2083 static import std.file;
2085 auto deleteme = testFilename();
2086 std.file.write(deleteme, "1\n2");
2087 scope(exit) std.file.remove(deleteme);
2089 auto f = File(deleteme);
2090 f.readf("%s", &input);
2092 import std.conv : ConvException;
2093 import std.exception : collectException;
2094 assert(collectException!ConvException(f.readf("%s", &input)).msg ==
2095 "Unexpected '\\n' when converting from type LockingTextReader to type int");
2099 Reads a line from the file and parses it using $(REF formattedRead, std,format,read).
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.
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
2117 auto f = File("input");
2119 while (f.readfln("%d %d %d", a, b, c) == 3)
2126 % cat << EOF > input
2137 uint readfln(alias format, Data...)(auto ref Data data)
2138 if (isSomeString!(typeof(format)))
2140 import std.format : checkFormatException;
2142 alias e = checkFormatException!(format, Data);
2143 static assert(!e, e);
2144 return this.readfln(format, data);
2148 uint readfln(Data...)(scope const(char)[] format, auto ref Data data)
2150 import std.format.read : formattedRead;
2151 import std.string : stripRight;
2153 string line = this.readln.stripRight("\r\n");
2154 return formattedRead(line, format, data);
2159 static import std.file;
2161 auto deleteme = testFilename();
2162 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2163 scope(exit) std.file.remove(deleteme);
2165 auto f = File(deleteme);
2167 assert(s == "hello", "["~s~"]");
2169 assert(s == "world", "["~s~"]");
2172 f.readfln("%s", b1);
2173 f.readfln("%s", b2);
2174 assert(b1 == true && b2 == false);
2178 Returns a temporary file by calling $(CSTDIO tmpfile).
2179 Note that the created file has no $(LREF name).*/
2180 static File tmpfile() @safe
2182 import std.exception : errnoEnforce;
2184 return File(errnoEnforce(.tmpfile(),
2185 "Could not create temporary file with tmpfile()"),
2190 Unsafe function that wraps an existing `FILE*`. The resulting $(D
2191 File) never takes the initiative in closing the file.
2192 Note that the created file has no $(LREF name)*/
2193 /*private*/ static File wrapFile(FILE* f) @safe
2195 import std.exception : enforce;
2197 return File(enforce(f, "Could not wrap null FILE*"),
2198 null, /*uint.max / 2*/ 9999);
2202 Returns the `FILE*` corresponding to this object.
2204 FILE* getFP() @safe pure
2206 import std.exception : enforce;
2208 enforce(_p && _p.handle,
2209 "Attempting to call getFP() on an unopened file");
2215 static import core.stdc.stdio;
2216 assert(stdout.getFP() == core.stdc.stdio.stdout);
2220 Returns the file number corresponding to this object.
2222 @property fileno_t fileno() const @trusted
2224 import std.exception : enforce;
2226 enforce(isOpen, "Attempting to call fileno() on an unopened file");
2227 return .fileno(cast(FILE*) _p.handle);
2231 Returns the underlying operating system `HANDLE` (Windows only).
2234 @property HANDLE windowsHandle();
2237 @property HANDLE windowsHandle()
2239 return cast(HANDLE)_get_osfhandle(fileno);
2243 // Note: This was documented until 2013/08
2245 Range that reads one line at a time. Returned by $(LREF byLine).
2247 Allows to directly use range operations on lines of a file.
2249 private struct ByLineImpl(Char, Terminator)
2252 import std.typecons : borrow, RefCountedAutoInitialize, SafeRefCounted;
2254 /* Ref-counting stops the source range's Impl
2255 * from getting out of sync after the range is copied, e.g.
2256 * when accessing range.front, then using std.range.take,
2257 * then accessing range.front again. */
2258 alias PImpl = SafeRefCounted!(Impl, RefCountedAutoInitialize.no);
2261 static if (isScalarType!Terminator)
2262 enum defTerm = '\n';
2264 enum defTerm = cast(Terminator)"\n";
2267 this(File f, KeepTerminator kt = No.keepTerminator,
2268 Terminator terminator = defTerm)
2270 impl = PImpl(f, kt, terminator);
2273 /* Verifiably `@safe` when built with -preview=DIP1000. */
2274 @property bool empty() @trusted
2276 // Using `ref` is actually necessary here.
2277 return impl.borrow!((ref i) => i.empty);
2280 /* Verifiably `@safe` when built with -preview=DIP1000. */
2281 @property Char[] front() @trusted
2283 // Using `ref` is likely optional here.
2284 return impl.borrow!((ref i) => i.front);
2287 /* Verifiably `@safe` when built with -preview=DIP1000. */
2288 void popFront() @trusted
2290 return impl.borrow!((ref i) => i.popFront());
2300 Terminator terminator;
2301 KeepTerminator keepTerminator;
2306 this(File f, KeepTerminator kt, Terminator terminator)
2309 this.terminator = terminator;
2310 keepTerminator = kt;
2313 // Range primitive implementations.
2314 @property bool empty()
2317 return line is null;
2320 @property Char[] front()
2337 import std.algorithm.searching : endsWith;
2338 assert(file.isOpen);
2340 file.readln(line, terminator);
2341 if (line.length > buffer.length)
2350 else if (keepTerminator == No.keepTerminator
2351 && endsWith(line, terminator))
2353 static if (isScalarType!Terminator)
2355 else static if (isArray!Terminator)
2358 is(immutable ElementEncodingType!Terminator == immutable Char));
2359 const tlen = terminator.length;
2362 static assert(false);
2363 line = line[0 .. line.length - tlen];
2371 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2372 set up to read from the file handle one line at a time.
2374 The element type for the range will be `Char[]`. Range primitives
2375 may throw `StdioException` on I/O error.
2378 Each `front` will not persist after $(D
2379 popFront) is called, so the caller must copy its contents (e.g. by
2380 calling `to!string`) when retention is needed. If the caller needs
2381 to retain a copy of every line, use the $(LREF byLineCopy) function
2385 Char = Character type for each line, defaulting to `char`.
2386 keepTerminator = Use `Yes.keepTerminator` to include the
2387 terminator at the end of each line.
2388 terminator = Line separator (`'\n'` by default). Use
2389 $(REF newline, std,ascii) for portability (unless the file was opened in
2394 import std.algorithm, std.stdio, std.string;
2395 // Count words in a file using ranges.
2398 auto file = File("file.txt"); // Open for reading
2399 const wordCount = file.byLine() // Read lines
2400 .map!split // Split into words
2401 .map!(a => a.length) // Count words per line
2402 .sum(); // Total word count
2409 import std.range, std.stdio;
2410 // Read lines using foreach.
2413 auto file = File("file.txt"); // Open for reading
2414 auto range = file.byLine();
2415 // Print first three lines
2416 foreach (line; range.take(3))
2418 // Print remaining lines beginning with '#'
2419 foreach (line; range)
2421 if (!line.empty && line[0] == '#')
2426 Notice that neither example accesses the line data returned by
2427 `front` after the corresponding `popFront` call is made (because
2428 the contents may well have changed).
2431 Windows specific Example:
2439 foreach (line; File("file.txt").byLine(No.keepTerminator, "\r\n"))
2441 writeln("|"~line~"|");
2442 if (line == "HelloWorld")
2443 writeln("^This Line is here.");
2448 auto byLine(Terminator = char, Char = char)
2449 (KeepTerminator keepTerminator = No.keepTerminator,
2450 Terminator terminator = '\n')
2451 if (isScalarType!Terminator)
2453 return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
2457 auto byLine(Terminator, Char = char)
2458 (KeepTerminator keepTerminator, Terminator terminator)
2459 if (is(immutable ElementEncodingType!Terminator == immutable Char))
2461 return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
2466 static import std.file;
2467 auto deleteme = testFilename();
2468 std.file.write(deleteme, "hi");
2469 scope(success) std.file.remove(deleteme);
2471 import std.meta : AliasSeq;
2472 static foreach (T; AliasSeq!(char, wchar, dchar))
2474 auto blc = File(deleteme).byLine!(T, T);
2475 assert(blc.front == "hi");
2476 // check front is cached
2477 assert(blc.front is blc.front);
2481 // https://issues.dlang.org/show_bug.cgi?id=19980
2484 static import std.file;
2485 auto deleteme = testFilename();
2486 std.file.write(deleteme, "Line 1\nLine 2\nLine 3\n");
2487 scope(success) std.file.remove(deleteme);
2489 auto f = File(deleteme);
2492 assert(f.byLine().front == "Line 1");
2495 private struct ByLineCopy(Char, Terminator)
2498 import std.typecons : borrow, RefCountedAutoInitialize, SafeRefCounted;
2500 /* Ref-counting stops the source range's ByLineCopyImpl
2501 * from getting out of sync after the range is copied, e.g.
2502 * when accessing range.front, then using std.range.take,
2503 * then accessing range.front again. */
2504 alias Impl = SafeRefCounted!(ByLineCopyImpl!(Char, Terminator),
2505 RefCountedAutoInitialize.no);
2509 this(File f, KeepTerminator kt, Terminator terminator)
2511 impl = Impl(f, kt, terminator);
2514 /* Verifiably `@safe` when built with -preview=DIP1000. */
2515 @property bool empty() @trusted
2517 // Using `ref` is actually necessary here.
2518 return impl.borrow!((ref i) => i.empty);
2521 /* Verifiably `@safe` when built with -preview=DIP1000. */
2522 @property Char[] front() @trusted
2524 // Using `ref` is likely optional here.
2525 return impl.borrow!((ref i) => i.front);
2528 /* Verifiably `@safe` when built with -preview=DIP1000. */
2529 void popFront() @trusted
2531 impl.borrow!((ref i) => i.popFront());
2535 private struct ByLineCopyImpl(Char, Terminator)
2537 ByLineImpl!(Unqual!Char, Terminator).Impl impl;
2542 this(File f, KeepTerminator kt, Terminator terminator)
2544 impl = ByLineImpl!(Unqual!Char, Terminator).Impl(f, kt, terminator);
2547 @property bool empty()
2556 line = impl.front.dup;
2570 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2571 set up to read from the file handle one line
2572 at a time. Each line will be newly allocated. `front` will cache
2573 its value to allow repeated calls without unnecessary allocations.
2575 Note: Due to caching byLineCopy can be more memory-efficient than
2576 `File.byLine.map!idup`.
2578 The element type for the range will be `Char[]`. Range
2579 primitives may throw `StdioException` on I/O error.
2582 Char = Character type for each line, defaulting to $(D immutable char).
2583 keepTerminator = Use `Yes.keepTerminator` to include the
2584 terminator at the end of each line.
2585 terminator = Line separator (`'\n'` by default). Use
2586 $(REF newline, std,ascii) for portability (unless the file was opened in
2591 import std.algorithm, std.array, std.stdio;
2592 // Print sorted lines of a file.
2595 auto sortedLines = File("file.txt") // Open for reading
2596 .byLineCopy() // Read persistent lines
2597 .array() // into an array
2598 .sort(); // then sort them
2599 foreach (line; sortedLines)
2604 $(REF readText, std,file)
2606 auto byLineCopy(Terminator = char, Char = immutable char)
2607 (KeepTerminator keepTerminator = No.keepTerminator,
2608 Terminator terminator = '\n')
2609 if (isScalarType!Terminator)
2611 return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2615 auto byLineCopy(Terminator, Char = immutable char)
2616 (KeepTerminator keepTerminator, Terminator terminator)
2617 if (is(immutable ElementEncodingType!Terminator == immutable Char))
2619 return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2624 static assert(is(typeof(File("").byLine.front) == char[]));
2625 static assert(is(typeof(File("").byLineCopy.front) == string));
2627 is(typeof(File("").byLineCopy!(char, char).front) == char[]));
2632 import std.algorithm.comparison : equal;
2633 static import std.file;
2635 auto deleteme = testFilename();
2636 std.file.write(deleteme, "");
2637 scope(success) std.file.remove(deleteme);
2640 auto f = File(deleteme);
2641 foreach (line; f.byLine())
2648 void test(Terminator)(string txt, in string[] witness,
2649 KeepTerminator kt, Terminator term, bool popFirstLine = false)
2651 import std.algorithm.sorting : sort;
2652 import std.array : array;
2653 import std.conv : text;
2654 import std.range.primitives : walkLength;
2657 std.file.write(deleteme, txt);
2658 auto f = File(deleteme);
2664 auto lines = f.byLine(kt, term);
2670 assert(lines.empty || lines.front is lines.front);
2671 foreach (line; lines)
2673 assert(line == witness[i++]);
2675 assert(i == witness.length, text(i, " != ", witness.length));
2677 // https://issues.dlang.org/show_bug.cgi?id=11830
2678 auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
2679 assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
2681 // test persistent lines
2682 assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
2685 KeepTerminator kt = No.keepTerminator;
2686 test("", null, kt, '\n');
2687 test("\n", [ "" ], kt, '\n');
2688 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n');
2689 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true);
2690 test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n');
2691 test("foo", [ "foo" ], kt, '\n', true);
2692 test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
2694 test("sue\r", ["sue"], kt, '\r');
2696 kt = Yes.keepTerminator;
2697 test("", null, kt, '\n');
2698 test("\n", [ "\n" ], kt, '\n');
2699 test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n');
2700 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n');
2701 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true);
2702 test("foo", [ "foo" ], kt, '\n');
2703 test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
2705 test("sue\r", ["sue\r"], kt, '\r');
2710 import std.algorithm.comparison : equal;
2711 import std.range : drop, take;
2715 static import std.file;
2717 /* the C function tmpfile doesn't seem to work, even when called from C */
2718 auto deleteme = testFilename();
2719 auto file = File(deleteme, "w+");
2720 scope(success) std.file.remove(deleteme);
2722 else version (CRuntime_Bionic)
2724 static import std.file;
2726 /* the C function tmpfile doesn't work when called from a shared
2728 https://code.google.com/p/android/issues/detail?id=66815 */
2729 auto deleteme = testFilename();
2730 auto file = File(deleteme, "w+");
2731 scope(success) std.file.remove(deleteme);
2734 auto file = File.tmpfile();
2735 file.write("1\n2\n3\n");
2737 // https://issues.dlang.org/show_bug.cgi?id=9599
2739 File.ByLineImpl!(char, char) fbl = file.byLine();
2741 assert(fbl.front == "1");
2742 assert(fbl.front is fbl2.front);
2743 assert(fbl.take(1).equal(["1"]));
2744 assert(fbl.equal(["2", "3"]));
2746 assert(file.isOpen); // we still have a valid reference
2749 fbl = file.byLine();
2750 assert(!fbl.drop(2).empty);
2751 assert(fbl.equal(["3"]));
2753 assert(file.isOpen);
2756 assert(!file.isOpen);
2761 static import std.file;
2762 auto deleteme = testFilename();
2763 std.file.write(deleteme, "hi");
2764 scope(success) std.file.remove(deleteme);
2766 auto blc = File(deleteme).byLineCopy;
2768 // check front is cached
2769 assert(blc.front is blc.front);
2773 Creates an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2774 set up to parse one line at a time from the file into a tuple.
2776 Range primitives may throw `StdioException` on I/O error.
2779 format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
2782 The input range set up to parse one line at a time into a record tuple.
2786 It is similar to $(LREF byLine) and uses
2787 $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood.
2789 template byRecord(Fields...)
2791 auto byRecord(string format)
2793 return ByRecordImpl!(Fields)(this, format);
2800 static import std.file;
2801 import std.typecons : tuple;
2803 // prepare test file
2804 auto testFile = std.file.deleteme();
2805 scope(failure) printf("Failed test at line %d\n", __LINE__);
2806 std.file.write(testFile, "1 2\n4 1\n5 100");
2807 scope(exit) std.file.remove(testFile);
2809 File f = File(testFile);
2810 scope(exit) f.close();
2812 auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
2814 foreach (e; f.byRecord!(int, int)("%s %s"))
2816 assert(e == expected[i++]);
2820 // Note: This was documented until 2013/08
2822 * Range that reads a chunk at a time.
2824 private struct ByChunkImpl
2832 chunk_ = file_.rawRead(chunk_);
2833 if (chunk_.length == 0)
2838 this(File file, size_t size)
2840 this(file, new ubyte[](size));
2843 this(File file, ubyte[] buffer)
2845 import std.exception : enforce;
2846 enforce(buffer.length, "size must be larger than 0");
2852 // `ByChunk`'s input range primitive operations.
2856 return !file_.isOpen;
2865 import core.exception : RangeError;
2867 throw new RangeError();
2877 import core.exception : RangeError;
2879 throw new RangeError();
2886 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2887 set up to read from the file handle a chunk at a time.
2889 The element type for the range will be `ubyte[]`. Range primitives
2890 may throw `StdioException` on I/O error.
2896 // Read standard input 4KB at a time
2897 foreach (ubyte[] buffer; stdin.byChunk(4096))
2904 The parameter may be a number (as shown in the example above) dictating the
2905 size of each chunk. Alternatively, `byChunk` accepts a
2906 user-provided buffer that it uses directly.
2912 // Read standard input 4KB at a time
2913 foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
2920 In either case, the content of the buffer is reused across calls. That means
2921 `front` will not persist after `popFront` is called, so if retention is
2922 needed, the caller must copy its contents (e.g. by calling `buffer.dup`).
2924 In the example above, `buffer.length` is 4096 for all iterations, except
2925 for the last one, in which case `buffer.length` may be less than 4096 (but
2926 always greater than zero).
2928 With the mentioned limitations, `byChunk` works with any algorithm
2929 compatible with input ranges.
2933 // Efficient file copy, 1MB at a time.
2934 import std.algorithm, std.stdio;
2937 stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
2941 $(REF joiner, std,algorithm,iteration) can be used to join chunks together into
2942 a single range lazily.
2945 import std.algorithm, std.stdio;
2949 static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
2951 static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
2955 Returns: A call to `byChunk` returns a range initialized with the `File`
2956 object and the appropriate buffer.
2958 Throws: If the user-provided size is zero or the user-provided buffer
2959 is empty, throws an `Exception`. In case of an I/O error throws
2962 auto byChunk(size_t chunkSize)
2964 return ByChunkImpl(this, chunkSize);
2967 auto byChunk(ubyte[] buffer)
2969 return ByChunkImpl(this, buffer);
2974 static import std.file;
2976 scope(failure) printf("Failed test at line %d\n", __LINE__);
2978 auto deleteme = testFilename();
2979 std.file.write(deleteme, "asd\ndef\nasdf");
2981 auto witness = ["asd\n", "def\n", "asdf" ];
2982 auto f = File(deleteme);
2987 std.file.remove(deleteme);
2991 foreach (chunk; f.byChunk(4))
2992 assert(chunk == cast(ubyte[]) witness[i++]);
2994 assert(i == witness.length);
2999 static import std.file;
3001 scope(failure) printf("Failed test at line %d\n", __LINE__);
3003 auto deleteme = testFilename();
3004 std.file.write(deleteme, "asd\ndef\nasdf");
3006 auto witness = ["asd\n", "def\n", "asdf" ];
3007 auto f = File(deleteme);
3012 std.file.remove(deleteme);
3016 foreach (chunk; f.byChunk(new ubyte[4]))
3017 assert(chunk == cast(ubyte[]) witness[i++]);
3019 assert(i == witness.length);
3022 // Note: This was documented until 2013/08
3024 `Range` that locks the file and allows fast writing to it.
3026 struct LockingTextWriter
3029 import std.range.primitives : ElementType, isInfinite, isInputRange;
3030 // Access the FILE* handle through the 'file_' member
3031 // to keep the object alive through refcounting
3034 // the unshared version of FILE* handle, extracted from the File object
3035 @property _iobuf* handle_() @trusted { return cast(_iobuf*) file_._p.handle; }
3037 // the file's orientation (byte- or wide-oriented)
3040 // Buffers for when we need to transcode.
3041 wchar highSurrogate = '\0'; // '\0' indicates empty
3042 void highSurrogateShouldBeEmpty() @safe
3044 import std.utf : UTFException;
3045 if (highSurrogate != '\0')
3046 throw new UTFException("unpaired surrogate UTF-16 value");
3049 size_t rbuf8Filled = 0;
3052 this(ref File f) @trusted
3054 import std.exception : enforce;
3056 enforce(f._p && f._p.handle, "Attempting to write to closed File");
3058 FILE* fps = f._p.handle;
3060 version (CRuntime_Microsoft)
3062 // Microsoft doesn't implement fwide. Instead, there's the
3063 // concept of ANSI/UNICODE mode. fputc doesn't work in UNICODE
3064 // mode; fputwc has to be used. So that essentially means
3065 // "wide-oriented" for us.
3066 immutable int mode = _setmode(f.fileno, _O_TEXT);
3067 // Set some arbitrary mode to obtain the previous one.
3068 if (mode != -1) // _setmode() succeeded
3070 _setmode(f.fileno, mode); // Restore previous mode.
3071 if (mode & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT))
3073 orientation_ = 1; // wide
3079 import core.stdc.wchar_ : fwide;
3080 orientation_ = fwide(fps, 0);
3088 if (auto p = file_._p)
3090 if (p.handle) _FUNLOCK(p.handle);
3093 /* Destroy file_ before possibly throwing. Else it wouldn't be
3094 destroyed, and its reference count would be wrong. */
3095 highSurrogateShouldBeEmpty();
3100 if (auto p = file_._p)
3102 if (p.handle) _FLOCK(p.handle);
3106 /// Range primitive implementations.
3107 void put(A)(scope A writeme)
3108 if ((isSomeChar!(ElementType!A) ||
3109 is(ElementType!A : const(ubyte))) &&
3113 import std.exception : errnoEnforce;
3115 alias C = ElementEncodingType!A;
3116 static assert(!is(C == void));
3117 static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[]))
3119 if (orientation_ <= 0)
3121 //file.write(writeme); causes infinite recursion!!!
3122 //file.rawWrite(writeme);
3123 auto result = trustedFwrite(file_._p.handle, writeme);
3124 if (result != writeme.length) errnoEnforce(0);
3129 // put each element in turn.
3130 foreach (c; writeme)
3137 void put(C)(scope C c) @safe
3138 if (isSomeChar!C || is(C : const(ubyte)))
3140 import std.utf : decodeFront, encode, stride;
3142 static if (c.sizeof == 1)
3144 highSurrogateShouldBeEmpty();
3145 if (orientation_ <= 0) trustedFPUTC(c, handle_);
3146 else if (c <= 0x7F) trustedFPUTWC(c, handle_);
3147 else if (c >= 0b1100_0000) // start byte of multibyte sequence
3152 else // continuation byte of multibyte sequence
3154 rbuf8[rbuf8Filled] = c;
3156 if (stride(rbuf8[]) == rbuf8Filled) // sequence is complete
3158 char[] str = rbuf8[0 .. rbuf8Filled];
3159 immutable dchar d = decodeFront(str);
3160 wchar_t[4 / wchar_t.sizeof] wbuf;
3161 immutable size = encode(wbuf, d);
3162 foreach (i; 0 .. size)
3163 trustedFPUTWC(wbuf[i], handle_);
3168 else static if (c.sizeof == 2)
3170 import std.utf : decode;
3174 highSurrogateShouldBeEmpty();
3175 if (orientation_ <= 0) trustedFPUTC(c, handle_);
3176 else trustedFPUTWC(c, handle_);
3178 else if (0xD800 <= c && c <= 0xDBFF) // high surrogate
3180 highSurrogateShouldBeEmpty();
3183 else // standalone or low surrogate
3186 if (highSurrogate != '\0')
3188 immutable wchar[2] rbuf = [highSurrogate, c];
3190 d = decode(rbuf[], index);
3193 if (orientation_ <= 0)
3196 immutable size = encode(wbuf, d);
3197 foreach (i; 0 .. size)
3198 trustedFPUTC(wbuf[i], handle_);
3202 wchar_t[4 / wchar_t.sizeof] wbuf;
3203 immutable size = encode(wbuf, d);
3204 foreach (i; 0 .. size)
3205 trustedFPUTWC(wbuf[i], handle_);
3210 else // 32-bit characters
3212 import std.utf : encode;
3214 highSurrogateShouldBeEmpty();
3215 if (orientation_ <= 0)
3219 trustedFPUTC(c, handle_);
3224 immutable len = encode(buf, c);
3225 foreach (i ; 0 .. len)
3226 trustedFPUTC(buf[i], handle_);
3233 import std.utf : isValidDchar;
3235 assert(isValidDchar(c));
3238 trustedFPUTWC(cast(wchar_t) c, handle_);
3242 trustedFPUTWC(cast(wchar_t)
3243 ((((c - 0x10000) >> 10) & 0x3FF)
3244 + 0xD800), handle_);
3245 trustedFPUTWC(cast(wchar_t)
3246 (((c - 0x10000) & 0x3FF) + 0xDC00),
3250 else version (Posix)
3252 trustedFPUTWC(cast(wchar_t) c, handle_);
3264 * Output range which locks the file when created, and unlocks the file when it goes
3267 * Returns: An $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3268 * which accepts string types, `ubyte[]`, individual character types, and
3269 * individual `ubyte`s.
3271 * Note: Writing either arrays of `char`s or `ubyte`s is faster than
3272 * writing each character individually from a range. For large amounts of data,
3273 * writing the contents in chunks using an intermediary array can result
3274 * in a speed increase.
3276 * Throws: $(REF UTFException, std, utf) if the data given is a `char` range
3277 * and it contains malformed UTF data.
3279 * See_Also: $(LREF byChunk) for an example.
3281 auto lockingTextWriter() @safe
3283 return LockingTextWriter(this);
3286 // An output range which optionally locks the file and puts it into
3287 // binary mode (similar to rawWrite). Because it needs to restore
3288 // the file mode on destruction, it is RefCounted on Windows.
3289 struct BinaryWriterImpl(bool locking)
3291 import std.traits : hasIndirections;
3293 // Access the FILE* handle through the 'file_' member
3294 // to keep the object alive through refcounting
3305 // Don't use this, but `File.lockingBinaryWriter()` instead.
3306 // Must be public for RefCounted and emplace() in druntime.
3307 this(scope ref File f)
3309 import std.exception : enforce;
3311 enforce(f._p && f._p.handle);
3313 FILE* fps = f._p.handle;
3319 .fflush(fps); // before changing translation mode
3321 oldMode = ._setmode(fd, _O_BINARY);
3327 if (!file_._p || !file_._p.handle)
3330 FILE* fps = file_._p.handle;
3334 .fflush(fps); // before restoring translation mode
3335 ._setmode(fd, oldMode);
3341 void rawWrite(T)(in T[] buffer)
3343 import std.conv : text;
3344 import std.exception : errnoEnforce;
3346 auto result = trustedFwrite(file_._p.handle, buffer);
3347 if (result == result.max) result = 0;
3348 errnoEnforce(result == buffer.length,
3349 text("Wrote ", result, " instead of ", buffer.length,
3350 " objects of type ", T.stringof, " to file `",
3356 @disable this(this);
3362 if (auto p = file_._p)
3364 if (p.handle) _FLOCK(p.handle);
3369 void put(T)(auto ref scope const T value)
3370 if (!hasIndirections!T &&
3373 rawWrite((&value)[0 .. 1]);
3376 void put(T)(scope const(T)[] array)
3377 if (!hasIndirections!T &&
3384 /** Returns an output range that locks the file and allows fast writing to it.
3387 Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
3388 in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
3390 import std.algorithm, std.complex, std.range, std.stdio;
3395 writef("P5\n%d %d %d\n", size, size, ubyte.max);
3397 iota(-1, 3, 2.0/size).map!(y =>
3398 iota(-1.5, 0.5, 2.0/size).map!(x =>
3400 recurrence!((a, n) => x + y * complex(0, 1) + a[n-1]^^2)(complex(0))
3402 .countUntil!(z => z.re^^2 + z.im^^2 > 4))
3405 .copy(stdout.lockingBinaryWriter);
3409 auto lockingBinaryWriter()
3411 alias LockingBinaryWriterImpl = BinaryWriterImpl!true;
3415 import std.typecons : RefCounted;
3416 alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl;
3419 alias LockingBinaryWriter = LockingBinaryWriterImpl;
3421 return LockingBinaryWriter(this);
3426 import std.algorithm.mutation : reverse;
3427 import std.exception : collectException;
3428 static import std.file;
3429 import std.range : only, retro;
3430 import std.string : format;
3432 auto deleteme = testFilename();
3433 scope(exit) collectException(std.file.remove(deleteme));
3436 auto writer = File(deleteme, "wb").lockingBinaryWriter();
3437 auto input = File(deleteme, "rb");
3439 ubyte[1] byteIn = [42];
3440 writer.rawWrite(byteIn);
3443 ubyte[1] byteOut = input.rawRead(new ubyte[1]);
3444 assert(byteIn[0] == byteOut[0]);
3447 auto output = File(deleteme, "wb");
3448 auto writer = output.lockingBinaryWriter();
3449 auto input = File(deleteme, "rb");
3451 T[] readExact(T)(T[] buf)
3453 auto result = input.rawRead(buf);
3454 assert(result.length == buf.length,
3455 "Read %d out of %d bytes"
3456 .format(result.length, buf.length));
3462 byteIn.only.copy(writer); output.flush();
3463 ubyte byteOut = readExact(new ubyte[1])[0];
3464 assert(byteIn == byteOut);
3467 ubyte[] bytesIn = [1, 2, 3, 4, 5];
3468 bytesIn.copy(writer); output.flush();
3469 ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]);
3470 scope(failure) .writeln(bytesOut);
3471 assert(bytesIn == bytesOut);
3473 // test ranges of values
3474 bytesIn.retro.copy(writer); output.flush();
3475 bytesOut = readExact(bytesOut);
3477 assert(bytesIn == bytesOut);
3480 "foobar".copy(writer); output.flush();
3481 char[] charsOut = readExact(new char[6]);
3482 assert(charsOut == "foobar");
3484 // test ranges of arrays
3485 only("foo", "bar").copy(writer); output.flush();
3486 charsOut = readExact(charsOut);
3487 assert(charsOut == "foobar");
3489 // test that we are writing arrays as is,
3490 // without UTF-8 transcoding
3491 "foo"d.copy(writer); output.flush();
3492 dchar[] dcharsOut = readExact(new dchar[3]);
3493 assert(dcharsOut == "foo");
3496 /** Returns the size of the file in bytes, ulong.max if file is not searchable or throws if the operation fails.
3499 import std.stdio, std.file;
3503 string deleteme = "delete.me";
3504 auto file_handle = File(deleteme, "w");
3505 file_handle.write("abc"); //create temporary file
3506 scope(exit) deleteme.remove; //remove temporary file at scope exit
3508 assert(file_handle.size() == 3); //check if file size is 3 bytes
3512 @property ulong size() @safe
3514 import std.exception : collectException;
3517 if (collectException(pos = tell)) return ulong.max;
3518 scope(exit) seek(pos);
3526 @system struct SystemToString
3534 @trusted struct TrustedToString
3542 @safe struct SafeToString
3550 @system void systemTests()
3552 //system code can write to files/stdout with anything!
3557 f.write("just a string");
3558 f.write("string with arg: ", 47);
3559 f.write(SystemToString());
3560 f.write(TrustedToString());
3561 f.write(SafeToString());
3563 write("just a string");
3564 write("string with arg: ", 47);
3565 write(SystemToString());
3566 write(TrustedToString());
3567 write(SafeToString());
3569 f.writeln("just a string");
3570 f.writeln("string with arg: ", 47);
3571 f.writeln(SystemToString());
3572 f.writeln(TrustedToString());
3573 f.writeln(SafeToString());
3575 writeln("just a string");
3576 writeln("string with arg: ", 47);
3577 writeln(SystemToString());
3578 writeln(TrustedToString());
3579 writeln(SafeToString());
3581 f.writef("string with arg: %s", 47);
3582 f.writef("%s", SystemToString());
3583 f.writef("%s", TrustedToString());
3584 f.writef("%s", SafeToString());
3586 writef("string with arg: %s", 47);
3587 writef("%s", SystemToString());
3588 writef("%s", TrustedToString());
3589 writef("%s", SafeToString());
3591 f.writefln("string with arg: %s", 47);
3592 f.writefln("%s", SystemToString());
3593 f.writefln("%s", TrustedToString());
3594 f.writefln("%s", SafeToString());
3596 writefln("string with arg: %s", 47);
3597 writefln("%s", SystemToString());
3598 writefln("%s", TrustedToString());
3599 writefln("%s", SafeToString());
3603 @safe void safeTests()
3607 //safe code can write to files only with @safe and @trusted code...
3610 f.write("just a string");
3611 f.write("string with arg: ", 47);
3612 f.write(TrustedToString());
3613 f.write(SafeToString());
3615 write("just a string");
3616 write("string with arg: ", 47);
3617 write(TrustedToString());
3618 write(SafeToString());
3620 f.writeln("just a string");
3621 f.writeln("string with arg: ", 47);
3622 f.writeln(TrustedToString());
3623 f.writeln(SafeToString());
3625 writeln("just a string");
3626 writeln("string with arg: ", 47);
3627 writeln(TrustedToString());
3628 writeln(SafeToString());
3630 f.writef("string with arg: %s", 47);
3631 f.writef("%s", TrustedToString());
3632 f.writef("%s", SafeToString());
3634 writef("string with arg: %s", 47);
3635 writef("%s", TrustedToString());
3636 writef("%s", SafeToString());
3638 f.writefln("string with arg: %s", 47);
3639 f.writefln("%s", TrustedToString());
3640 f.writefln("%s", SafeToString());
3642 writefln("string with arg: %s", 47);
3643 writefln("%s", TrustedToString());
3644 writefln("%s", SafeToString());
3647 static assert(!__traits(compiles, f.write(SystemToString().toString())));
3648 static assert(!__traits(compiles, f.writeln(SystemToString())));
3649 static assert(!__traits(compiles, f.writef("%s", SystemToString())));
3650 static assert(!__traits(compiles, f.writefln("%s", SystemToString())));
3652 static assert(!__traits(compiles, write(SystemToString().toString())));
3653 static assert(!__traits(compiles, writeln(SystemToString())));
3654 static assert(!__traits(compiles, writef("%s", SystemToString())));
3655 static assert(!__traits(compiles, writefln("%s", SystemToString())));
3664 import std.exception : collectException;
3665 static import std.file;
3667 auto deleteme = testFilename();
3668 scope(exit) collectException(std.file.remove(deleteme));
3669 std.file.write(deleteme, "1 2 3");
3670 auto f = File(deleteme);
3671 assert(f.size == 5);
3672 assert(f.tell == 0);
3677 static import std.file;
3678 import std.range : chain, only, repeat;
3679 import std.range.primitives : isOutputRange;
3681 auto deleteme = testFilename();
3682 scope(exit) std.file.remove(deleteme);
3685 auto writer = File(deleteme, "w").lockingTextWriter();
3686 static assert(isOutputRange!(typeof(writer), dchar));
3691 writer.put(chain(only('本'), only('語')));
3692 // https://issues.dlang.org/show_bug.cgi?id=11945
3693 writer.put(repeat('#', 12));
3694 // https://issues.dlang.org/show_bug.cgi?id=17229
3695 writer.put(cast(immutable(ubyte)[])"日本語");
3697 assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語");
3700 @safe unittest // wchar -> char
3702 static import std.file;
3703 import std.exception : assertThrown;
3704 import std.utf : UTFException;
3706 auto deleteme = testFilename();
3707 scope(exit) std.file.remove(deleteme);
3710 auto writer = File(deleteme, "w").lockingTextWriter();
3711 writer.put("\U0001F608"w);
3713 assert(std.file.readText!string(deleteme) == "\U0001F608");
3715 // Test invalid input: unpaired high surrogate
3717 immutable wchar surr = "\U0001F608"w[0];
3718 auto f = File(deleteme, "w");
3719 assertThrown!UTFException(() {
3720 auto writer = f.lockingTextWriter();
3723 assertThrown!UTFException(writer.put(char('y')));
3724 assertThrown!UTFException(writer.put(wchar('y')));
3725 assertThrown!UTFException(writer.put(dchar('y')));
3726 assertThrown!UTFException(writer.put(surr));
3727 // First `surr` is still unpaired at this point. `writer` gets
3728 // destroyed now, and the destructor throws a UTFException for
3729 // the unpaired surrogate.
3732 assert(std.file.readText!string(deleteme) == "x");
3734 // Test invalid input: unpaired low surrogate
3736 immutable wchar surr = "\U0001F608"w[1];
3737 auto writer = File(deleteme, "w").lockingTextWriter();
3738 assertThrown!UTFException(writer.put(surr));
3740 assertThrown!UTFException(writer.put(surr));
3742 assert(std.file.readText!string(deleteme) == "y");
3745 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18801
3747 static import std.file;
3748 import std.string : stripLeft;
3750 auto deleteme = testFilename();
3751 scope(exit) std.file.remove(deleteme);
3754 auto writer = File(deleteme, "w,ccs=UTF-8").lockingTextWriter();
3757 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foo");
3760 auto writer = File(deleteme, "a,ccs=UTF-8").lockingTextWriter();
3763 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foobar");
3765 @safe unittest // char/wchar -> wchar_t
3767 import core.stdc.locale : LC_CTYPE, setlocale;
3768 import core.stdc.wchar_ : fwide;
3769 import core.stdc.string : strlen;
3770 import std.algorithm.searching : any, endsWith;
3771 import std.conv : text;
3772 import std.meta : AliasSeq;
3773 import std.string : fromStringz, stripLeft;
3774 static import std.file;
3775 auto deleteme = testFilename();
3776 scope(exit) std.file.remove(deleteme);
3777 const char* oldCt = () @trusted {
3778 const(char)* p = setlocale(LC_CTYPE, null);
3779 // Subsequent calls to `setlocale` might invalidate this return value,
3781 // See: https://github.com/dlang/phobos/pull/7660
3782 return p ? p[0 .. strlen(p) + 1].idup.ptr : null;
3784 const utf8 = ["en_US.UTF-8", "C.UTF-8", ".65001"].any!((loc) @trusted {
3785 return setlocale(LC_CTYPE, loc.ptr).fromStringz.endsWith(loc);
3787 scope(exit) () @trusted { setlocale(LC_CTYPE, oldCt); } ();
3788 alias strs = AliasSeq!("xä\U0001F607", "yö\U0001F608"w);
3790 auto f = File(deleteme, "w");
3791 version (CRuntime_Microsoft)
3793 () @trusted { _setmode(fileno(f.getFP()), _O_U8TEXT); } ();
3797 assert(fwide(f.getFP(), 1) == 1);
3799 auto writer = f.lockingTextWriter();
3800 assert(writer.orientation_ == 1);
3801 static foreach (s; strs) writer.put(s);
3803 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") ==
3806 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18789
3808 static import std.file;
3809 auto deleteme = testFilename();
3810 scope(exit) std.file.remove(deleteme);
3811 // converting to char
3813 auto f = File(deleteme, "w");
3814 f.writeln("\U0001F608"w); // UTFException
3816 // converting to wchar_t
3818 auto f = File(deleteme, "w,ccs=UTF-16LE");
3820 f.writeln("ö"); // writes garbage
3821 f.writeln("\U0001F608"); // ditto
3823 f.writeln("\U0001F608"w); // leads to ErrnoException
3829 import std.exception : collectException;
3830 auto e = collectException({ File f; f.writeln("Hello!"); }());
3831 assert(e && e.msg == "Attempting to write to closed File");
3834 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=21592
3836 import std.exception : collectException;
3837 import std.utf : UTFException;
3838 static import std.file;
3839 auto deleteme = testFilename();
3840 scope(exit) std.file.remove(deleteme);
3841 auto f = File(deleteme, "w");
3842 auto e = collectException!UTFException(f.writeln(wchar(0xD801)));
3843 assert(e.next is null);
3846 version (StdStressTest)
3848 // https://issues.dlang.org/show_bug.cgi?id=15768
3851 import std.parallelism : parallel;
3852 import std.range : iota;
3854 auto deleteme = testFilename();
3855 stderr = File(deleteme, "w");
3857 foreach (t; 1_000_000.iota.parallel)
3859 stderr.write("aaa");
3864 /// Used to specify the lock type for `File.lock` and `File.tryLock`.
3868 * Specifies a _read (shared) lock. A _read lock denies all processes
3869 * write access to the specified region of the file, including the
3870 * process that first locks the region. All processes can _read the
3871 * locked region. Multiple simultaneous _read locks are allowed, as
3872 * long as there are no exclusive locks.
3877 * Specifies a read/write (exclusive) lock. A read/write lock denies all
3878 * other processes both read and write access to the locked file region.
3879 * If a segment has an exclusive lock, it may not have any shared locks
3880 * or other exclusive locks.
3885 struct LockingTextReader
3888 private char _front;
3889 private bool _hasChar;
3893 import std.exception : enforce;
3894 enforce(f.isOpen, "LockingTextReader: File must be open");
3896 _FLOCK(_f._p.handle);
3901 _FLOCK(_f._p.handle);
3907 ungetc(_front, cast(FILE*)_f._p.handle);
3909 // File locking has its own reference count
3910 if (_f.isOpen) _FUNLOCK(_f._p.handle);
3913 void opAssign(LockingTextReader r)
3915 import std.algorithm.mutation : swap;
3919 @property bool empty()
3923 if (!_f.isOpen || _f.eof)
3925 immutable int c = _FGETC(cast(_iobuf*) _f._p.handle);
3931 _front = cast(char) c;
3937 @property char front()
3943 import core.exception : RangeError;
3945 throw new RangeError();
3965 // @system due to readf
3966 static import std.file;
3967 import std.range.primitives : isInputRange;
3969 static assert(isInputRange!LockingTextReader);
3970 auto deleteme = testFilename();
3971 std.file.write(deleteme, "1 2 3");
3972 scope(exit) std.file.remove(deleteme);
3974 auto f = File(deleteme);
3983 // https://issues.dlang.org/show_bug.cgi?id=13686
3986 import std.algorithm.comparison : equal;
3987 static import std.file;
3988 import std.utf : byDchar;
3990 auto deleteme = testFilename();
3991 std.file.write(deleteme, "Тест");
3992 scope(exit) std.file.remove(deleteme);
3995 File(deleteme).readf("%s", &s);
3996 assert(s == "Тест");
3998 auto ltr = LockingTextReader(File(deleteme)).byDchar;
3999 assert(equal(ltr, "Тест".byDchar));
4002 // https://issues.dlang.org/show_bug.cgi?id=12320
4005 static import std.file;
4006 auto deleteme = testFilename();
4007 std.file.write(deleteme, "ab");
4008 scope(exit) std.file.remove(deleteme);
4009 auto ltr = LockingTextReader(File(deleteme));
4010 assert(ltr.front == 'a');
4012 assert(ltr.front == 'b');
4017 // https://issues.dlang.org/show_bug.cgi?id=14861
4020 // @system due to readf
4021 static import std.file;
4022 auto deleteme = testFilename();
4023 File fw = File(deleteme, "w");
4024 for (int i; i != 5000; i++)
4025 fw.writeln(i, ";", "Иванов;Пётр;Петрович");
4027 scope(exit) std.file.remove(deleteme);
4029 File fr = File(deleteme, "r");
4030 scope (exit) fr.close();
4031 int nom; string fam, nam, ot;
4032 // Error format read
4034 fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot);
4038 * Indicates whether `T` is a file handle, i.e. the type
4039 * is implicitly convertable to $(LREF File) or a pointer to a
4040 * $(REF FILE, core,stdc,stdio).
4043 * `true` if `T` is a file handle, `false` otherwise.
4045 template isFileHandle(T)
4047 enum isFileHandle = is(T : FILE*) ||
4054 static assert(isFileHandle!(FILE*));
4055 static assert(isFileHandle!(File));
4059 * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
4061 private @property File trustedStdout() @trusted
4066 /***********************************
4067 Writes its arguments in text format to standard output (without a trailing newline).
4070 args = the items to write to `stdout`
4072 Throws: In case of an I/O error, throws an `StdioException`.
4075 Reads `stdin` and writes it to `stdout` with an argument
4084 for (size_t count = 0; (line = readln) !is null; count++)
4086 write("Input ", count, ": ", line, "\n");
4091 void write(T...)(T args)
4092 if (!is(T[0] : File))
4094 trustedStdout.write(args);
4099 static import std.file;
4101 scope(failure) printf("Failed test at line %d\n", __LINE__);
4103 if (false) write(buf);
4105 auto deleteme = testFilename();
4106 auto f = File(deleteme, "w");
4107 f.write("Hello, ", "world number ", 42, "!");
4109 scope(exit) { std.file.remove(deleteme); }
4110 assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!");
4113 /***********************************
4114 * Equivalent to `write(args, '\n')`. Calling `writeln` without
4115 * arguments is valid and just prints a newline to the standard
4119 * args = the items to write to `stdout`
4122 * In case of an I/O error, throws an $(LREF StdioException).
4124 * Reads `stdin` and writes it to `stdout` with an argument
4133 for (size_t count = 0; (line = readln) !is null; count++)
4135 writeln("Input ", count, ": ", line);
4140 void writeln(T...)(T args)
4142 static if (T.length == 0)
4144 import std.exception : enforce;
4146 enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
4148 else static if (T.length == 1 &&
4149 is(T[0] : const(char)[]) &&
4150 (is(T[0] == U[], U) || __traits(isStaticArray, T[0])))
4152 // Specialization for strings - a very frequent case
4153 auto w = .trustedStdout.lockingTextWriter();
4155 static if (__traits(isStaticArray, T[0]))
4167 // Most general instance
4168 trustedStdout.write(args, '\n');
4174 // Just make sure the call compiles
4175 if (false) writeln();
4177 if (false) writeln("wyda");
4179 // https://issues.dlang.org/show_bug.cgi?id=8040
4180 if (false) writeln(null);
4181 if (false) writeln(">", null, "<");
4183 // https://issues.dlang.org/show_bug.cgi?id=14041
4197 static import std.file;
4199 scope(failure) printf("Failed test at line %d\n", __LINE__);
4202 auto deleteme = testFilename();
4203 auto f = File(deleteme, "w");
4204 scope(exit) { std.file.remove(deleteme); }
4205 f.writeln("Hello, ", "world number ", 42, "!");
4208 assert(cast(char[]) std.file.read(deleteme) ==
4209 "Hello, world number 42!\r\n");
4211 assert(cast(char[]) std.file.read(deleteme) ==
4212 "Hello, world number 42!\n");
4214 // test writeln on stdout
4215 auto saveStdout = stdout;
4216 scope(exit) stdout = saveStdout;
4217 stdout.open(deleteme, "w");
4218 writeln("Hello, ", "world number ", 42, "!");
4221 assert(cast(char[]) std.file.read(deleteme) ==
4222 "Hello, world number 42!\r\n");
4224 assert(cast(char[]) std.file.read(deleteme) ==
4225 "Hello, world number 42!\n");
4227 stdout.open(deleteme, "w");
4229 writeln("Hello!"w); // https://issues.dlang.org/show_bug.cgi?id=8386
4230 writeln("Hello!"d); // https://issues.dlang.org/show_bug.cgi?id=8386
4231 writeln("embedded\0null"c); // https://issues.dlang.org/show_bug.cgi?id=8730
4234 assert(cast(char[]) std.file.read(deleteme) ==
4235 "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
4237 assert(cast(char[]) std.file.read(deleteme) ==
4238 "Hello!\nHello!\nHello!\nembedded\0null\n");
4243 static import std.file;
4245 auto deleteme = testFilename();
4246 auto f = File(deleteme, "w");
4247 scope(exit) { std.file.remove(deleteme); }
4249 enum EI : int { A, B }
4250 enum ED : double { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4251 enum EC : char { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4252 enum ES : string { A = "aaa", B = "bbb" }
4254 f.writeln(EI.A); // false, but A on 2.058
4255 f.writeln(EI.B); // true, but B on 2.058
4257 f.writeln(ED.A); // A
4258 f.writeln(ED.B); // B
4260 f.writeln(EC.A); // A
4261 f.writeln(EC.B); // B
4263 f.writeln(ES.A); // A
4264 f.writeln(ES.B); // B
4268 assert(cast(char[]) std.file.read(deleteme) ==
4269 "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
4271 assert(cast(char[]) std.file.read(deleteme) ==
4272 "A\nB\nA\nB\nA\nB\nA\nB\n");
4277 static auto useInit(T)(T ltw)
4284 useInit(stdout.lockingTextWriter());
4289 // https://issues.dlang.org/show_bug.cgi?id=21920
4290 void function(string) printer = &writeln!string;
4291 if (false) printer("Hello");
4295 /***********************************
4296 Writes formatted data to standard output (without a trailing newline).
4299 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4300 When passed as a compile-time argument, the string will be statically checked
4301 against the argument types passed.
4302 args = Items to write.
4304 Note: In older versions of Phobos, it used to be possible to write:
4307 writef(stderr, "%s", "message");
4310 to print a message to `stderr`. This syntax is no longer supported, and has
4314 stderr.writef("%s", "message");
4318 void writef(alias fmt, A...)(A args)
4319 if (isSomeString!(typeof(fmt)))
4321 import std.format : checkFormatException;
4323 alias e = checkFormatException!(fmt, A);
4324 static assert(!e, e);
4325 return .writef(fmt, args);
4329 void writef(Char, A...)(in Char[] fmt, A args)
4331 trustedStdout.writef(fmt, args);
4336 static import std.file;
4338 scope(failure) printf("Failed test at line %d\n", __LINE__);
4341 auto deleteme = testFilename();
4342 auto f = File(deleteme, "w");
4343 scope(exit) { std.file.remove(deleteme); }
4344 f.writef!"Hello, %s world number %s!"("nice", 42);
4346 assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
4347 // test write on stdout
4348 auto saveStdout = stdout;
4349 scope(exit) stdout = saveStdout;
4350 stdout.open(deleteme, "w");
4351 writef!"Hello, %s world number %s!"("nice", 42);
4353 assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
4356 /***********************************
4357 * Equivalent to $(D writef(fmt, args, '\n')).
4359 void writefln(alias fmt, A...)(A args)
4360 if (isSomeString!(typeof(fmt)))
4362 import std.format : checkFormatException;
4364 alias e = checkFormatException!(fmt, A);
4365 static assert(!e, e);
4366 return .writefln(fmt, args);
4370 void writefln(Char, A...)(in Char[] fmt, A args)
4372 trustedStdout.writefln(fmt, args);
4377 static import std.file;
4379 scope(failure) printf("Failed test at line %d\n", __LINE__);
4381 // test File.writefln
4382 auto deleteme = testFilename();
4383 auto f = File(deleteme, "w");
4384 scope(exit) { std.file.remove(deleteme); }
4385 f.writefln!"Hello, %s world number %s!"("nice", 42);
4388 assert(cast(char[]) std.file.read(deleteme) ==
4389 "Hello, nice world number 42!\r\n");
4391 assert(cast(char[]) std.file.read(deleteme) ==
4392 "Hello, nice world number 42!\n",
4393 cast(char[]) std.file.read(deleteme));
4396 auto saveStdout = stdout;
4397 scope(exit) stdout = saveStdout;
4398 stdout.open(deleteme, "w");
4399 writefln!"Hello, %s world number %s!"("nice", 42);
4402 assert(cast(char[]) std.file.read(deleteme) ==
4403 "Hello, nice world number 42!\r\n");
4405 assert(cast(char[]) std.file.read(deleteme) ==
4406 "Hello, nice world number 42!\n");
4410 * Reads formatted data from `stdin` using $(REF formattedRead, std,_format).
4412 * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4413 * When passed as a compile-time argument, the string will be statically checked
4414 * against the argument types passed.
4415 * args = Items to be read.
4417 * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
4418 * this number will be less than the number of variables provided.
4434 % echo "1 2 3" | rdmd test.d
4440 uint readf(alias format, A...)(auto ref A args)
4441 if (isSomeString!(typeof(format)))
4443 import std.format : checkFormatException;
4445 alias e = checkFormatException!(format, A);
4446 static assert(!e, e);
4447 return .readf(format, args);
4451 uint readf(A...)(scope const(char)[] format, auto ref A args)
4453 return stdin.readf(format, args);
4459 if (false) readf("%s", &f);
4464 if (false) readf("%s %s %s", a, b, c);
4465 // backwards compatibility with pointers
4466 if (false) readf("%s %s %s", a, &b, c);
4467 if (false) readf("%s %s %s", &a, &b, &c);
4470 /**********************************
4471 * Read line from `stdin`.
4473 * This version manages its own read buffer, which means one memory allocation per call. If you are not
4474 * retaining a reference to the read data, consider the `readln(buf)` version, which may offer
4475 * better performance as it can reuse its read buffer.
4478 * The line that was read, including the line terminator character.
4480 * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
4481 * terminator = Line terminator (by default, `'\n'`).
4483 * String terminators are not supported due to ambiguity with readln(buf) below.
4485 * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4487 * Reads `stdin` and writes it to `stdout`.
4494 while ((line = readln()) !is null)
4499 S readln(S = string)(dchar terminator = '\n')
4502 return stdin.readln!S(terminator);
4505 /**********************************
4506 * Read line from `stdin` and write it to buf[], including terminating character.
4508 * This can be faster than $(D line = readln()) because you can reuse
4509 * the buffer for each call. Note that reusing the buffer means that you
4510 * must copy the previous contents if you wish to retain them.
4513 * `size_t` 0 for end of file, otherwise number of characters read
4515 * buf = Buffer used to store the resulting line data. buf is resized as necessary.
4516 * terminator = Line terminator (by default, `'\n'`). Use $(REF newline, std,ascii)
4517 * for portability (unless the file was opened in text mode).
4519 * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4521 * Reads `stdin` and writes it to `stdout`.
4533 size_t readln(C)(ref C[] buf, dchar terminator = '\n')
4534 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
4536 return stdin.readln(buf, terminator);
4540 size_t readln(C, R)(ref C[] buf, R terminator)
4541 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
4542 isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
4544 return stdin.readln(buf, terminator);
4549 import std.meta : AliasSeq;
4551 //we can't actually test readln, so at the very least,
4552 //we test compilability
4557 static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
4560 readln!String('\t');
4562 static foreach (String; AliasSeq!(char[], wchar[], dchar[]))
4567 readln(buf, "<br />");
4573 Reads a line from `stdin` and parses it using $(REF formattedRead, std,format,read).
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.
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
4592 while (readfln("%d %d %d", a, b, c) == 3)
4599 % cat << EOF > input
4604 % rdmd sum_rows.d < input
4610 uint readfln(alias format, Data...)(auto ref Data data)
4612 import std.format : checkFormatException;
4614 alias e = checkFormatException!(format, Data);
4615 static assert(!e, e);
4616 return .readfln(format, data);
4620 uint readfln(Data...)(scope const(char)[] format, auto ref Data data)
4622 return stdin.readfln(format, data);
4631 if (false) readfln("%f %s %c %d", f, s, c, n);
4632 if (false) readfln!"%f %s %c %d"(f, s, c, n);
4637 * Convenience function that forwards to `core.sys.posix.stdio.fopen`
4638 * (to `_wfopen` on Windows)
4639 * with appropriately-constructed C-style strings.
4641 private FILE* _fopen(R1, R2)(R1 name, R2 mode = "r")
4642 if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
4643 (isSomeFiniteCharInputRange!R2 || isSomeString!R2))
4645 import std.internal.cstring : tempCString;
4647 auto namez = name.tempCString!FSChar();
4648 auto modez = mode.tempCString!FSChar();
4650 static _fopenImpl(scope const(FSChar)* namez, scope const(FSChar)* modez) @trusted nothrow @nogc
4654 return _wfopen(namez, modez);
4656 else version (Posix)
4659 * The new opengroup large file support API is transparently
4660 * included in the normal C bindings. https://www.opengroup.org/platform/lfs.html#1.0
4661 * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
4662 * the normal functions work fine. If not, then large file support
4663 * probably isn't available. Do not use the old transitional API
4664 * (the native extern(C) fopen64, https://unix.org/version2/whatsnew/lfs20mar.html#3.0)
4666 import core.sys.posix.stdio : fopen;
4667 return fopen(namez, modez);
4671 return fopen(namez, modez);
4674 return _fopenImpl(namez, modez);
4679 /***********************************
4680 * Convenience function that forwards to `core.sys.posix.stdio.popen`
4681 * with appropriately-constructed C-style strings.
4683 FILE* _popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
4684 if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
4685 (isSomeFiniteCharInputRange!R2 || isSomeString!R2))
4687 import std.internal.cstring : tempCString;
4689 auto namez = name.tempCString!FSChar();
4690 auto modez = mode.tempCString!FSChar();
4692 static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
4694 import core.sys.posix.stdio : popen;
4695 return popen(namez, modez);
4697 return popenImpl(namez, modez);
4702 * Convenience function that forwards to `core.stdc.stdio.fwrite`
4704 private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
4706 return fwrite(obj.ptr, T.sizeof, obj.length, f);
4710 * Convenience function that forwards to `core.stdc.stdio.fread`
4712 private auto trustedFread(T)(FILE* f, T[] obj) @trusted
4713 if (!imported!"std.traits".hasIndirections!T)
4715 return fread(obj.ptr, T.sizeof, obj.length, f);
4718 private auto trustedFread(T)(FILE* f, T[] obj) @system
4719 if (imported!"std.traits".hasIndirections!T)
4721 return fread(obj.ptr, T.sizeof, obj.length, f);
4725 * Iterates through the lines of a file by using `foreach`.
4732 foreach (string line; lines(stdin))
4738 The line terminator (`'\n'` by default) is part of the string read (it
4739 could be missing in the last line of the file). Several types are
4740 supported for `line`, and the behavior of `lines`
4741 changes accordingly:
4743 $(OL $(LI If `line` has type `string`, $(D
4744 wstring), or `dstring`, a new string of the respective type
4745 is allocated every read.) $(LI If `line` has type $(D
4746 char[]), `wchar[]`, `dchar[]`, the line's content
4747 will be reused (overwritten) across reads.) $(LI If `line`
4748 has type `immutable(ubyte)[]`, the behavior is similar to
4749 case (1), except that no UTF checking is attempted upon input.) $(LI
4750 If `line` has type `ubyte[]`, the behavior is
4751 similar to case (2), except that no UTF checking is attempted upon
4754 In all cases, a two-symbols versions is also accepted, in which case
4755 the first symbol (of integral type, e.g. `ulong` or $(D
4756 uint)) tracks the zero-based number of the current line.
4760 foreach (ulong i, string line; lines(stdin))
4766 In case of an I/O error, an `StdioException` is thrown.
4775 private dchar terminator = '\n';
4780 f = File to read lines from.
4781 terminator = Line separator (`'\n'` by default).
4783 this(File f, dchar terminator = '\n') @safe
4786 this.terminator = terminator;
4789 int opApply(D)(scope D dg)
4791 import std.traits : Parameters;
4792 alias Parms = Parameters!(dg);
4793 static if (isSomeString!(Parms[$ - 1]))
4796 static if (is(Parms[$ - 1] : const(char)[]))
4798 else static if (is(Parms[$ - 1] : const(wchar)[]))
4800 else static if (is(Parms[$ - 1] : const(dchar)[]))
4803 static if (Parms.length == 2)
4807 import std.conv : to;
4809 if (!f.readln(line, terminator)) break;
4810 auto copy = to!(Parms[$ - 1])(line);
4811 static if (Parms.length == 2)
4813 result = dg(i, copy);
4820 if (result != 0) break;
4827 return opApplyRaw(dg);
4831 int opApplyRaw(D)(scope D dg)
4833 import std.conv : to;
4834 import std.exception : assumeUnique;
4835 import std.traits : Parameters;
4837 alias Parms = Parameters!(dg);
4838 enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
4841 _FLOCK(f._p.handle);
4842 scope(exit) _FUNLOCK(f._p.handle);
4844 static if (Parms.length == 2)
4846 while ((c = _FGETC(cast(_iobuf*) f._p.handle)) != -1)
4848 buffer ~= to!(ubyte)(c);
4849 if (c == terminator)
4851 static if (duplicate)
4852 auto arg = assumeUnique(buffer);
4855 // unlock the file while calling the delegate
4856 _FUNLOCK(f._p.handle);
4857 scope(exit) _FLOCK(f._p.handle);
4858 static if (Parms.length == 1)
4864 result = dg(line, arg);
4868 static if (!duplicate)
4872 // can only reach when _FGETC returned -1
4873 if (!f.eof) throw new StdioException("Error in reading file"); // error occured
4881 As pointed out in <https://github.com/dlang/phobos/issues/10605>,
4882 it's a pity that `byLine()` & co. aren't @safe to use yet.
4884 This is a first attempt at working towards that goal.
4885 For now, this test doesn't do much; as there isn't much to do safely yet.
4887 auto deleteMe = testFilename();
4888 scope(exit) { imported!"std.file".remove(deleteMe); }
4892 auto f = File(deleteMe, "w");
4893 scope(exit) { f.close(); }
4894 foreach (i; 1 .. 11)
4900 auto f = File(deleteMe, "r");
4901 scope(exit) { f.close(); }
4903 auto myLines = lines(f);
4904 foreach (string line; myLines)
4910 auto f = File(deleteMe, "r");
4911 scope(exit) { f.close(); }
4913 auto myByLineCopy = f.byLineCopy;
4914 foreach (line; myByLineCopy)
4919 auto f = File(deleteMe, "r");
4920 scope(exit) { f.close(); }
4922 auto myByLine = f.byLine;
4923 foreach (line; myByLine)
4930 static import std.file;
4931 import std.meta : AliasSeq;
4933 scope(failure) printf("Failed test at line %d\n", __LINE__);
4935 auto deleteme = testFilename();
4936 scope(exit) { std.file.remove(deleteme); }
4939 AliasSeq!(string, wstring, dstring,
4940 char[], wchar[], dchar[]);
4941 foreach (T; TestedWith)
4943 // test looping with an empty file
4944 std.file.write(deleteme, "");
4945 auto f = File(deleteme, "r");
4946 foreach (T line; lines(f))
4952 // test looping with a file with three lines
4953 std.file.write(deleteme, "Line one\nline two\nline three\n");
4954 f.open(deleteme, "r");
4956 foreach (T line; lines(f))
4958 if (i == 0) assert(line == "Line one\n");
4959 else if (i == 1) assert(line == "line two\n");
4960 else if (i == 2) assert(line == "line three\n");
4966 // test looping with a file with three lines, last without a newline
4967 std.file.write(deleteme, "Line one\nline two\nline three");
4968 f.open(deleteme, "r");
4970 foreach (T line; lines(f))
4972 if (i == 0) assert(line == "Line one\n");
4973 else if (i == 1) assert(line == "line two\n");
4974 else if (i == 2) assert(line == "line three");
4981 // test with ubyte[] inputs
4982 alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]);
4983 foreach (T; TestedWith2)
4985 // test looping with an empty file
4986 std.file.write(deleteme, "");
4987 auto f = File(deleteme, "r");
4988 foreach (T line; lines(f))
4994 // test looping with a file with three lines
4995 std.file.write(deleteme, "Line one\nline two\nline three\n");
4996 f.open(deleteme, "r");
4998 foreach (T line; lines(f))
5000 if (i == 0) assert(cast(char[]) line == "Line one\n");
5001 else if (i == 1) assert(cast(char[]) line == "line two\n",
5002 T.stringof ~ " " ~ cast(char[]) line);
5003 else if (i == 2) assert(cast(char[]) line == "line three\n");
5009 // test looping with a file with three lines, last without a newline
5010 std.file.write(deleteme, "Line one\nline two\nline three");
5011 f.open(deleteme, "r");
5013 foreach (T line; lines(f))
5015 if (i == 0) assert(cast(char[]) line == "Line one\n");
5016 else if (i == 1) assert(cast(char[]) line == "line two\n");
5017 else if (i == 2) assert(cast(char[]) line == "line three");
5025 static foreach (T; AliasSeq!(ubyte[]))
5027 // test looping with a file with three lines, last without a newline
5028 // using a counter too this time
5029 std.file.write(deleteme, "Line one\nline two\nline three");
5030 auto f = File(deleteme, "r");
5032 foreach (ulong j, T line; lines(f))
5034 if (i == 0) assert(cast(char[]) line == "Line one\n");
5035 else if (i == 1) assert(cast(char[]) line == "line two\n");
5036 else if (i == 2) assert(cast(char[]) line == "line three");
5045 Iterates through a file a chunk at a time by using `foreach`.
5052 foreach (ubyte[] buffer; chunks(stdin, 4096))
5059 The content of `buffer` is reused across calls. In the
5060 example above, `buffer.length` is 4096 for all iterations,
5061 except for the last one, in which case `buffer.length` may
5062 be less than 4096 (but always greater than zero).
5064 In case of an I/O error, an `StdioException` is thrown.
5066 auto chunks(File f, size_t size)
5068 return ChunksImpl(f, size);
5070 private struct ChunksImpl
5073 private size_t size;
5074 // private string fileName; // Currently, no use
5076 this(File f, size_t size)
5079 assert(size, "size must be larger than 0");
5087 int opApply(D)(scope D dg)
5089 import core.stdc.stdlib : alloca;
5090 import std.exception : enforce;
5092 enforce(f.isOpen, "Attempting to read from an unopened file");
5093 enum maxStackSize = 1024 * 16;
5094 ubyte[] buffer = void;
5095 if (size < maxStackSize)
5096 buffer = (cast(ubyte*) alloca(size))[0 .. size];
5098 buffer = new ubyte[size];
5102 while ((r = trustedFread(f._p.handle, buffer)) > 0)
5108 if (!f.eof) throw new StdioException(null);
5111 static if (is(typeof(dg(tally, buffer))))
5113 if ((result = dg(tally, buffer)) != 0) break;
5117 if ((result = dg(buffer)) != 0) break;
5127 static import std.file;
5129 scope(failure) printf("Failed test at line %d\n", __LINE__);
5131 auto deleteme = testFilename();
5132 scope(exit) { std.file.remove(deleteme); }
5134 // test looping with an empty file
5135 std.file.write(deleteme, "");
5136 auto f = File(deleteme, "r");
5137 foreach (ubyte[] line; chunks(f, 4))
5143 // test looping with a file with three lines
5144 std.file.write(deleteme, "Line one\nline two\nline three\n");
5145 f = File(deleteme, "r");
5147 foreach (ubyte[] line; chunks(f, 3))
5149 if (i == 0) assert(cast(char[]) line == "Lin");
5150 else if (i == 1) assert(cast(char[]) line == "e o");
5151 else if (i == 2) assert(cast(char[]) line == "ne\n");
5158 // Issue 21730 - null ptr dereferenced in ChunksImpl.opApply (SIGSEGV)
5161 import std.exception : assertThrown;
5162 static import std.file;
5164 auto deleteme = testFilename();
5165 scope(exit) { if (std.file.exists(deleteme)) std.file.remove(deleteme); }
5167 auto err1 = File(deleteme, "w+x");
5169 std.file.remove(deleteme);
5170 assertThrown(() {foreach (ubyte[] buf; chunks(err1, 4096)) {}}());
5174 Writes an array or range to a file.
5175 Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
5176 Similar to $(REF write, std,file), strings are written as-is,
5177 rather than encoded according to the `File`'s $(HTTP
5178 en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
5181 void toFile(T)(T data, string fileName)
5182 if (is(typeof(copy(data, stdout.lockingBinaryWriter))))
5184 copy(data, File(fileName, "wb").lockingBinaryWriter);
5189 static import std.file;
5191 auto deleteme = testFilename();
5192 scope(exit) { std.file.remove(deleteme); }
5194 "Test".toFile(deleteme);
5195 assert(std.file.readText(deleteme) == "Test");
5198 /*********************
5199 * Thrown if I/O errors happen.
5201 class StdioException : Exception
5203 static import core.stdc.errno;
5204 /// Operating system error code.
5208 Initialize with a message and an error code.
5210 this(string message, uint e = core.stdc.errno.errno) @trusted
5212 import std.exception : errnoString;
5214 auto sysmsg = errnoString(errno);
5215 // If e is 0, we don't use the system error message. (The message
5216 // is "Success", which is rather pointless for an exception.)
5217 super(e == 0 ? message
5218 : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
5221 /** Convenience functions that throw an `StdioException`. */
5222 static void opCall(string msg) @safe
5224 throw new StdioException(msg);
5228 static void opCall() @safe
5230 throw new StdioException(null, core.stdc.errno.errno);
5234 enum StdFileHandle: string
5236 stdin = "core.stdc.stdio.stdin",
5237 stdout = "core.stdc.stdio.stdout",
5238 stderr = "core.stdc.stdio.stderr",
5241 // Undocumented but public because the std* handles are aliasing it.
5242 @property ref File makeGlobal(StdFileHandle _iob)()
5244 __gshared File.Impl impl;
5245 __gshared File result;
5247 // Use an inline spinlock to make sure the initializer is only run once.
5248 // We assume there will be at most uint.max / 2 threads trying to initialize
5249 // `handle` at once and steal the high bit to indicate that the globals have
5250 // been initialized.
5251 static shared uint spinlock;
5252 import core.atomic : atomicLoad, atomicOp, MemoryOrder;
5253 if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
5257 if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2)
5259 if (atomicOp!"+="(spinlock, 1) == 1)
5261 with (StdFileHandle)
5262 assert(_iob == stdin || _iob == stdout || _iob == stderr);
5263 impl.handle = cast() mixin(_iob);
5265 atomicOp!"+="(spinlock, uint.max / 2);
5268 atomicOp!"-="(spinlock, 1);
5274 /** The standard input stream.
5277 stdin as a $(LREF File).
5280 The returned $(LREF File) wraps $(REF stdin,core,stdc,stdio), and
5281 is therefore thread global. Reassigning `stdin` to a different
5282 `File` must be done in a single-threaded or locked context in
5283 order to avoid race conditions.
5285 All reading from `stdin` automatically locks the file globally,
5286 and will cause all other threads calling `read` to wait until
5287 the lock is released.
5289 alias stdin = makeGlobal!(StdFileHandle.stdin);
5294 // Read stdin, sort lines, write to stdout
5295 import std.algorithm.mutation : copy;
5296 import std.algorithm.sorting : sort;
5297 import std.array : array;
5298 import std.typecons : Yes;
5302 stdin // read from stdin
5303 .byLineCopy(Yes.keepTerminator) // copying each line
5304 .array() // convert to array of lines
5305 .sort() // sort the lines
5306 .copy( // copy output of .sort to an OutputRange
5307 stdout.lockingTextWriter()); // the OutputRange
5312 The standard output stream.
5315 stdout as a $(LREF File).
5318 The returned $(LREF File) wraps $(REF stdout,core,stdc,stdio), and
5319 is therefore thread global. Reassigning `stdout` to a different
5320 `File` must be done in a single-threaded or locked context in
5321 order to avoid race conditions.
5323 All writing to `stdout` automatically locks the file globally,
5324 and will cause all other threads calling `write` to wait until
5325 the lock is released.
5327 alias stdout = makeGlobal!(StdFileHandle.stdout);
5334 stdout.writeln("Write a message to stdout.");
5343 import std.algorithm.iteration : filter, map, sum;
5344 import std.format : format;
5345 import std.range : iota, tee;
5349 .filter!(a => a % 2) // 1 3 5
5350 .map!(a => a * 2) // 2 6 10
5351 .tee!(_ => stdout.writefln("len: %d", len++))
5363 import std.algorithm.mutation : copy;
5364 import std.algorithm.iteration : map;
5365 import std.format : format;
5366 import std.range : iota;
5369 .map!(e => "N: %d".format(e))
5370 .copy(stdout.lockingTextWriter()); // the OutputRange
5375 The standard error stream.
5378 stderr as a $(LREF File).
5381 The returned $(LREF File) wraps $(REF stderr,core,stdc,stdio), and
5382 is therefore thread global. Reassigning `stderr` to a different
5383 `File` must be done in a single-threaded or locked context in
5384 order to avoid race conditions.
5386 All writing to `stderr` automatically locks the file globally,
5387 and will cause all other threads calling `write` to wait until
5388 the lock is released.
5390 alias stderr = makeGlobal!(StdFileHandle.stderr);
5397 stderr.writeln("Write a message to stderr.");
5403 static import std.file;
5404 import std.typecons : tuple;
5406 scope(failure) printf("Failed test at line %d\n", __LINE__);
5407 auto deleteme = testFilename();
5409 std.file.write(deleteme, "1 2\n4 1\n5 100");
5410 scope(exit) std.file.remove(deleteme);
5412 File f = File(deleteme);
5413 scope(exit) f.close();
5414 auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
5416 foreach (e; f.byRecord!(int, int)("%s %s"))
5419 assert(e == t[i++]);
5427 // Retain backwards compatibility
5428 // https://issues.dlang.org/show_bug.cgi?id=17472
5429 static assert(is(typeof(stdin) == File));
5430 static assert(is(typeof(stdout) == File));
5431 static assert(is(typeof(stderr) == File));
5434 // roll our own appender, but with "safe" arrays
5435 private struct ReadlnAppender
5439 bool safeAppend = false;
5441 void initialize(char[] b) @safe
5446 @property char[] data() @trusted
5449 assumeSafeAppend(buf.ptr[0 .. pos]);
5450 return buf.ptr[0 .. pos];
5453 bool reserveWithoutAllocating(size_t n)
5455 if (buf.length >= pos + n) // buf is already large enough
5458 immutable curCap = buf.capacity;
5459 if (curCap >= pos + n)
5461 buf.length = curCap;
5462 /* Any extra capacity we end up not using can safely be claimed
5470 void reserve(size_t n) @trusted
5472 import core.stdc.string : memcpy;
5473 if (!reserveWithoutAllocating(n))
5475 size_t ncap = buf.length * 2 + 128 + n;
5476 char[] nbuf = new char[ncap];
5477 memcpy(nbuf.ptr, buf.ptr, pos);
5479 // Allocated a new buffer. No one else knows about it.
5483 void putchar(char c) @trusted
5488 void putdchar(dchar dc) @trusted
5490 import std.utf : encode, UseReplacementDchar;
5493 immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc);
5498 void putonly(const char[] b) @trusted
5500 import core.stdc.string : memcpy;
5501 assert(pos == 0); // assume this is the only put call
5502 if (reserveWithoutAllocating(b.length))
5503 memcpy(buf.ptr + pos, b.ptr, b.length);
5510 private struct LockedFile
5512 private @system _iobuf* fp;
5514 this(FILE* fps) @trusted
5517 // Since fps is now locked, we can cast away shared
5518 fp = cast(_iobuf*) fps;
5522 @disable this(this);
5523 @disable void opAssign(LockedFile);
5525 // these use unlocked fgetc calls
5526 @trusted fgetc() { return _FGETC(fp); }
5527 @trusted fgetwc() { return _FGETWC(fp); }
5531 _FUNLOCK(cast(FILE*) fp);
5540 auto lf = LockedFile(fps);
5541 static assert(!__traits(compiles, lf = LockedFile(fps)));
5542 version (ShouldFail)
5544 lf.fps = null; // error with -preview=systemVariables
5549 // Private implementation of readln
5550 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) @safe
5552 version (CRuntime_Microsoft)
5554 auto lf = LockedFile(fps);
5557 app.initialize(buf);
5560 while ((c = lf.fgetc()) != -1)
5562 app.putchar(cast(char) c);
5563 if (c == terminator)
5576 else static if (__traits(compiles, core.sys.posix.stdio.getdelim))
5578 if (orientation == File.Orientation.wide)
5580 import core.stdc.wchar_ : fwide;
5582 auto lf = LockedFile(fps);
5583 /* Stream is in wide characters.
5584 * Read them and convert to chars.
5589 for (int c = void; (c = lf.fgetwc()) != -1; )
5591 if ((c & ~0x7F) == 0)
5593 if (c == terminator)
5598 if (c >= 0xD800 && c <= 0xDBFF)
5601 if ((c2 = lf.fgetwc()) != -1 ||
5602 c2 < 0xDC00 && c2 > 0xDFFF)
5604 StdioException("unpaired UTF-16 surrogate");
5606 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5608 import std.utf : encode;
5616 else version (Posix)
5619 for (int c; (c = lf.fgetwc()) != -1; )
5621 import std.utf : encode;
5623 if ((c & ~0x7F) == 0)
5624 buf ~= cast(char) c;
5626 encode(buf, cast(dchar) c);
5627 if (c == terminator)
5639 return () @trusted {
5640 import core.stdc.stdlib : free;
5642 static char *lineptr = null;
5643 static size_t n = 0;
5648 // Bound memory used by readln
5655 const s = core.sys.posix.stdio.getdelim(&lineptr, &n, terminator, fps);
5660 buf.length = 0; // end of file
5664 const line = lineptr[0 .. s];
5665 if (s <= buf.length)
5677 else // version (NO_GETDELIM)
5679 import core.stdc.wchar_ : fwide;
5681 auto lf = LockedFile(fps);
5682 if (orientation == File.Orientation.wide)
5684 /* Stream is in wide characters.
5685 * Read them and convert to chars.
5690 for (int c; (c = lf.fgetwc()) != -1; )
5692 if ((c & ~0x7F) == 0)
5694 if (c == terminator)
5699 if (c >= 0xD800 && c <= 0xDBFF)
5702 if ((c2 = lf.fgetwc()) != -1 ||
5703 c2 < 0xDC00 && c2 > 0xDFFF)
5705 StdioException("unpaired UTF-16 surrogate");
5707 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5709 import std.utf : encode;
5717 else version (Posix)
5719 import std.utf : encode;
5721 for (int c; (c = lf.fgetwc()) != -1; )
5723 if ((c & ~0x7F) == 0)
5724 buf ~= cast(char) c;
5726 encode(buf, cast(dchar) c);
5727 if (c == terminator)
5741 // First, fill the existing buffer
5742 for (size_t bufPos = 0; bufPos < buf.length; )
5744 immutable c = lf.fgetc();
5747 buf.length = bufPos;
5750 buf[bufPos++] = cast(char) c;
5751 if (c == terminator)
5753 // No need to test for errors in file
5754 buf.length = bufPos;
5758 // Then, append to it
5759 for (int c; (c = lf.fgetc()) != -1; )
5761 buf ~= cast(char) c;
5762 if (c == terminator)
5764 // No need to test for errors in file
5778 static import std.file;
5779 auto deleteme = testFilename();
5780 scope(exit) std.file.remove(deleteme);
5782 std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n");
5783 File f = File(deleteme, "rb");
5785 char[] ln = new char[2];
5788 assert(ln == "abcd\n");
5789 char[] t = ln[0 .. 2];
5792 // https://issues.dlang.org/show_bug.cgi?id=13856: ln stomped to "abtd"
5793 assert(ln == "abcd\n");
5795 // it can also stomp the array length
5798 assert(ln == "0123456789abcde\n");
5803 assert(ln == "1234\n");
5804 assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough
5807 /** Experimental network access via the File interface
5809 Opens a TCP connection to the given host and port, then returns
5810 a File struct with read and write access through the same interface
5811 as any other file (meaning writef and the byLine ranges work!).
5821 File openNetwork(string host, ushort port)
5823 import core.stdc.string : memcpy;
5824 import core.sys.posix.arpa.inet : htons;
5825 import core.sys.posix.netdb : gethostbyname;
5826 import core.sys.posix.netinet.in_ : sockaddr_in;
5827 static import core.sys.posix.unistd;
5828 static import sock = core.sys.posix.sys.socket;
5829 import std.conv : to;
5830 import std.exception : enforce;
5831 import std.internal.cstring : tempCString;
5833 auto h = enforce( gethostbyname(host.tempCString()),
5834 new StdioException("gethostbyname"));
5836 int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
5837 enforce(s != -1, new StdioException("socket"));
5841 // want to make sure it doesn't dangle if something throws. Upon
5842 // normal exit, the File struct's reference counting takes care of
5843 // closing, so we don't need to worry about success
5844 core.sys.posix.unistd.close(s);
5849 addr.sin_family = sock.AF_INET;
5850 addr.sin_port = htons(port);
5851 memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
5853 enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
5854 new StdioException("Connect failed"));
5857 f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
5862 version (StdUnittest) private string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
5864 import std.conv : text;
5865 import std.file : deleteme;
5866 import std.path : baseName;
5868 // filename intentionally contains non-ASCII (Russian) characters for
5869 // https://issues.dlang.org/show_bug.cgi?id=7648
5870 return text(deleteme, "-детка.", baseName(file), ".", line);