1 // Written in the D programming language.
4 Standard I/O functions that extend $(B core.stdc.stdio). $(B core.stdc.stdio)
5 is $(D_PARAM public)ally imported when importing $(B std.stdio).
7 Source: $(PHOBOSSRC std/_stdio.d)
8 Copyright: Copyright Digital Mars 2007-.
9 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
10 Authors: $(HTTP digitalmars.com, Walter Bright),
11 $(HTTP erdani.org, Andrei Alexandrescu),
16 import core.stdc.stddef; // wchar_t
17 public import core.stdc.stdio;
18 import std.algorithm.mutation; // copy
19 import std.meta; // allSatisfy
20 import std.range.primitives; // ElementEncodingType, empty, front,
21 // isBidirectionalRange, isInputRange, put
22 import std.traits; // isSomeChar, isSomeString, Unqual, isPointer
23 import std.typecons; // Flag
26 If flag $(D KeepTerminator) is set to $(D KeepTerminator.yes), then the delimiter
27 is included in the strings returned.
29 alias KeepTerminator = Flag!"keepTerminator";
31 version (CRuntime_Microsoft)
33 version = MICROSOFT_STDIO;
35 else version (CRuntime_DigitalMars)
37 // Specific to the way Digital Mars C does stdio
38 version = DIGITAL_MARS_STDIO;
41 version (CRuntime_Glibc)
43 // Specific to the way Gnu C does stdio
45 version = HAS_GETDELIM;
51 version = HAS_GETDELIM;
57 version = HAS_GETDELIM;
63 version = HAS_GETDELIM;
69 version = NO_GETDELIM;
72 version (CRuntime_Bionic)
75 version = NO_GETDELIM;
78 // Character type used for operating system filesystem APIs
81 private alias FSChar = wchar;
85 private alias FSChar = char;
92 // core.stdc.stdio.fopen expects file names to be
93 // encoded in CP_ACP on Windows instead of UTF-8.
94 /+ Waiting for druntime pull 299
96 extern (C) nothrow @nogc FILE* _wfopen(in wchar* filename, in wchar* mode);
97 extern (C) nothrow @nogc FILE* _wfreopen(in wchar* filename, in wchar* mode, FILE* fp);
99 import core.sys.windows.windows : HANDLE;
102 version (DIGITAL_MARS_STDIO)
107 * Digital Mars under-the-hood C I/O functions.
108 * Use _iobuf* for the unshared version of FILE*,
109 * usable when the FILE is locked.
113 int _fputc_nlock(int, _iobuf*);
114 int _fputwc_nlock(int, _iobuf*);
115 int _fgetc_nlock(_iobuf*);
116 int _fgetwc_nlock(_iobuf*);
117 int __fp_lock(FILE*);
118 void __fp_unlock(FILE*);
120 int setmode(int, int);
122 alias FPUTC = _fputc_nlock;
123 alias FPUTWC = _fputwc_nlock;
124 alias FGETC = _fgetc_nlock;
125 alias FGETWC = _fgetwc_nlock;
127 alias FLOCK = __fp_lock;
128 alias FUNLOCK = __fp_unlock;
130 alias _setmode = setmode;
131 enum _O_BINARY = 0x8000;
132 int _fileno(FILE* f) { return f._file; }
133 alias fileno = _fileno;
135 else version (MICROSOFT_STDIO)
140 * Microsoft under-the-hood C I/O functions
144 int _fputc_nolock(int, _iobuf*);
145 int _fputwc_nolock(int, _iobuf*);
146 int _fgetc_nolock(_iobuf*);
147 int _fgetwc_nolock(_iobuf*);
148 void _lock_file(FILE*);
149 void _unlock_file(FILE*);
150 int _setmode(int, int);
152 FILE* _fdopen(int, const (char)*);
153 int _fseeki64(FILE*, long, int);
154 long _ftelli64(FILE*);
156 alias FPUTC = _fputc_nolock;
157 alias FPUTWC = _fputwc_nolock;
158 alias FGETC = _fgetc_nolock;
159 alias FGETWC = _fgetwc_nolock;
161 alias FLOCK = _lock_file;
162 alias FUNLOCK = _unlock_file;
164 alias setmode = _setmode;
165 alias fileno = _fileno;
175 else version (GCC_IO)
178 * Gnu under-the-hood C I/O functions; see
179 * http://gnu.org/software/libc/manual/html_node/I_002fO-on-Streams.html
185 int fputc_unlocked(int, _iobuf*);
186 int fputwc_unlocked(wchar_t, _iobuf*);
187 int fgetc_unlocked(_iobuf*);
188 int fgetwc_unlocked(_iobuf*);
189 void flockfile(FILE*);
190 void funlockfile(FILE*);
192 private size_t fwrite_unlocked(const(void)* ptr,
193 size_t size, size_t n, _iobuf *stream);
196 alias FPUTC = fputc_unlocked;
197 alias FPUTWC = fputwc_unlocked;
198 alias FGETC = fgetc_unlocked;
199 alias FGETWC = fgetwc_unlocked;
201 alias FLOCK = flockfile;
202 alias FUNLOCK = funlockfile;
204 else version (GENERIC_IO)
211 void flockfile(FILE*);
212 void funlockfile(FILE*);
215 int fputc_unlocked(int c, _iobuf* fp) { return fputc(c, cast(shared) fp); }
216 int fputwc_unlocked(wchar_t c, _iobuf* fp)
218 import core.stdc.wchar_ : fputwc;
219 return fputwc(c, cast(shared) fp);
221 int fgetc_unlocked(_iobuf* fp) { return fgetc(cast(shared) fp); }
222 int fgetwc_unlocked(_iobuf* fp)
224 import core.stdc.wchar_ : fgetwc;
225 return fgetwc(cast(shared) fp);
228 alias FPUTC = fputc_unlocked;
229 alias FPUTWC = fputwc_unlocked;
230 alias FGETC = fgetc_unlocked;
231 alias FGETWC = fgetwc_unlocked;
233 alias FLOCK = flockfile;
234 alias FUNLOCK = funlockfile;
238 static assert(0, "unsupported C I/O system");
241 version (HAS_GETDELIM) extern(C) nothrow @nogc
243 ptrdiff_t getdelim(char**, size_t*, int, FILE*);
244 // getline() always comes together with getdelim()
245 ptrdiff_t getline(char**, size_t*, FILE*);
248 //------------------------------------------------------------------------------
249 struct ByRecord(Fields...)
252 import std.typecons : Tuple;
256 Tuple!(Fields) current;
260 this(File f, string format)
264 this.format = format;
265 popFront(); // prime the range
268 /// Range primitive implementations.
269 @property bool empty()
275 @property ref Tuple!(Fields) front()
283 import std.conv : text;
284 import std.exception : enforce;
285 import std.format : formattedRead;
286 import std.string : chomp;
288 enforce(file.isOpen, "ByRecord: File must be open");
297 formattedRead(line, format, ¤t);
298 enforce(line.empty, text("Leftover characters in record: `",
304 template byRecord(Fields...)
306 ByRecord!(Fields) byRecord(File f, string format)
308 return typeof(return)(f, format);
313 Encapsulates a $(D FILE*). Generally D does not attempt to provide
314 thin wrappers over equivalent functions in the C standard library, but
315 manipulating $(D FILE*) values directly is unsafe and error-prone in
316 many ways. The $(D File) type ensures safe manipulation, automatic
317 file closing, and a lot of convenience.
319 The underlying $(D FILE*) handle is maintained in a reference-counted
320 manner, such that as soon as the last $(D File) variable bound to a
321 given $(D FILE*) goes out of scope, the underlying $(D FILE*) is
322 automatically closed.
327 void main(string[] args)
329 auto f = File("test.txt", "w"); // open for writing
333 auto g = f; // now g and f write to the same file
334 // internal reference count is 2
335 g.write(", ", args[1]);
336 // g exits scope, reference count decreases to 1
339 // f exits scope, reference count falls to zero,
340 // underlying `FILE*` is closed.
352 import std.range.primitives : ElementEncodingType;
353 import std.traits : isScalarType, isArray;
354 enum Orientation { unknown, narrow, wide }
358 FILE * handle = null; // Is null iff this Impl is closed by another File
359 uint refs = uint.max / 2;
360 bool isPopened; // true iff the stream has been created by popen()
361 Orientation orientation;
364 private string _name;
366 package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted
368 import core.stdc.stdlib : malloc;
369 import std.exception : enforce;
372 _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
375 _p.isPopened = isPopened;
376 _p.orientation = Orientation.unknown;
381 Constructor taking the name of the file to open and the open mode.
383 Copying one $(D File) object to another results in the two $(D File)
384 objects referring to the same underlying file.
386 The destructor automatically closes the file as soon as no $(D File)
387 object refers to it anymore.
390 name = range or string representing the file _name
391 stdioOpenmode = range or string represting the open mode
392 (with the same semantics as in the C standard library
393 $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen)
396 Throws: $(D ErrnoException) if the file could not be opened.
398 this(string name, in char[] stdioOpenmode = "rb") @safe
400 import std.conv : text;
401 import std.exception : errnoEnforce;
403 this(errnoEnforce(.fopen(name, stdioOpenmode),
404 text("Cannot open file `", name, "' in mode `",
405 stdioOpenmode, "'")),
408 // MSVCRT workaround (issue 14422)
409 version (MICROSOFT_STDIO)
412 foreach (c; stdioOpenmode)
418 if (append && !update)
424 this(R1, R2)(R1 name)
425 if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1))
427 import std.conv : to;
428 this(name.to!string, "rb");
432 this(R1, R2)(R1 name, R2 mode)
433 if (isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) &&
434 isInputRange!R2 && isSomeChar!(ElementEncodingType!R2))
436 import std.conv : to;
437 this(name.to!string, mode.to!string);
442 static import std.file;
443 import std.utf : byChar;
444 auto deleteme = testFilename();
445 auto f = File(deleteme.byChar, "w".byChar);
447 std.file.remove(deleteme);
455 this(this) @safe nothrow
463 Assigns a file to another. The target of the assignment gets detached
464 from whatever file it was attached to, and attaches itself to the new
467 void opAssign(File rhs) @safe
469 import std.algorithm.mutation : swap;
475 First calls $(D detach) (throwing on failure), and then attempts to
476 _open file $(D name) with mode $(D stdioOpenmode). The mode has the
477 same semantics as in the C standard library $(HTTP
478 cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
480 Throws: $(D ErrnoException) in case of error.
482 void open(string name, in char[] stdioOpenmode = "rb") @safe
485 this = File(name, stdioOpenmode);
489 Reuses the `File` object to either open a different file, or change
490 the file mode. If `name` is `null`, the mode of the currently open
491 file is changed; otherwise, a new file is opened, reusing the C
492 `FILE*`. The function has the same semantics as in the C standard
493 library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen)
496 Note: Calling `reopen` with a `null` `name` is not implemented
499 Throws: $(D ErrnoException) in case of error.
501 void reopen(string name, in char[] stdioOpenmode = "rb") @trusted
503 import std.conv : text;
504 import std.exception : enforce, errnoEnforce;
505 import std.internal.cstring : tempCString;
507 enforce(isOpen, "Attempting to reopen() an unopened file");
509 auto namez = (name == null ? _name : name).tempCString!FSChar();
510 auto modez = stdioOpenmode.tempCString!FSChar();
512 FILE* fd = _p.handle;
514 fd = _wfreopen(namez, modez, fd);
516 fd = freopen(namez, modez, fd);
518 errnoEnforce(fd, name
519 ? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'")
520 : text("Cannot reopen file in mode `", stdioOpenmode, "'"));
526 @system unittest // Test changing filename
528 import std.exception : assertThrown, assertNotThrown;
529 static import std.file;
531 auto deleteme = testFilename();
532 std.file.write(deleteme, "foo");
533 scope(exit) std.file.remove(deleteme);
534 auto f = File(deleteme);
535 assert(f.readln() == "foo");
537 auto deleteme2 = testFilename();
538 std.file.write(deleteme2, "bar");
539 scope(exit) std.file.remove(deleteme2);
541 assert(f.name == deleteme2);
542 assert(f.readln() == "bar");
546 version (CRuntime_DigitalMars) {} else // Not implemented
547 version (CRuntime_Microsoft) {} else // Not implemented
548 @system unittest // Test changing mode
550 import std.exception : assertThrown, assertNotThrown;
551 static import std.file;
553 auto deleteme = testFilename();
554 std.file.write(deleteme, "foo");
555 scope(exit) std.file.remove(deleteme);
556 auto f = File(deleteme, "r+");
557 assert(f.readln() == "foo");
563 assert(f.name == deleteme);
565 assert(std.file.readText(deleteme) == "barbaz");
569 First calls $(D detach) (throwing on failure), and then runs a command
570 by calling the C standard library function $(HTTP
571 opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen).
573 Throws: $(D ErrnoException) in case of error.
575 version (Posix) void popen(string command, in char[] stdioOpenmode = "r") @safe
577 import std.exception : errnoEnforce;
580 this = File(errnoEnforce(.popen(command, stdioOpenmode),
581 "Cannot run command `"~command~"'"),
586 First calls $(D detach) (throwing on failure), and then attempts to
587 associate the given file descriptor with the $(D File). The mode must
588 be compatible with the mode of the file descriptor.
590 Throws: $(D ErrnoException) in case of error.
592 void fdopen(int fd, in char[] stdioOpenmode = "rb") @safe
594 fdopen(fd, stdioOpenmode, null);
597 package void fdopen(int fd, in char[] stdioOpenmode, string name) @trusted
599 import std.exception : errnoEnforce;
600 import std.internal.cstring : tempCString;
602 auto modez = stdioOpenmode.tempCString();
605 version (DIGITAL_MARS_STDIO)
607 // This is a re-implementation of DMC's fdopen, but without the
608 // mucking with the file descriptor. POSIX standard requires the
609 // new fdopen'd file to retain the given file descriptor's
611 import core.stdc.stdio : fopen;
612 auto fp = fopen("NUL", modez);
613 errnoEnforce(fp, "Cannot open placeholder NUL stream");
615 auto iob = cast(_iobuf*) fp;
618 iob._flag &= ~_IOTRAN;
623 version (Windows) // MSVCRT
624 auto fp = _fdopen(fd, modez);
627 import core.sys.posix.stdio : fdopen;
628 auto fp = fdopen(fd, modez);
632 this = File(fp, name);
635 // Declare a dummy HANDLE to allow generating documentation
636 // for Windows-only methods.
637 version (StdDdoc) { version (Windows) {} else alias HANDLE = int; }
640 First calls $(D detach) (throwing on failure), and then attempts to
641 associate the given Windows $(D HANDLE) with the $(D File). The mode must
642 be compatible with the access attributes of the handle. Windows only.
644 Throws: $(D ErrnoException) in case of error.
647 void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode);
650 void windowsHandleOpen(HANDLE handle, in char[] stdioOpenmode)
652 import core.stdc.stdint : intptr_t;
653 import std.exception : errnoEnforce;
654 import std.format : format;
656 // Create file descriptors from the handles
657 version (DIGITAL_MARS_STDIO)
658 auto fd = _handleToFD(handle, FHND_DEVICE);
663 foreach (c; stdioOpenmode)
666 case 'r': mode |= _O_RDONLY; break;
667 case '+': mode &=~_O_RDONLY; break;
668 case 'a': mode |= _O_APPEND; break;
669 case 'b': mode |= _O_BINARY; break;
670 case 't': mode |= _O_TEXT; break;
671 case ',': break modeLoop;
675 auto fd = _open_osfhandle(cast(intptr_t) handle, mode);
678 errnoEnforce(fd >= 0, "Cannot open Windows HANDLE");
679 fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle));
683 /** Returns $(D true) if the file is opened. */
684 @property bool isOpen() const @safe pure nothrow
686 return _p !is null && _p.handle;
690 Returns $(D true) if the file is at end (see $(HTTP
691 cplusplus.com/reference/clibrary/cstdio/feof.html, feof)).
693 Throws: $(D Exception) if the file is not opened.
695 @property bool eof() const @trusted pure
697 import std.exception : enforce;
699 enforce(_p && _p.handle, "Calling eof() against an unopened file.");
700 return .feof(cast(FILE*) _p.handle) != 0;
703 /** Returns the name of the last opened file, if any.
704 If a $(D File) was created with $(LREF tmpfile) and $(LREF wrapFile)
706 @property string name() const @safe pure nothrow
712 If the file is not opened, returns $(D true). Otherwise, returns
713 $(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for
716 @property bool error() const @trusted pure nothrow
718 return !isOpen || .ferror(cast(FILE*) _p.handle);
724 static import std.file;
725 auto deleteme = testFilename();
726 auto f = File(deleteme, "w");
727 scope(exit) std.file.remove(deleteme);
734 Detaches from the underlying file. If the sole owner, calls $(D close).
736 Throws: $(D ErrnoException) on failure if closing the file.
753 static import std.file;
755 auto deleteme = testFilename();
756 scope(exit) std.file.remove(deleteme);
757 auto f = File(deleteme, "w");
762 assert(f._p.refs == 1);
767 If the file was unopened, succeeds vacuously. Otherwise closes the
768 file (by calling $(HTTP
769 cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)),
770 throwing on error. Even if an exception is thrown, afterwards the $(D
771 File) object is empty. This is different from $(D detach) in that it
772 always closes the file; consequently, all other $(D File) objects
773 referring to the same handle will see a closed file henceforth.
775 Throws: $(D ErrnoException) on error.
777 void close() @trusted
779 import core.stdc.stdlib : free;
780 import std.exception : errnoEnforce;
782 if (!_p) return; // succeed vacuously
788 _p = null; // start a new life
790 if (!_p.handle) return; // Impl is closed by another File
792 scope(exit) _p.handle = null; // nullify the handle anyway
795 import core.sys.posix.stdio : pclose;
796 import std.format : format;
800 auto res = pclose(_p.handle);
801 errnoEnforce(res != -1,
802 "Could not close pipe `"~_name~"'");
803 errnoEnforce(res == 0, format("Command returned %d", res));
807 errnoEnforce(.fclose(_p.handle) == 0,
808 "Could not close file `"~_name~"'");
812 If the file is not opened, succeeds vacuously. Otherwise, returns
813 $(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html,
814 _clearerr) for the file handle.
816 void clearerr() @safe pure nothrow
818 _p is null || _p.handle is null ||
819 .clearerr(_p.handle);
823 Flushes the C $(D FILE) buffers.
825 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush)
828 Throws: $(D Exception) if the file is not opened or if the call to $(D fflush) fails.
830 void flush() @trusted
832 import std.exception : enforce, errnoEnforce;
834 enforce(isOpen, "Attempting to flush() in an unopened file");
835 errnoEnforce(.fflush(_p.handle) == 0);
841 import std.exception : assertThrown;
842 static import std.file;
844 auto deleteme = testFilename();
845 auto f = File(deleteme, "w");
846 scope(exit) std.file.remove(deleteme);
849 assertThrown(f.flush());
853 Forces any data buffered by the OS to be written to disk.
854 Call $(LREF flush) before calling this function to flush the C $(D FILE) buffers first.
857 $(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx,
858 $(D FlushFileBuffers)) on Windows and
859 $(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html,
860 $(D fsync)) on POSIX for the file handle.
862 Throws: $(D Exception) if the file is not opened or if the OS call fails.
866 import std.exception : enforce;
868 enforce(isOpen, "Attempting to sync() an unopened file");
872 import core.sys.windows.windows : FlushFileBuffers;
873 wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed");
877 import core.sys.posix.unistd : fsync;
878 import std.exception : errnoEnforce;
879 errnoEnforce(fsync(fileno) == 0, "fsync failed");
884 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the
885 file handle. The number of items to read and the size of
886 each item is inferred from the size and type of the input array, respectively.
888 Returns: The slice of $(D buffer) containing the data that was actually read.
889 This will be shorter than $(D buffer) if EOF was reached before the buffer
892 Throws: $(D Exception) if $(D buffer) is empty.
893 $(D ErrnoException) if the file is not opened or the call to $(D fread) fails.
895 $(D rawRead) always reads in binary mode on Windows.
897 T[] rawRead(T)(T[] buffer)
899 import std.exception : errnoEnforce;
902 throw new Exception("rawRead must take a non-empty buffer");
905 immutable fd = ._fileno(_p.handle);
906 immutable mode = ._setmode(fd, _O_BINARY);
907 scope(exit) ._setmode(fd, mode);
908 version (DIGITAL_MARS_STDIO)
910 import core.atomic : atomicOp;
913 immutable info = __fhnd_info[fd];
914 atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
915 scope(exit) __fhnd_info[fd] = info;
918 immutable freadResult = trustedFread(_p.handle, buffer);
919 assert(freadResult <= buffer.length); // fread return guarantee
920 if (freadResult != buffer.length) // error or eof
922 errnoEnforce(!error);
923 return buffer[0 .. freadResult];
931 static import std.file;
933 auto testFile = testFilename();
934 std.file.write(testFile, "\r\n\n\r\n");
935 scope(exit) std.file.remove(testFile);
937 auto f = File(testFile, "r");
938 auto buf = f.rawRead(new char[5]);
940 assert(buf == "\r\n\n\r\n");
944 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file
945 handle. The number of items to write and the size of each
946 item is inferred from the size and type of the input array, respectively. An
947 error is thrown if the buffer could not be written in its entirety.
949 $(D rawWrite) always writes in binary mode on Windows.
951 Throws: $(D ErrnoException) if the file is not opened or if the call to $(D fwrite) fails.
953 void rawWrite(T)(in T[] buffer)
955 import std.conv : text;
956 import std.exception : errnoEnforce;
960 flush(); // before changing translation mode
961 immutable fd = ._fileno(_p.handle);
962 immutable mode = ._setmode(fd, _O_BINARY);
963 scope(exit) ._setmode(fd, mode);
964 version (DIGITAL_MARS_STDIO)
966 import core.atomic : atomicOp;
969 immutable info = __fhnd_info[fd];
970 atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
971 scope(exit) __fhnd_info[fd] = info;
973 scope(exit) flush(); // before restoring translation mode
975 auto result = trustedFwrite(_p.handle, buffer);
976 if (result == result.max) result = 0;
977 errnoEnforce(result == buffer.length,
978 text("Wrote ", result, " instead of ", buffer.length,
979 " objects of type ", T.stringof, " to file `",
986 static import std.file;
988 auto testFile = testFilename();
989 auto f = File(testFile, "w");
990 scope(exit) std.file.remove(testFile);
992 f.rawWrite("\r\n\n\r\n");
994 assert(std.file.read(testFile) == "\r\n\n\r\n");
998 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek)
1001 Throws: $(D Exception) if the file is not opened.
1002 $(D ErrnoException) if the call to $(D fseek) fails.
1004 void seek(long offset, int origin = SEEK_SET) @trusted
1006 import std.conv : to, text;
1007 import std.exception : enforce, errnoEnforce;
1009 enforce(isOpen, "Attempting to seek() in an unopened file");
1012 version (CRuntime_Microsoft)
1014 alias fseekFun = _fseeki64;
1019 alias fseekFun = fseek;
1023 else version (Posix)
1025 import core.sys.posix.stdio : fseeko, off_t;
1026 alias fseekFun = fseeko;
1028 errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0,
1029 "Could not seek in file `"~_name~"'");
1034 import std.conv : text;
1035 static import std.file;
1037 auto deleteme = testFilename();
1038 auto f = File(deleteme, "w+");
1039 scope(exit) { f.close(); std.file.remove(deleteme); }
1040 f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1042 assert(f.readln() == "hijklmnopqrstuvwxyz");
1044 version (CRuntime_DigitalMars)
1045 auto bigOffset = int.max - 100;
1047 version (CRuntime_Bionic)
1048 auto bigOffset = int.max - 100;
1050 auto bigOffset = cast(ulong) int.max + 100;
1052 assert(f.tell == bigOffset, text(f.tell));
1053 // Uncomment the tests below only if you want to wait for
1055 // f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1056 // f.seek(-3, SEEK_END);
1057 // assert(f.readln() == "xyz");
1061 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the
1062 managed file handle.
1064 Throws: $(D Exception) if the file is not opened.
1065 $(D ErrnoException) if the call to $(D ftell) fails.
1067 @property ulong tell() const @trusted
1069 import std.exception : enforce, errnoEnforce;
1071 enforce(isOpen, "Attempting to tell() in an unopened file");
1074 version (CRuntime_Microsoft)
1075 immutable result = _ftelli64(cast(FILE*) _p.handle);
1077 immutable result = ftell(cast(FILE*) _p.handle);
1079 else version (Posix)
1081 import core.sys.posix.stdio : ftello;
1082 immutable result = ftello(cast(FILE*) _p.handle);
1084 errnoEnforce(result != -1,
1085 "Query ftell() failed for file `"~_name~"'");
1092 import std.conv : text;
1093 static import std.file;
1095 auto testFile = testFilename();
1096 std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz");
1097 scope(exit) { std.file.remove(testFile); }
1099 auto f = File(testFile);
1100 auto a = new ubyte[4];
1102 assert(f.tell == 4, text(f.tell));
1106 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind)
1107 for the file handle.
1109 Throws: $(D Exception) if the file is not opened.
1113 import std.exception : enforce;
1115 enforce(isOpen, "Attempting to rewind() an unopened file");
1120 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
1123 Throws: $(D Exception) if the file is not opened.
1124 $(D ErrnoException) if the call to $(D setvbuf) fails.
1126 void setvbuf(size_t size, int mode = _IOFBF) @trusted
1128 import std.exception : enforce, errnoEnforce;
1130 enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1131 errnoEnforce(.setvbuf(_p.handle, null, mode, size) == 0,
1132 "Could not set buffering for file `"~_name~"'");
1136 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
1137 _setvbuf) for the file handle.
1139 Throws: $(D Exception) if the file is not opened.
1140 $(D ErrnoException) if the call to $(D setvbuf) fails.
1142 void setvbuf(void[] buf, int mode = _IOFBF) @trusted
1144 import std.exception : enforce, errnoEnforce;
1146 enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1147 errnoEnforce(.setvbuf(_p.handle,
1148 cast(char*) buf.ptr, mode, buf.length) == 0,
1149 "Could not set buffering for file `"~_name~"'");
1155 import core.sys.windows.windows : ULARGE_INTEGER, OVERLAPPED, BOOL;
1157 private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
1160 if (!start && !length)
1162 ULARGE_INTEGER liStart = void, liLength = void;
1163 liStart.QuadPart = start;
1164 liLength.QuadPart = length;
1165 OVERLAPPED overlapped;
1166 overlapped.Offset = liStart.LowPart;
1167 overlapped.OffsetHigh = liStart.HighPart;
1168 overlapped.hEvent = null;
1169 return F(windowsHandle, flags, 0, liLength.LowPart,
1170 liLength.HighPart, &overlapped);
1173 private static T wenforce(T)(T cond, string str)
1175 import core.sys.windows.windows : GetLastError;
1176 import std.windows.syserror : sysErrorString;
1178 if (cond) return cond;
1179 throw new Exception(str ~ ": " ~ sysErrorString(GetLastError()));
1184 private int lockImpl(int operation, short l_type,
1185 ulong start, ulong length)
1187 import core.sys.posix.fcntl : fcntl, flock, off_t;
1188 import core.sys.posix.unistd : getpid;
1189 import std.conv : to;
1193 fl.l_whence = SEEK_SET;
1194 fl.l_start = to!off_t(start);
1195 fl.l_len = to!off_t(length);
1196 fl.l_pid = getpid();
1197 return fcntl(fileno, operation, &fl);
1202 Locks the specified file segment. If the file segment is already locked
1203 by another process, waits until the existing lock is released.
1204 If both $(D start) and $(D length) are zero, the entire file is locked.
1206 Locks created using $(D lock) and $(D tryLock) have the following properties:
1208 $(LI All locks are automatically released when the process terminates.)
1209 $(LI Locks are not inherited by child processes.)
1210 $(LI Closing a file will release all locks associated with the file. On POSIX,
1211 even locks acquired via a different $(D File) will be released as well.)
1212 $(LI Not all NFS implementations correctly implement file locking.)
1215 void lock(LockType lockType = LockType.readWrite,
1216 ulong start = 0, ulong length = 0)
1218 import std.exception : enforce;
1220 enforce(isOpen, "Attempting to call lock() on an unopened file");
1223 import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK;
1224 import std.exception : errnoEnforce;
1225 immutable short type = lockType == LockType.readWrite
1226 ? F_WRLCK : F_RDLCK;
1227 errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1,
1228 "Could not set lock for file `"~_name~"'");
1233 import core.sys.windows.windows : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK;
1234 immutable type = lockType == LockType.readWrite ?
1235 LOCKFILE_EXCLUSIVE_LOCK : 0;
1236 wenforce(lockImpl!LockFileEx(start, length, type),
1237 "Could not set lock for file `"~_name~"'");
1240 static assert(false);
1244 Attempts to lock the specified file segment.
1245 If both $(D start) and $(D length) are zero, the entire file is locked.
1246 Returns: $(D true) if the lock was successful, and $(D false) if the
1247 specified file segment was already locked.
1249 bool tryLock(LockType lockType = LockType.readWrite,
1250 ulong start = 0, ulong length = 0)
1252 import std.exception : enforce;
1254 enforce(isOpen, "Attempting to call tryLock() on an unopened file");
1257 import core.stdc.errno : EACCES, EAGAIN, errno;
1258 import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK;
1259 import std.exception : errnoEnforce;
1260 immutable short type = lockType == LockType.readWrite
1261 ? F_WRLCK : F_RDLCK;
1262 immutable res = lockImpl(F_SETLK, type, start, length);
1263 if (res == -1 && (errno == EACCES || errno == EAGAIN))
1265 errnoEnforce(res != -1, "Could not set lock for file `"~_name~"'");
1271 import core.sys.windows.windows : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK,
1272 ERROR_IO_PENDING, ERROR_LOCK_VIOLATION, LOCKFILE_FAIL_IMMEDIATELY;
1273 immutable type = lockType == LockType.readWrite
1274 ? LOCKFILE_EXCLUSIVE_LOCK : 0;
1275 immutable res = lockImpl!LockFileEx(start, length,
1276 type | LOCKFILE_FAIL_IMMEDIATELY);
1277 if (!res && (GetLastError() == ERROR_IO_PENDING
1278 || GetLastError() == ERROR_LOCK_VIOLATION))
1280 wenforce(res, "Could not set lock for file `"~_name~"'");
1284 static assert(false);
1288 Removes the lock over the specified file segment.
1290 void unlock(ulong start = 0, ulong length = 0)
1292 import std.exception : enforce;
1294 enforce(isOpen, "Attempting to call unlock() on an unopened file");
1297 import core.sys.posix.fcntl : F_SETLK, F_UNLCK;
1298 import std.exception : errnoEnforce;
1299 errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1,
1300 "Could not remove lock for file `"~_name~"'");
1305 import core.sys.windows.windows : UnlockFileEx;
1306 wenforce(lockImpl!UnlockFileEx(start, length),
1307 "Could not remove lock for file `"~_name~"'");
1310 static assert(false);
1316 static import std.file;
1317 auto deleteme = testFilename();
1318 scope(exit) std.file.remove(deleteme);
1319 auto f = File(deleteme, "wb");
1320 assert(f.tryLock());
1321 auto g = File(deleteme, "wb");
1322 assert(!g.tryLock());
1323 assert(!g.tryLock(LockType.read));
1325 f.lock(LockType.read);
1326 assert(!g.tryLock());
1327 assert(g.tryLock(LockType.read));
1335 static import std.file;
1336 auto deleteme = testFilename();
1337 scope(exit) std.file.remove(deleteme);
1339 // Since locks are per-process, we cannot test lock failures within
1340 // the same process. fork() is used to create a second process.
1341 static void runForked(void delegate() code)
1343 import core.stdc.stdlib : exit;
1344 import core.sys.posix.sys.wait : wait;
1345 import core.sys.posix.unistd : fork;
1347 if ((child = fork()) == 0)
1354 assert(wait(&status) != -1);
1355 assert(status == 0, "Fork crashed");
1359 auto f = File(deleteme, "w+b");
1363 auto g = File(deleteme, "a+b");
1364 assert(g.tryLock());
1366 assert(g.tryLock(LockType.read));
1369 assert(f.tryLock());
1372 auto g = File(deleteme, "a+b");
1373 assert(!g.tryLock());
1374 assert(!g.tryLock(LockType.read));
1378 f.lock(LockType.read);
1381 auto g = File(deleteme, "a+b");
1382 assert(!g.tryLock());
1383 assert(g.tryLock(LockType.read));
1391 Writes its arguments in text format to the file.
1393 Throws: $(D Exception) if the file is not opened.
1394 $(D ErrnoException) on an error writing to the file.
1396 void write(S...)(S args)
1398 import std.traits : isBoolean, isIntegral, isAggregateType;
1399 auto w = lockingTextWriter();
1402 alias A = typeof(arg);
1403 static if (isAggregateType!A || is(A == enum))
1405 import std.format : formattedWrite;
1407 formattedWrite(w, "%s", arg);
1409 else static if (isSomeString!A)
1413 else static if (isIntegral!A)
1415 import std.conv : toTextRange;
1417 toTextRange(arg, w);
1419 else static if (isBoolean!A)
1421 put(w, arg ? "true" : "false");
1423 else static if (isSomeChar!A)
1429 import std.format : formattedWrite;
1431 // Most general case
1432 formattedWrite(w, "%s", arg);
1438 Writes its arguments in text format to the file, followed by a newline.
1440 Throws: $(D Exception) if the file is not opened.
1441 $(D ErrnoException) on an error writing to the file.
1443 void writeln(S...)(S args)
1449 Writes its arguments in text format to the file, according to the
1453 fmt = The $(LINK2 std_format.html#format-string, format string).
1454 When passed as a compile-time argument, the string will be statically checked
1455 against the argument types passed.
1456 args = Items to write.
1458 Throws: $(D Exception) if the file is not opened.
1459 $(D ErrnoException) on an error writing to the file.
1461 void writef(alias fmt, A...)(A args)
1462 if (isSomeString!(typeof(fmt)))
1464 import std.format : checkFormatException;
1466 alias e = checkFormatException!(fmt, A);
1467 static assert(!e, e.msg);
1468 return this.writef(fmt, args);
1472 void writef(Char, A...)(in Char[] fmt, A args)
1474 import std.format : formattedWrite;
1476 formattedWrite(lockingTextWriter(), fmt, args);
1479 /// Equivalent to `file.writef(fmt, args, '\n')`.
1480 void writefln(alias fmt, A...)(A args)
1481 if (isSomeString!(typeof(fmt)))
1483 import std.format : checkFormatException;
1485 alias e = checkFormatException!(fmt, A);
1486 static assert(!e, e.msg);
1487 return this.writefln(fmt, args);
1491 void writefln(Char, A...)(in Char[] fmt, A args)
1493 import std.format : formattedWrite;
1495 auto w = lockingTextWriter();
1496 formattedWrite(w, fmt, args);
1501 Read line from the file handle and return it as a specified type.
1503 This version manages its own read buffer, which means one memory allocation per call. If you are not
1504 retaining a reference to the read data, consider the $(D File.readln(buf)) version, which may offer
1505 better performance as it can reuse its read buffer.
1508 S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
1509 terminator = Line terminator (by default, $(D '\n')).
1512 String terminators are not supported due to ambiguity with readln(buf) below.
1515 The line that was read, including the line terminator character.
1518 $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
1522 // Reads `stdin` and writes it to `stdout`.
1528 while ((line = stdin.readln()) !is null)
1533 S readln(S = string)(dchar terminator = '\n')
1536 Unqual!(ElementEncodingType!S)[] buf;
1537 readln(buf, terminator);
1543 import std.algorithm.comparison : equal;
1544 static import std.file;
1545 import std.meta : AliasSeq;
1547 auto deleteme = testFilename();
1548 std.file.write(deleteme, "hello\nworld\n");
1549 scope(exit) std.file.remove(deleteme);
1550 foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
1552 auto witness = [ "hello\n", "world\n" ];
1553 auto f = File(deleteme);
1556 while ((buf = f.readln!String()).length)
1558 assert(i < witness.length);
1559 assert(equal(buf, witness[i++]));
1561 assert(i == witness.length);
1567 static import std.file;
1568 import std.typecons : Tuple;
1570 auto deleteme = testFilename();
1571 std.file.write(deleteme, "cześć \U0002000D");
1572 scope(exit) std.file.remove(deleteme);
1573 uint[] lengths = [12,8,7];
1574 foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
1576 immutable(C)[] witness = "cześć \U0002000D";
1577 auto buf = File(deleteme).readln!(immutable(C)[])();
1578 assert(buf.length == lengths[i]);
1579 assert(buf == witness);
1584 Read line from the file handle and write it to $(D buf[]), including
1585 terminating character.
1587 This can be faster than $(D line = File.readln()) because you can reuse
1588 the buffer for each call. Note that reusing the buffer means that you
1589 must copy the previous contents if you wish to retain them.
1592 buf = Buffer used to store the resulting line data. buf is
1593 resized as necessary.
1594 terminator = Line terminator (by default, $(D '\n')). Use
1595 $(REF newline, std,ascii) for portability (unless the file was opened in
1599 0 for end of file, otherwise number of characters read
1601 Throws: $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode
1606 // Read lines from `stdin` into a string
1607 // Ignore lines starting with '#'
1608 // Write the string to `stdout`
1615 while (stdin.readln(buf))
1627 This method can be more efficient than the one in the previous example
1628 because $(D stdin.readln(buf)) reuses (if possible) memory allocated
1629 for $(D buf), whereas $(D line = stdin.readln()) makes a new memory allocation
1632 For even better performance you can help $(D readln) by passing in a
1633 large buffer to avoid memory reallocations. This can be done by reusing the
1634 largest buffer returned by $(D readln):
1638 // Read lines from `stdin` and count words
1649 if (line.length > buf.length)
1652 words += line.split.length;
1658 This is actually what $(LREF byLine) does internally, so its usage
1659 is recommended if you want to process a complete file.
1661 size_t readln(C)(ref C[] buf, dchar terminator = '\n')
1662 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
1664 import std.exception : enforce;
1666 static if (is(C == char))
1668 enforce(_p && _p.handle, "Attempt to read from an unopened file.");
1669 if (_p.orientation == Orientation.unknown)
1671 import core.stdc.wchar_ : fwide;
1672 auto w = fwide(_p.handle, 0);
1673 if (w < 0) _p.orientation = Orientation.narrow;
1674 else if (w > 0) _p.orientation = Orientation.wide;
1676 return readlnImpl(_p.handle, buf, terminator, _p.orientation);
1680 // TODO: optimize this
1681 string s = readln(terminator);
1683 if (!s.length) return 0;
1694 // @system due to readln
1695 static import std.file;
1696 auto deleteme = testFilename();
1697 std.file.write(deleteme, "123\n456789");
1698 scope(exit) std.file.remove(deleteme);
1700 auto file = File(deleteme);
1701 char[] buffer = new char[10];
1702 char[] line = buffer;
1704 auto beyond = line.length;
1705 buffer[beyond] = 'a';
1706 file.readln(line); // should not write buffer beyond line
1707 assert(buffer[beyond] == 'a');
1710 @system unittest // bugzilla 15293
1712 // @system due to readln
1713 static import std.file;
1714 auto deleteme = testFilename();
1715 std.file.write(deleteme, "a\n\naa");
1716 scope(exit) std.file.remove(deleteme);
1718 auto file = File(deleteme);
1722 file.readln(buffer, '\n');
1725 file.readln(line, '\n');
1728 file.readln(line, '\n');
1730 assert(line[0 .. 1].capacity == 0);
1734 size_t readln(C, R)(ref C[] buf, R terminator)
1735 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
1736 isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
1738 import std.algorithm.mutation : swap;
1739 import std.algorithm.searching : endsWith;
1740 import std.range.primitives : back;
1742 auto last = terminator.back;
1747 if (!readln(buf2, last) || endsWith(buf2, terminator))
1766 static import std.file;
1767 import std.typecons : Tuple;
1769 auto deleteme = testFilename();
1770 std.file.write(deleteme, "hello\n\rworld\nhow\n\rare ya");
1771 scope(exit) std.file.remove(deleteme);
1772 foreach (C; Tuple!(char, wchar, dchar).Types)
1774 immutable(C)[][] witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
1775 auto f = File(deleteme);
1778 while (f.readln(buf, "\n\r"))
1780 assert(i < witness.length);
1781 assert(buf == witness[i++]);
1783 assert(buf.length == 0);
1788 * Reads formatted _data from the file using $(REF formattedRead, std,_format).
1790 * format = The $(LINK2 std_format.html#_format-string, _format string).
1791 * When passed as a compile-time argument, the string will be statically checked
1792 * against the argument types passed.
1793 * data = Items to be read.
1800 auto f = File("input");
1810 % echo "1 2 3" > input
1817 uint readf(alias format, Data...)(auto ref Data data)
1818 if (isSomeString!(typeof(format)))
1820 import std.format : checkFormatException;
1822 alias e = checkFormatException!(format, Data);
1823 static assert(!e, e.msg);
1824 return this.readf(format, data);
1828 uint readf(Data...)(in char[] format, auto ref Data data)
1830 import std.format : formattedRead;
1833 auto input = LockingTextReader(this);
1834 return formattedRead(input, format, data);
1840 static import std.file;
1842 auto deleteme = testFilename();
1843 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
1844 scope(exit) std.file.remove(deleteme);
1846 auto f = File(deleteme);
1848 assert(s == "hello", "["~s~"]");
1850 assert(s == "world", "["~s~"]");
1853 f.readf("%s\n%s\n", b1, b2);
1854 assert(b1 == true && b2 == false);
1857 // backwards compatibility with pointers
1860 // @system due to readf
1861 static import std.file;
1863 auto deleteme = testFilename();
1864 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
1865 scope(exit) std.file.remove(deleteme);
1867 auto f = File(deleteme);
1868 f.readf("%s\n", &s);
1869 assert(s == "hello", "["~s~"]");
1870 f.readf("%s\n", &s);
1871 assert(s == "world", "["~s~"]");
1875 f.readf("%s\n%s\n", &b1, &b2);
1876 assert(b1 == true && b2 == false);
1879 // backwards compatibility (mixed)
1882 // @system due to readf
1883 static import std.file;
1885 auto deleteme = testFilename();
1886 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
1887 scope(exit) std.file.remove(deleteme);
1889 auto f = File(deleteme);
1890 f.readf("%s\n%s\n", s1, &s2);
1891 assert(s1 == "hello");
1892 assert(s2 == "world");
1896 f.readf("%s\n%s\n", &b1, b2);
1897 assert(b1 == true && b2 == false);
1900 // Issue 12260 - Nice error of std.stdio.readf with newlines
1903 static import std.file;
1905 auto deleteme = testFilename();
1906 std.file.write(deleteme, "1\n2");
1907 scope(exit) std.file.remove(deleteme);
1909 auto f = File(deleteme);
1910 f.readf("%s", &input);
1912 import std.conv : ConvException;
1913 import std.exception : collectException;
1914 assert(collectException!ConvException(f.readf("%s", &input)).msg ==
1915 "Unexpected '\\n' when converting from type LockingTextReader to type int");
1919 Returns a temporary file by calling
1920 $(HTTP cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile).
1921 Note that the created file has no $(LREF name).*/
1922 static File tmpfile() @safe
1924 import std.exception : errnoEnforce;
1926 return File(errnoEnforce(.tmpfile(),
1927 "Could not create temporary file with tmpfile()"),
1932 Unsafe function that wraps an existing $(D FILE*). The resulting $(D
1933 File) never takes the initiative in closing the file.
1934 Note that the created file has no $(LREF name)*/
1935 /*private*/ static File wrapFile(FILE* f) @safe
1937 import std.exception : enforce;
1939 return File(enforce(f, "Could not wrap null FILE*"),
1940 null, /*uint.max / 2*/ 9999);
1944 Returns the $(D FILE*) corresponding to this object.
1946 FILE* getFP() @safe pure
1948 import std.exception : enforce;
1950 enforce(_p && _p.handle,
1951 "Attempting to call getFP() on an unopened file");
1957 static import core.stdc.stdio;
1958 assert(stdout.getFP() == core.stdc.stdio.stdout);
1962 Returns the file number corresponding to this object.
1964 @property int fileno() const @trusted
1966 import std.exception : enforce;
1968 enforce(isOpen, "Attempting to call fileno() on an unopened file");
1969 return .fileno(cast(FILE*) _p.handle);
1973 Returns the underlying operating system $(D HANDLE) (Windows only).
1976 @property HANDLE windowsHandle();
1979 @property HANDLE windowsHandle()
1981 version (DIGITAL_MARS_STDIO)
1982 return _fdToHandle(fileno);
1984 return cast(HANDLE)_get_osfhandle(fileno);
1988 // Note: This was documented until 2013/08
1990 Range that reads one line at a time. Returned by $(LREF byLine).
1992 Allows to directly use range operations on lines of a file.
1994 struct ByLine(Char, Terminator)
1997 import std.typecons : RefCounted, RefCountedAutoInitialize;
1999 /* Ref-counting stops the source range's Impl
2000 * from getting out of sync after the range is copied, e.g.
2001 * when accessing range.front, then using std.range.take,
2002 * then accessing range.front again. */
2003 alias PImpl = RefCounted!(Impl, RefCountedAutoInitialize.no);
2006 static if (isScalarType!Terminator)
2007 enum defTerm = '\n';
2009 enum defTerm = cast(Terminator)"\n";
2012 this(File f, KeepTerminator kt = No.keepTerminator,
2013 Terminator terminator = defTerm)
2015 impl = PImpl(f, kt, terminator);
2018 @property bool empty()
2020 return impl.refCountedPayload.empty;
2023 @property Char[] front()
2025 return impl.refCountedPayload.front;
2030 impl.refCountedPayload.popFront();
2040 Terminator terminator;
2041 KeepTerminator keepTerminator;
2044 this(File f, KeepTerminator kt, Terminator terminator)
2047 this.terminator = terminator;
2048 keepTerminator = kt;
2052 // Range primitive implementations.
2053 @property bool empty()
2055 return line is null;
2058 @property Char[] front()
2065 import std.algorithm.searching : endsWith;
2066 assert(file.isOpen);
2068 file.readln(line, terminator);
2069 if (line.length > buffer.length)
2078 else if (keepTerminator == No.keepTerminator
2079 && endsWith(line, terminator))
2081 static if (isScalarType!Terminator)
2083 else static if (isArray!Terminator)
2086 is(Unqual!(ElementEncodingType!Terminator) == Char));
2087 const tlen = terminator.length;
2090 static assert(false);
2091 line = line[0 .. line.length - tlen];
2098 Returns an input range set up to read from the file handle one line
2101 The element type for the range will be $(D Char[]). Range primitives
2102 may throw $(D StdioException) on I/O error.
2105 Each $(D front) will not persist after $(D
2106 popFront) is called, so the caller must copy its contents (e.g. by
2107 calling $(D to!string)) when retention is needed. If the caller needs
2108 to retain a copy of every line, use the $(LREF byLineCopy) function
2112 Char = Character type for each line, defaulting to $(D char).
2113 keepTerminator = Use $(D Yes.keepTerminator) to include the
2114 terminator at the end of each line.
2115 terminator = Line separator ($(D '\n') by default). Use
2116 $(REF newline, std,ascii) for portability (unless the file was opened in
2121 import std.algorithm, std.stdio, std.string;
2122 // Count words in a file using ranges.
2125 auto file = File("file.txt"); // Open for reading
2126 const wordCount = file.byLine() // Read lines
2127 .map!split // Split into words
2128 .map!(a => a.length) // Count words per line
2129 .sum(); // Total word count
2136 import std.range, std.stdio;
2137 // Read lines using foreach.
2140 auto file = File("file.txt"); // Open for reading
2141 auto range = file.byLine();
2142 // Print first three lines
2143 foreach (line; range.take(3))
2145 // Print remaining lines beginning with '#'
2146 foreach (line; range)
2148 if (!line.empty && line[0] == '#')
2153 Notice that neither example accesses the line data returned by
2154 $(D front) after the corresponding $(D popFront) call is made (because
2155 the contents may well have changed).
2157 auto byLine(Terminator = char, Char = char)
2158 (KeepTerminator keepTerminator = No.keepTerminator,
2159 Terminator terminator = '\n')
2160 if (isScalarType!Terminator)
2162 return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
2166 auto byLine(Terminator, Char = char)
2167 (KeepTerminator keepTerminator, Terminator terminator)
2168 if (is(Unqual!(ElementEncodingType!Terminator) == Char))
2170 return ByLine!(Char, Terminator)(this, keepTerminator, terminator);
2175 static import std.file;
2176 auto deleteme = testFilename();
2177 std.file.write(deleteme, "hi");
2178 scope(success) std.file.remove(deleteme);
2180 import std.meta : AliasSeq;
2181 foreach (T; AliasSeq!(char, wchar, dchar))
2183 auto blc = File(deleteme).byLine!(T, T);
2184 assert(blc.front == "hi");
2185 // check front is cached
2186 assert(blc.front is blc.front);
2190 private struct ByLineCopy(Char, Terminator)
2193 import std.typecons : RefCounted, RefCountedAutoInitialize;
2195 /* Ref-counting stops the source range's ByLineCopyImpl
2196 * from getting out of sync after the range is copied, e.g.
2197 * when accessing range.front, then using std.range.take,
2198 * then accessing range.front again. */
2199 alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
2200 RefCountedAutoInitialize.no);
2204 this(File f, KeepTerminator kt, Terminator terminator)
2206 impl = Impl(f, kt, terminator);
2209 @property bool empty()
2211 return impl.refCountedPayload.empty;
2214 @property Char[] front()
2216 return impl.refCountedPayload.front;
2221 impl.refCountedPayload.popFront();
2225 private struct ByLineCopyImpl(Char, Terminator)
2227 ByLine!(Unqual!Char, Terminator).Impl impl;
2232 this(File f, KeepTerminator kt, Terminator terminator)
2234 impl = ByLine!(Unqual!Char, Terminator).Impl(f, kt, terminator);
2237 @property bool empty()
2246 line = impl.front.dup;
2260 Returns an input range set up to read from the file handle one line
2261 at a time. Each line will be newly allocated. $(D front) will cache
2262 its value to allow repeated calls without unnecessary allocations.
2264 Note: Due to caching byLineCopy can be more memory-efficient than
2265 $(D File.byLine.map!idup).
2267 The element type for the range will be $(D Char[]). Range
2268 primitives may throw $(D StdioException) on I/O error.
2271 Char = Character type for each line, defaulting to $(D immutable char).
2272 keepTerminator = Use $(D Yes.keepTerminator) to include the
2273 terminator at the end of each line.
2274 terminator = Line separator ($(D '\n') by default). Use
2275 $(REF newline, std,ascii) for portability (unless the file was opened in
2280 import std.algorithm, std.array, std.stdio;
2281 // Print sorted lines of a file.
2284 auto sortedLines = File("file.txt") // Open for reading
2285 .byLineCopy() // Read persistent lines
2286 .array() // into an array
2287 .sort(); // then sort them
2288 foreach (line; sortedLines)
2293 $(REF readText, std,file)
2295 auto byLineCopy(Terminator = char, Char = immutable char)
2296 (KeepTerminator keepTerminator = No.keepTerminator,
2297 Terminator terminator = '\n')
2298 if (isScalarType!Terminator)
2300 return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2304 auto byLineCopy(Terminator, Char = immutable char)
2305 (KeepTerminator keepTerminator, Terminator terminator)
2306 if (is(Unqual!(ElementEncodingType!Terminator) == Unqual!Char))
2308 return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2313 static assert(is(typeof(File("").byLine.front) == char[]));
2314 static assert(is(typeof(File("").byLineCopy.front) == string));
2316 is(typeof(File("").byLineCopy!(char, char).front) == char[]));
2321 import std.algorithm.comparison : equal;
2322 static import std.file;
2324 scope(failure) printf("Failed test at line %d\n", __LINE__);
2325 auto deleteme = testFilename();
2326 std.file.write(deleteme, "");
2327 scope(success) std.file.remove(deleteme);
2330 auto f = File(deleteme);
2331 foreach (line; f.byLine())
2338 void test(Terminator)(string txt, in string[] witness,
2339 KeepTerminator kt, Terminator term, bool popFirstLine = false)
2341 import std.algorithm.sorting : sort;
2342 import std.array : array;
2343 import std.conv : text;
2344 import std.range.primitives : walkLength;
2347 std.file.write(deleteme, txt);
2348 auto f = File(deleteme);
2354 auto lines = f.byLine(kt, term);
2360 assert(lines.empty || lines.front is lines.front);
2361 foreach (line; lines)
2363 assert(line == witness[i++]);
2365 assert(i == witness.length, text(i, " != ", witness.length));
2368 auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
2369 assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
2371 // test persistent lines
2372 assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
2375 KeepTerminator kt = No.keepTerminator;
2376 test("", null, kt, '\n');
2377 test("\n", [ "" ], kt, '\n');
2378 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n');
2379 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true);
2380 test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n');
2381 test("foo", [ "foo" ], kt, '\n', true);
2382 test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
2384 test("sue\r", ["sue"], kt, '\r');
2386 kt = Yes.keepTerminator;
2387 test("", null, kt, '\n');
2388 test("\n", [ "\n" ], kt, '\n');
2389 test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n');
2390 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n');
2391 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true);
2392 test("foo", [ "foo" ], kt, '\n');
2393 test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
2395 test("sue\r", ["sue\r"], kt, '\r');
2400 import std.algorithm.comparison : equal;
2401 import std.range : drop, take;
2405 static import std.file;
2407 /* the C function tmpfile doesn't seem to work, even when called from C */
2408 auto deleteme = testFilename();
2409 auto file = File(deleteme, "w+");
2410 scope(success) std.file.remove(deleteme);
2412 else version (CRuntime_Bionic)
2414 static import std.file;
2416 /* the C function tmpfile doesn't work when called from a shared
2418 https://code.google.com/p/android/issues/detail?id=66815 */
2419 auto deleteme = testFilename();
2420 auto file = File(deleteme, "w+");
2421 scope(success) std.file.remove(deleteme);
2424 auto file = File.tmpfile();
2425 file.write("1\n2\n3\n");
2429 File.ByLine!(char, char) fbl = file.byLine();
2431 assert(fbl.front == "1");
2432 assert(fbl.front is fbl2.front);
2433 assert(fbl.take(1).equal(["1"]));
2434 assert(fbl.equal(["2", "3"]));
2436 assert(file.isOpen); // we still have a valid reference
2439 fbl = file.byLine();
2440 assert(!fbl.drop(2).empty);
2441 assert(fbl.equal(["3"]));
2443 assert(file.isOpen);
2446 assert(!file.isOpen);
2451 static import std.file;
2452 auto deleteme = testFilename();
2453 std.file.write(deleteme, "hi");
2454 scope(success) std.file.remove(deleteme);
2456 auto blc = File(deleteme).byLineCopy;
2458 // check front is cached
2459 assert(blc.front is blc.front);
2463 Creates an input range set up to parse one line at a time from the file
2466 Range primitives may throw $(D StdioException) on I/O error.
2469 format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
2472 The input range set up to parse one line at a time into a record tuple.
2476 It is similar to $(LREF byLine) and uses
2477 $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood.
2479 template byRecord(Fields...)
2481 ByRecord!(Fields) byRecord(string format)
2483 return typeof(return)(this, format);
2490 static import std.file;
2491 import std.typecons : tuple;
2493 // prepare test file
2494 auto testFile = testFilename();
2495 scope(failure) printf("Failed test at line %d\n", __LINE__);
2496 std.file.write(testFile, "1 2\n4 1\n5 100");
2497 scope(exit) std.file.remove(testFile);
2499 File f = File(testFile);
2500 scope(exit) f.close();
2502 auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
2504 foreach (e; f.byRecord!(int, int)("%s %s"))
2506 assert(e == expected[i++]);
2510 // Note: This was documented until 2013/08
2512 * Range that reads a chunk at a time.
2522 chunk_ = file_.rawRead(chunk_);
2523 if (chunk_.length == 0)
2528 this(File file, size_t size)
2530 this(file, new ubyte[](size));
2533 this(File file, ubyte[] buffer)
2535 import std.exception : enforce;
2536 enforce(buffer.length, "size must be larger than 0");
2542 // $(D ByChunk)'s input range primitive operations.
2546 return !file_.isOpen;
2555 import core.exception : RangeError;
2557 throw new RangeError();
2567 import core.exception : RangeError;
2569 throw new RangeError();
2576 Returns an input range set up to read from the file handle a chunk at a
2579 The element type for the range will be $(D ubyte[]). Range primitives
2580 may throw $(D StdioException) on I/O error.
2586 // Read standard input 4KB at a time
2587 foreach (ubyte[] buffer; stdin.byChunk(4096))
2594 The parameter may be a number (as shown in the example above) dictating the
2595 size of each chunk. Alternatively, $(D byChunk) accepts a
2596 user-provided buffer that it uses directly.
2602 // Read standard input 4KB at a time
2603 foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
2610 In either case, the content of the buffer is reused across calls. That means
2611 $(D front) will not persist after $(D popFront) is called, so if retention is
2612 needed, the caller must copy its contents (e.g. by calling $(D buffer.dup)).
2614 In the example above, $(D buffer.length) is 4096 for all iterations, except
2615 for the last one, in which case $(D buffer.length) may be less than 4096 (but
2616 always greater than zero).
2618 With the mentioned limitations, $(D byChunk) works with any algorithm
2619 compatible with input ranges.
2623 // Efficient file copy, 1MB at a time.
2624 import std.algorithm, std.stdio;
2627 stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
2631 $(REF joiner, std,algorithm,iteration) can be used to join chunks together into
2632 a single range lazily.
2635 import std.algorithm, std.stdio;
2639 static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
2641 static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
2645 Returns: A call to $(D byChunk) returns a range initialized with the $(D File)
2646 object and the appropriate buffer.
2648 Throws: If the user-provided size is zero or the user-provided buffer
2649 is empty, throws an $(D Exception). In case of an I/O error throws
2650 $(D StdioException).
2652 auto byChunk(size_t chunkSize)
2654 return ByChunk(this, chunkSize);
2657 ByChunk byChunk(ubyte[] buffer)
2659 return ByChunk(this, buffer);
2664 static import std.file;
2666 scope(failure) printf("Failed test at line %d\n", __LINE__);
2668 auto deleteme = testFilename();
2669 std.file.write(deleteme, "asd\ndef\nasdf");
2671 auto witness = ["asd\n", "def\n", "asdf" ];
2672 auto f = File(deleteme);
2677 std.file.remove(deleteme);
2681 foreach (chunk; f.byChunk(4))
2682 assert(chunk == cast(ubyte[]) witness[i++]);
2684 assert(i == witness.length);
2689 static import std.file;
2691 scope(failure) printf("Failed test at line %d\n", __LINE__);
2693 auto deleteme = testFilename();
2694 std.file.write(deleteme, "asd\ndef\nasdf");
2696 auto witness = ["asd\n", "def\n", "asdf" ];
2697 auto f = File(deleteme);
2702 std.file.remove(deleteme);
2706 foreach (chunk; f.byChunk(new ubyte[4]))
2707 assert(chunk == cast(ubyte[]) witness[i++]);
2709 assert(i == witness.length);
2712 // Note: This was documented until 2013/08
2714 $(D Range) that locks the file and allows fast writing to it.
2716 struct LockingTextWriter
2719 import std.range.primitives : ElementType, isInfinite, isInputRange;
2720 // the shared file handle
2723 // the unshared version of fps
2724 @property _iobuf* handle_() @trusted { return cast(_iobuf*) fps_; }
2726 // the file's orientation (byte- or wide-oriented)
2730 this(ref File f) @trusted
2732 import core.stdc.wchar_ : fwide;
2733 import std.exception : enforce;
2735 enforce(f._p && f._p.handle, "Attempting to write to closed File");
2737 orientation_ = fwide(fps_, 0);
2758 /// Range primitive implementations.
2759 void put(A)(A writeme)
2760 if ((isSomeChar!(Unqual!(ElementType!A)) ||
2761 is(ElementType!A : const(ubyte))) &&
2765 import std.exception : errnoEnforce;
2767 alias C = ElementEncodingType!A;
2768 static assert(!is(C == void));
2769 static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[]))
2771 if (orientation_ <= 0)
2773 //file.write(writeme); causes infinite recursion!!!
2774 //file.rawWrite(writeme);
2775 auto result = trustedFwrite(fps_, writeme);
2776 if (result != writeme.length) errnoEnforce(0);
2781 // put each element in turn.
2782 alias Elem = Unqual!(ElementType!A);
2783 foreach (Elem c; writeme)
2790 void put(C)(C c) @safe if (isSomeChar!C || is(C : const(ubyte)))
2792 import std.traits : Parameters;
2793 static auto trustedFPUTC(int ch, _iobuf* h) @trusted
2795 return FPUTC(ch, h);
2797 static auto trustedFPUTWC(Parameters!FPUTWC[0] ch, _iobuf* h) @trusted
2799 return FPUTWC(ch, h);
2802 static if (c.sizeof == 1)
2805 if (orientation_ <= 0) trustedFPUTC(c, handle_);
2806 else trustedFPUTWC(c, handle_);
2808 else static if (c.sizeof == 2)
2810 import std.utf : encode, UseReplacementDchar;
2812 if (orientation_ <= 0)
2816 trustedFPUTC(c, handle_);
2821 immutable size = encode!(UseReplacementDchar.yes)(buf, c);
2822 foreach (i ; 0 .. size)
2823 trustedFPUTC(buf[i], handle_);
2828 trustedFPUTWC(c, handle_);
2831 else // 32-bit characters
2833 import std.utf : encode;
2835 if (orientation_ <= 0)
2839 trustedFPUTC(c, handle_);
2844 immutable len = encode(buf, c);
2845 foreach (i ; 0 .. len)
2846 trustedFPUTC(buf[i], handle_);
2853 import std.utf : isValidDchar;
2855 assert(isValidDchar(c));
2858 trustedFPUTWC(c, handle_);
2862 trustedFPUTWC(cast(wchar)
2863 ((((c - 0x10000) >> 10) & 0x3FF)
2864 + 0xD800), handle_);
2865 trustedFPUTWC(cast(wchar)
2866 (((c - 0x10000) & 0x3FF) + 0xDC00),
2870 else version (Posix)
2872 trustedFPUTWC(c, handle_);
2883 /** Returns an output range that locks the file and allows fast writing to it.
2885 See $(LREF byChunk) for an example.
2887 auto lockingTextWriter() @safe
2889 return LockingTextWriter(this);
2892 // An output range which optionally locks the file and puts it into
2893 // binary mode (similar to rawWrite). Because it needs to restore
2894 // the file mode on destruction, it is RefCounted on Windows.
2895 struct BinaryWriterImpl(bool locking)
2897 import std.traits : hasIndirections;
2905 version (DIGITAL_MARS_STDIO)
2912 import std.exception : enforce;
2914 enforce(f._p && f._p.handle);
2922 .fflush(fps); // before changing translation mode
2924 oldMode = ._setmode(fd, _O_BINARY);
2925 version (DIGITAL_MARS_STDIO)
2927 import core.atomic : atomicOp;
2930 oldInfo = __fhnd_info[fd];
2931 atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
2944 .fflush(fps); // before restoring translation mode
2945 version (DIGITAL_MARS_STDIO)
2948 __fhnd_info[fd] = oldInfo;
2950 ._setmode(fd, oldMode);
2957 void rawWrite(T)(in T[] buffer)
2959 import std.conv : text;
2960 import std.exception : errnoEnforce;
2962 auto result = trustedFwrite(fps, buffer);
2963 if (result == result.max) result = 0;
2964 errnoEnforce(result == buffer.length,
2965 text("Wrote ", result, " instead of ", buffer.length,
2966 " objects of type ", T.stringof, " to file `",
2972 @disable this(this);
2985 void put(T)(auto ref in T value)
2986 if (!hasIndirections!T &&
2989 rawWrite((&value)[0 .. 1]);
2992 void put(T)(in T[] array)
2993 if (!hasIndirections!T &&
3000 /** Returns an output range that locks the file and allows fast writing to it.
3003 Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
3004 in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
3006 import std.algorithm, std.range, std.stdio;
3011 writef("P5\n%d %d %d\n", size, size, ubyte.max);
3013 iota(-1, 3, 2.0/size).map!(y =>
3014 iota(-1.5, 0.5, 2.0/size).map!(x =>
3016 recurrence!((a, n) => x + y*1i + a[n-1]^^2)(0+0i)
3018 .countUntil!(z => z.re^^2 + z.im^^2 > 4))
3021 .copy(stdout.lockingBinaryWriter);
3025 auto lockingBinaryWriter()
3027 alias LockingBinaryWriterImpl = BinaryWriterImpl!true;
3031 import std.typecons : RefCounted;
3032 alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl;
3035 alias LockingBinaryWriter = LockingBinaryWriterImpl;
3037 return LockingBinaryWriter(this);
3042 import std.algorithm.mutation : reverse;
3043 import std.exception : collectException;
3044 static import std.file;
3045 import std.range : only, retro;
3046 import std.string : format;
3048 auto deleteme = testFilename();
3049 scope(exit) collectException(std.file.remove(deleteme));
3050 auto output = File(deleteme, "wb");
3051 auto writer = output.lockingBinaryWriter();
3052 auto input = File(deleteme, "rb");
3054 T[] readExact(T)(T[] buf)
3056 auto result = input.rawRead(buf);
3057 assert(result.length == buf.length,
3058 "Read %d out of %d bytes"
3059 .format(result.length, buf.length));
3065 byteIn.only.copy(writer); output.flush();
3066 ubyte byteOut = readExact(new ubyte[1])[0];
3067 assert(byteIn == byteOut);
3070 ubyte[] bytesIn = [1, 2, 3, 4, 5];
3071 bytesIn.copy(writer); output.flush();
3072 ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]);
3073 scope(failure) .writeln(bytesOut);
3074 assert(bytesIn == bytesOut);
3076 // test ranges of values
3077 bytesIn.retro.copy(writer); output.flush();
3078 bytesOut = readExact(bytesOut);
3080 assert(bytesIn == bytesOut);
3083 "foobar".copy(writer); output.flush();
3084 char[] charsOut = readExact(new char[6]);
3085 assert(charsOut == "foobar");
3087 // test ranges of arrays
3088 only("foo", "bar").copy(writer); output.flush();
3089 charsOut = readExact(charsOut);
3090 assert(charsOut == "foobar");
3092 // test that we are writing arrays as is,
3093 // without UTF-8 transcoding
3094 "foo"d.copy(writer); output.flush();
3095 dchar[] dcharsOut = readExact(new dchar[3]);
3096 assert(dcharsOut == "foo");
3099 /// Get the size of the file, ulong.max if file is not searchable, but still throws if an actual error occurs.
3100 @property ulong size() @safe
3102 import std.exception : collectException;
3105 if (collectException(pos = tell)) return ulong.max;
3106 scope(exit) seek(pos);
3114 @system struct SystemToString
3122 @trusted struct TrustedToString
3130 @safe struct SafeToString
3138 @system void systemTests()
3140 //system code can write to files/stdout with anything!
3145 f.write("just a string");
3146 f.write("string with arg: ", 47);
3147 f.write(SystemToString());
3148 f.write(TrustedToString());
3149 f.write(SafeToString());
3151 write("just a string");
3152 write("string with arg: ", 47);
3153 write(SystemToString());
3154 write(TrustedToString());
3155 write(SafeToString());
3157 f.writeln("just a string");
3158 f.writeln("string with arg: ", 47);
3159 f.writeln(SystemToString());
3160 f.writeln(TrustedToString());
3161 f.writeln(SafeToString());
3163 writeln("just a string");
3164 writeln("string with arg: ", 47);
3165 writeln(SystemToString());
3166 writeln(TrustedToString());
3167 writeln(SafeToString());
3169 f.writef("string with arg: %s", 47);
3170 f.writef("%s", SystemToString());
3171 f.writef("%s", TrustedToString());
3172 f.writef("%s", SafeToString());
3174 writef("string with arg: %s", 47);
3175 writef("%s", SystemToString());
3176 writef("%s", TrustedToString());
3177 writef("%s", SafeToString());
3179 f.writefln("string with arg: %s", 47);
3180 f.writefln("%s", SystemToString());
3181 f.writefln("%s", TrustedToString());
3182 f.writefln("%s", SafeToString());
3184 writefln("string with arg: %s", 47);
3185 writefln("%s", SystemToString());
3186 writefln("%s", TrustedToString());
3187 writefln("%s", SafeToString());
3191 @safe void safeTests()
3195 //safe code can write to files only with @safe and @trusted code...
3198 f.write("just a string");
3199 f.write("string with arg: ", 47);
3200 f.write(TrustedToString());
3201 f.write(SafeToString());
3203 write("just a string");
3204 write("string with arg: ", 47);
3205 write(TrustedToString());
3206 write(SafeToString());
3208 f.writeln("just a string");
3209 f.writeln("string with arg: ", 47);
3210 f.writeln(TrustedToString());
3211 f.writeln(SafeToString());
3213 writeln("just a string");
3214 writeln("string with arg: ", 47);
3215 writeln(TrustedToString());
3216 writeln(SafeToString());
3218 f.writef("string with arg: %s", 47);
3219 f.writef("%s", TrustedToString());
3220 f.writef("%s", SafeToString());
3222 writef("string with arg: %s", 47);
3223 writef("%s", TrustedToString());
3224 writef("%s", SafeToString());
3226 f.writefln("string with arg: %s", 47);
3227 f.writefln("%s", TrustedToString());
3228 f.writefln("%s", SafeToString());
3230 writefln("string with arg: %s", 47);
3231 writefln("%s", TrustedToString());
3232 writefln("%s", SafeToString());
3235 static assert(!__traits(compiles, f.write(SystemToString().toString())));
3236 static assert(!__traits(compiles, f.writeln(SystemToString())));
3237 static assert(!__traits(compiles, f.writef("%s", SystemToString())));
3238 static assert(!__traits(compiles, f.writefln("%s", SystemToString())));
3240 static assert(!__traits(compiles, write(SystemToString().toString())));
3241 static assert(!__traits(compiles, writeln(SystemToString())));
3242 static assert(!__traits(compiles, writef("%s", SystemToString())));
3243 static assert(!__traits(compiles, writefln("%s", SystemToString())));
3252 import std.exception : collectException;
3253 static import std.file;
3255 auto deleteme = testFilename();
3256 scope(exit) collectException(std.file.remove(deleteme));
3257 std.file.write(deleteme, "1 2 3");
3258 auto f = File(deleteme);
3259 assert(f.size == 5);
3260 assert(f.tell == 0);
3265 // @system due to readln
3266 static import std.file;
3267 import std.range : chain, only, repeat;
3268 import std.range.primitives : isOutputRange;
3270 auto deleteme = testFilename();
3271 scope(exit) std.file.remove(deleteme);
3274 File f = File(deleteme, "w");
3275 auto writer = f.lockingTextWriter();
3276 static assert(isOutputRange!(typeof(writer), dchar));
3281 writer.put(chain(only('本'), only('語')));
3282 writer.put(repeat('#', 12)); // BUG 11945
3283 writer.put(cast(immutable(ubyte)[])"日本語"); // Bug 17229
3285 assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語");
3290 import std.exception : collectException;
3291 auto e = collectException({ File f; f.writeln("Hello!"); }());
3292 assert(e && e.msg == "Attempting to write to closed File");
3295 /// Used to specify the lock type for $(D File.lock) and $(D File.tryLock).
3299 * Specifies a _read (shared) lock. A _read lock denies all processes
3300 * write access to the specified region of the file, including the
3301 * process that first locks the region. All processes can _read the
3302 * locked region. Multiple simultaneous _read locks are allowed, as
3303 * long as there are no exclusive locks.
3308 * Specifies a read/write (exclusive) lock. A read/write lock denies all
3309 * other processes both read and write access to the locked file region.
3310 * If a segment has an exclusive lock, it may not have any shared locks
3311 * or other exclusive locks.
3316 struct LockingTextReader
3319 private char _front;
3320 private bool _hasChar;
3324 import std.exception : enforce;
3325 enforce(f.isOpen, "LockingTextReader: File must be open");
3327 FLOCK(_f._p.handle);
3332 FLOCK(_f._p.handle);
3338 ungetc(_front, cast(FILE*)_f._p.handle);
3340 // File locking has its own reference count
3341 if (_f.isOpen) FUNLOCK(_f._p.handle);
3344 void opAssign(LockingTextReader r)
3346 import std.algorithm.mutation : swap;
3350 @property bool empty()
3354 if (!_f.isOpen || _f.eof)
3356 immutable int c = FGETC(cast(_iobuf*) _f._p.handle);
3362 _front = cast(char) c;
3368 @property char front()
3374 import core.exception : RangeError;
3376 throw new RangeError();
3396 // @system due to readf
3397 static import std.file;
3398 import std.range.primitives : isInputRange;
3400 static assert(isInputRange!LockingTextReader);
3401 auto deleteme = testFilename();
3402 std.file.write(deleteme, "1 2 3");
3403 scope(exit) std.file.remove(deleteme);
3405 auto f = File(deleteme);
3414 @system unittest // bugzilla 13686
3416 import std.algorithm.comparison : equal;
3417 static import std.file;
3418 import std.utf : byDchar;
3420 auto deleteme = testFilename();
3421 std.file.write(deleteme, "Тест");
3422 scope(exit) std.file.remove(deleteme);
3425 File(deleteme).readf("%s", &s);
3426 assert(s == "Тест");
3428 auto ltr = LockingTextReader(File(deleteme)).byDchar;
3429 assert(equal(ltr, "Тест".byDchar));
3432 @system unittest // bugzilla 12320
3434 static import std.file;
3435 auto deleteme = testFilename();
3436 std.file.write(deleteme, "ab");
3437 scope(exit) std.file.remove(deleteme);
3438 auto ltr = LockingTextReader(File(deleteme));
3439 assert(ltr.front == 'a');
3441 assert(ltr.front == 'b');
3446 @system unittest // bugzilla 14861
3448 // @system due to readf
3449 static import std.file;
3450 auto deleteme = testFilename();
3451 File fw = File(deleteme, "w");
3452 for (int i; i != 5000; i++)
3453 fw.writeln(i, ";", "Иванов;Пётр;Петрович");
3455 scope(exit) std.file.remove(deleteme);
3457 File fr = File(deleteme, "r");
3458 scope (exit) fr.close();
3459 int nom; string fam, nam, ot;
3460 // Error format read
3462 fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot);
3466 * Indicates whether $(D T) is a file handle, i.e. the type
3467 * is implicitly convertable to $(LREF File) or a pointer to a
3468 * $(REF FILE, core,stdc,stdio).
3471 * `true` if `T` is a file handle, `false` otherwise.
3473 template isFileHandle(T)
3475 enum isFileHandle = is(T : FILE*) ||
3482 static assert(isFileHandle!(FILE*));
3483 static assert(isFileHandle!(File));
3487 * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
3489 private @property File trustedStdout() @trusted
3494 /***********************************
3495 For each argument $(D arg) in $(D args), format the argument (using
3496 $(REF to, std,conv)) and write the resulting
3497 string to $(D args[0]). A call without any arguments will fail to
3501 args = the items to write to `stdout`
3503 Throws: In case of an I/O error, throws an $(D StdioException).
3506 Reads `stdin` and writes it to `stdout` with an argument
3515 for (size_t count = 0; (line = readln) !is null; count++)
3517 write("Input ", count, ": ", line, "\n");
3522 void write(T...)(T args)
3523 if (!is(T[0] : File))
3525 trustedStdout.write(args);
3530 static import std.file;
3532 scope(failure) printf("Failed test at line %d\n", __LINE__);
3534 if (false) write(buf);
3536 auto deleteme = testFilename();
3537 auto f = File(deleteme, "w");
3538 f.write("Hello, ", "world number ", 42, "!");
3540 scope(exit) { std.file.remove(deleteme); }
3541 assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!");
3544 /***********************************
3545 * Equivalent to `write(args, '\n')`. Calling `writeln` without
3546 * arguments is valid and just prints a newline to the standard
3550 * args = the items to write to `stdout`
3553 * In case of an I/O error, throws an $(LREF StdioException).
3555 * Reads $(D stdin) and writes it to $(D stdout) with a argument
3564 for (size_t count = 0; (line = readln) !is null; count++)
3566 writeln("Input ", count, ": ", line);
3571 void writeln(T...)(T args)
3573 import std.traits : isAggregateType;
3574 static if (T.length == 0)
3576 import std.exception : enforce;
3578 enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
3580 else static if (T.length == 1 &&
3581 is(typeof(args[0]) : const(char)[]) &&
3582 !is(typeof(args[0]) == enum) &&
3583 !is(Unqual!(typeof(args[0])) == typeof(null)) &&
3584 !isAggregateType!(typeof(args[0])))
3586 import std.traits : isStaticArray;
3588 // Specialization for strings - a very frequent case
3589 auto w = .trustedStdout.lockingTextWriter();
3591 static if (isStaticArray!(typeof(args[0])))
3603 // Most general instance
3604 trustedStdout.write(args, '\n');
3610 // Just make sure the call compiles
3611 if (false) writeln();
3613 if (false) writeln("wyda");
3616 if (false) writeln(null);
3617 if (false) writeln(">", null, "<");
3629 static import std.file;
3631 scope(failure) printf("Failed test at line %d\n", __LINE__);
3634 auto deleteme = testFilename();
3635 auto f = File(deleteme, "w");
3636 scope(exit) { std.file.remove(deleteme); }
3637 f.writeln("Hello, ", "world number ", 42, "!");
3640 assert(cast(char[]) std.file.read(deleteme) ==
3641 "Hello, world number 42!\r\n");
3643 assert(cast(char[]) std.file.read(deleteme) ==
3644 "Hello, world number 42!\n");
3646 // test writeln on stdout
3647 auto saveStdout = stdout;
3648 scope(exit) stdout = saveStdout;
3649 stdout.open(deleteme, "w");
3650 writeln("Hello, ", "world number ", 42, "!");
3653 assert(cast(char[]) std.file.read(deleteme) ==
3654 "Hello, world number 42!\r\n");
3656 assert(cast(char[]) std.file.read(deleteme) ==
3657 "Hello, world number 42!\n");
3659 stdout.open(deleteme, "w");
3661 writeln("Hello!"w); // bug 8386
3662 writeln("Hello!"d); // bug 8386
3663 writeln("embedded\0null"c); // bug 8730
3666 assert(cast(char[]) std.file.read(deleteme) ==
3667 "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
3669 assert(cast(char[]) std.file.read(deleteme) ==
3670 "Hello!\nHello!\nHello!\nembedded\0null\n");
3675 static import std.file;
3677 auto deleteme = testFilename();
3678 auto f = File(deleteme, "w");
3679 scope(exit) { std.file.remove(deleteme); }
3681 enum EI : int { A, B }
3682 enum ED : double { A, B }
3683 enum EC : char { A, B }
3684 enum ES : string { A = "aaa", B = "bbb" }
3686 f.writeln(EI.A); // false, but A on 2.058
3687 f.writeln(EI.B); // true, but B on 2.058
3689 f.writeln(ED.A); // A
3690 f.writeln(ED.B); // B
3692 f.writeln(EC.A); // A
3693 f.writeln(EC.B); // B
3695 f.writeln(ES.A); // A
3696 f.writeln(ES.B); // B
3700 assert(cast(char[]) std.file.read(deleteme) ==
3701 "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
3703 assert(cast(char[]) std.file.read(deleteme) ==
3704 "A\nB\nA\nB\nA\nB\nA\nB\n");
3709 static auto useInit(T)(T ltw)
3716 useInit(stdout.lockingTextWriter());
3720 /***********************************
3721 Writes formatted data to standard output (without a trailing newline).
3724 fmt = The $(LINK2 std_format.html#format-string, format string).
3725 When passed as a compile-time argument, the string will be statically checked
3726 against the argument types passed.
3727 args = Items to write.
3729 Note: In older versions of Phobos, it used to be possible to write:
3732 writef(stderr, "%s", "message");
3735 to print a message to $(D stderr). This syntax is no longer supported, and has
3739 stderr.writef("%s", "message");
3743 void writef(alias fmt, A...)(A args)
3744 if (isSomeString!(typeof(fmt)))
3746 import std.format : checkFormatException;
3748 alias e = checkFormatException!(fmt, A);
3749 static assert(!e, e.msg);
3750 return .writef(fmt, args);
3754 void writef(Char, A...)(in Char[] fmt, A args)
3756 trustedStdout.writef(fmt, args);
3761 static import std.file;
3763 scope(failure) printf("Failed test at line %d\n", __LINE__);
3766 auto deleteme = testFilename();
3767 auto f = File(deleteme, "w");
3768 scope(exit) { std.file.remove(deleteme); }
3769 f.writef!"Hello, %s world number %s!"("nice", 42);
3771 assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
3772 // test write on stdout
3773 auto saveStdout = stdout;
3774 scope(exit) stdout = saveStdout;
3775 stdout.open(deleteme, "w");
3776 writef!"Hello, %s world number %s!"("nice", 42);
3778 assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
3781 /***********************************
3782 * Equivalent to $(D writef(fmt, args, '\n')).
3784 void writefln(alias fmt, A...)(A args)
3785 if (isSomeString!(typeof(fmt)))
3787 import std.format : checkFormatException;
3789 alias e = checkFormatException!(fmt, A);
3790 static assert(!e, e.msg);
3791 return .writefln(fmt, args);
3795 void writefln(Char, A...)(in Char[] fmt, A args)
3797 trustedStdout.writefln(fmt, args);
3802 static import std.file;
3804 scope(failure) printf("Failed test at line %d\n", __LINE__);
3806 // test File.writefln
3807 auto deleteme = testFilename();
3808 auto f = File(deleteme, "w");
3809 scope(exit) { std.file.remove(deleteme); }
3810 f.writefln!"Hello, %s world number %s!"("nice", 42);
3813 assert(cast(char[]) std.file.read(deleteme) ==
3814 "Hello, nice world number 42!\r\n");
3816 assert(cast(char[]) std.file.read(deleteme) ==
3817 "Hello, nice world number 42!\n",
3818 cast(char[]) std.file.read(deleteme));
3821 auto saveStdout = stdout;
3822 scope(exit) stdout = saveStdout;
3823 stdout.open(deleteme, "w");
3824 writefln!"Hello, %s world number %s!"("nice", 42);
3827 assert(cast(char[]) std.file.read(deleteme) ==
3828 "Hello, nice world number 42!\r\n");
3830 assert(cast(char[]) std.file.read(deleteme) ==
3831 "Hello, nice world number 42!\n");
3835 * Reads formatted data from $(D stdin) using $(REF formattedRead, std,_format).
3837 * format = The $(LINK2 std_format.html#_format-string, _format string).
3838 * When passed as a compile-time argument, the string will be statically checked
3839 * against the argument types passed.
3840 * args = Items to be read.
3856 % echo "1 2 3" | rdmd test.d
3862 uint readf(alias format, A...)(auto ref A args)
3863 if (isSomeString!(typeof(format)))
3865 import std.format : checkFormatException;
3867 alias e = checkFormatException!(format, A);
3868 static assert(!e, e.msg);
3869 return .readf(format, args);
3873 uint readf(A...)(in char[] format, auto ref A args)
3875 return stdin.readf(format, args);
3881 if (false) uint x = readf("%s", &f);
3886 if (false) readf("%s %s %s", a, b, c);
3887 // backwards compatibility with pointers
3888 if (false) readf("%s %s %s", a, &b, c);
3889 if (false) readf("%s %s %s", &a, &b, &c);
3892 /**********************************
3893 * Read line from $(D stdin).
3895 * This version manages its own read buffer, which means one memory allocation per call. If you are not
3896 * retaining a reference to the read data, consider the $(D readln(buf)) version, which may offer
3897 * better performance as it can reuse its read buffer.
3900 * The line that was read, including the line terminator character.
3902 * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to $(D string).
3903 * terminator = Line terminator (by default, $(D '\n')).
3905 * String terminators are not supported due to ambiguity with readln(buf) below.
3907 * $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
3909 * Reads $(D stdin) and writes it to $(D stdout).
3916 while ((line = readln()) !is null)
3921 S readln(S = string)(dchar terminator = '\n')
3924 return stdin.readln!S(terminator);
3927 /**********************************
3928 * Read line from $(D stdin) and write it to buf[], including terminating character.
3930 * This can be faster than $(D line = readln()) because you can reuse
3931 * the buffer for each call. Note that reusing the buffer means that you
3932 * must copy the previous contents if you wish to retain them.
3935 * $(D size_t) 0 for end of file, otherwise number of characters read
3937 * buf = Buffer used to store the resulting line data. buf is resized as necessary.
3938 * terminator = Line terminator (by default, $(D '\n')). Use $(REF newline, std,ascii)
3939 * for portability (unless the file was opened in text mode).
3941 * $(D StdioException) on I/O error, or $(D UnicodeException) on Unicode conversion error.
3943 * Reads $(D stdin) and writes it to $(D stdout).
3955 size_t readln(C)(ref C[] buf, dchar terminator = '\n')
3956 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
3958 return stdin.readln(buf, terminator);
3962 size_t readln(C, R)(ref C[] buf, R terminator)
3963 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
3964 isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
3966 return stdin.readln(buf, terminator);
3971 import std.meta : AliasSeq;
3973 //we can't actually test readln, so at the very least,
3974 //we test compilability
3979 foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
3982 readln!String('\t');
3984 foreach (String; AliasSeq!(char[], wchar[], dchar[]))
3989 readln(buf, "<br />");
3995 * Convenience function that forwards to $(D core.sys.posix.stdio.fopen)
3996 * (to $(D _wfopen) on Windows)
3997 * with appropriately-constructed C-style strings.
3999 private FILE* fopen(R1, R2)(R1 name, R2 mode = "r")
4000 if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) &&
4001 (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2))
4003 import std.internal.cstring : tempCString;
4005 auto namez = name.tempCString!FSChar();
4006 auto modez = mode.tempCString!FSChar();
4008 static fopenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
4012 return _wfopen(namez, modez);
4014 else version (Posix)
4017 * The new opengroup large file support API is transparently
4018 * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0
4019 * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
4020 * the normal functions work fine. If not, then large file support
4021 * probably isn't available. Do not use the old transitional API
4022 * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0)
4024 import core.sys.posix.stdio : fopen;
4025 return fopen(namez, modez);
4029 return .fopen(namez, modez);
4032 return fopenImpl(namez, modez);
4037 /***********************************
4038 * Convenience function that forwards to $(D core.sys.posix.stdio.popen)
4039 * with appropriately-constructed C-style strings.
4041 FILE* popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
4042 if ((isInputRange!R1 && isSomeChar!(ElementEncodingType!R1) || isSomeString!R1) &&
4043 (isInputRange!R2 && isSomeChar!(ElementEncodingType!R2) || isSomeString!R2))
4045 import std.internal.cstring : tempCString;
4047 auto namez = name.tempCString!FSChar();
4048 auto modez = mode.tempCString!FSChar();
4050 static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
4052 import core.sys.posix.stdio : popen;
4053 return popen(namez, modez);
4055 return popenImpl(namez, modez);
4060 * Convenience function that forwards to $(D core.stdc.stdio.fwrite)
4062 private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
4064 return fwrite(obj.ptr, T.sizeof, obj.length, f);
4068 * Convenience function that forwards to $(D core.stdc.stdio.fread)
4070 private auto trustedFread(T)(FILE* f, T[] obj) @trusted
4072 return fread(obj.ptr, T.sizeof, obj.length, f);
4076 * Iterates through the lines of a file by using $(D foreach).
4083 foreach (string line; lines(stdin))
4089 The line terminator ($(D '\n') by default) is part of the string read (it
4090 could be missing in the last line of the file). Several types are
4091 supported for $(D line), and the behavior of $(D lines)
4092 changes accordingly:
4094 $(OL $(LI If $(D line) has type $(D string), $(D
4095 wstring), or $(D dstring), a new string of the respective type
4096 is allocated every read.) $(LI If $(D line) has type $(D
4097 char[]), $(D wchar[]), $(D dchar[]), the line's content
4098 will be reused (overwritten) across reads.) $(LI If $(D line)
4099 has type $(D immutable(ubyte)[]), the behavior is similar to
4100 case (1), except that no UTF checking is attempted upon input.) $(LI
4101 If $(D line) has type $(D ubyte[]), the behavior is
4102 similar to case (2), except that no UTF checking is attempted upon
4105 In all cases, a two-symbols versions is also accepted, in which case
4106 the first symbol (of integral type, e.g. $(D ulong) or $(D
4107 uint)) tracks the zero-based number of the current line.
4111 foreach (ulong i, string line; lines(stdin))
4117 In case of an I/O error, an $(D StdioException) is thrown.
4126 private dchar terminator = '\n';
4131 f = File to read lines from.
4132 terminator = Line separator ($(D '\n') by default).
4134 this(File f, dchar terminator = '\n')
4137 this.terminator = terminator;
4140 int opApply(D)(scope D dg)
4142 import std.traits : Parameters;
4143 alias Parms = Parameters!(dg);
4144 static if (isSomeString!(Parms[$ - 1]))
4146 enum bool duplicate = is(Parms[$ - 1] == string)
4147 || is(Parms[$ - 1] == wstring) || is(Parms[$ - 1] == dstring);
4149 static if (is(Parms[$ - 1] : const(char)[]))
4151 else static if (is(Parms[$ - 1] : const(wchar)[]))
4153 else static if (is(Parms[$ - 1] : const(dchar)[]))
4156 static if (Parms.length == 2)
4160 import std.conv : to;
4162 if (!f.readln(line, terminator)) break;
4163 auto copy = to!(Parms[$ - 1])(line);
4164 static if (Parms.length == 2)
4166 result = dg(i, copy);
4173 if (result != 0) break;
4180 return opApplyRaw(dg);
4184 int opApplyRaw(D)(scope D dg)
4186 import std.conv : to;
4187 import std.exception : assumeUnique;
4188 import std.traits : Parameters;
4190 alias Parms = Parameters!(dg);
4191 enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
4195 scope(exit) FUNLOCK(f._p.handle);
4197 static if (Parms.length == 2)
4199 while ((c = FGETC(cast(_iobuf*) f._p.handle)) != -1)
4201 buffer ~= to!(ubyte)(c);
4202 if (c == terminator)
4204 static if (duplicate)
4205 auto arg = assumeUnique(buffer);
4208 // unlock the file while calling the delegate
4209 FUNLOCK(f._p.handle);
4210 scope(exit) FLOCK(f._p.handle);
4211 static if (Parms.length == 1)
4217 result = dg(line, arg);
4221 static if (!duplicate)
4225 // can only reach when FGETC returned -1
4226 if (!f.eof) throw new StdioException("Error in reading file"); // error occured
4233 static import std.file;
4234 import std.meta : AliasSeq;
4236 scope(failure) printf("Failed test at line %d\n", __LINE__);
4238 auto deleteme = testFilename();
4239 scope(exit) { std.file.remove(deleteme); }
4242 AliasSeq!(string, wstring, dstring,
4243 char[], wchar[], dchar[]);
4244 foreach (T; TestedWith)
4246 // test looping with an empty file
4247 std.file.write(deleteme, "");
4248 auto f = File(deleteme, "r");
4249 foreach (T line; lines(f))
4255 // test looping with a file with three lines
4256 std.file.write(deleteme, "Line one\nline two\nline three\n");
4257 f.open(deleteme, "r");
4259 foreach (T line; lines(f))
4261 if (i == 0) assert(line == "Line one\n");
4262 else if (i == 1) assert(line == "line two\n");
4263 else if (i == 2) assert(line == "line three\n");
4269 // test looping with a file with three lines, last without a newline
4270 std.file.write(deleteme, "Line one\nline two\nline three");
4271 f.open(deleteme, "r");
4273 foreach (T line; lines(f))
4275 if (i == 0) assert(line == "Line one\n");
4276 else if (i == 1) assert(line == "line two\n");
4277 else if (i == 2) assert(line == "line three");
4284 // test with ubyte[] inputs
4285 alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]);
4286 foreach (T; TestedWith2)
4288 // test looping with an empty file
4289 std.file.write(deleteme, "");
4290 auto f = File(deleteme, "r");
4291 foreach (T line; lines(f))
4297 // test looping with a file with three lines
4298 std.file.write(deleteme, "Line one\nline two\nline three\n");
4299 f.open(deleteme, "r");
4301 foreach (T line; lines(f))
4303 if (i == 0) assert(cast(char[]) line == "Line one\n");
4304 else if (i == 1) assert(cast(char[]) line == "line two\n",
4305 T.stringof ~ " " ~ cast(char[]) line);
4306 else if (i == 2) assert(cast(char[]) line == "line three\n");
4312 // test looping with a file with three lines, last without a newline
4313 std.file.write(deleteme, "Line one\nline two\nline three");
4314 f.open(deleteme, "r");
4316 foreach (T line; lines(f))
4318 if (i == 0) assert(cast(char[]) line == "Line one\n");
4319 else if (i == 1) assert(cast(char[]) line == "line two\n");
4320 else if (i == 2) assert(cast(char[]) line == "line three");
4328 foreach (T; AliasSeq!(ubyte[]))
4330 // test looping with a file with three lines, last without a newline
4331 // using a counter too this time
4332 std.file.write(deleteme, "Line one\nline two\nline three");
4333 auto f = File(deleteme, "r");
4335 foreach (ulong j, T line; lines(f))
4337 if (i == 0) assert(cast(char[]) line == "Line one\n");
4338 else if (i == 1) assert(cast(char[]) line == "line two\n");
4339 else if (i == 2) assert(cast(char[]) line == "line three");
4348 Iterates through a file a chunk at a time by using $(D foreach).
4355 foreach (ubyte[] buffer; chunks(stdin, 4096))
4362 The content of $(D buffer) is reused across calls. In the
4363 example above, $(D buffer.length) is 4096 for all iterations,
4364 except for the last one, in which case $(D buffer.length) may
4365 be less than 4096 (but always greater than zero).
4367 In case of an I/O error, an $(D StdioException) is thrown.
4369 auto chunks(File f, size_t size)
4371 return ChunksImpl(f, size);
4373 private struct ChunksImpl
4376 private size_t size;
4377 // private string fileName; // Currently, no use
4379 this(File f, size_t size)
4382 assert(size, "size must be larger than 0");
4390 int opApply(D)(scope D dg)
4392 import core.stdc.stdlib : alloca;
4393 enum maxStackSize = 1024 * 16;
4394 ubyte[] buffer = void;
4395 if (size < maxStackSize)
4396 buffer = (cast(ubyte*) alloca(size))[0 .. size];
4398 buffer = new ubyte[size];
4402 while ((r = trustedFread(f._p.handle, buffer)) > 0)
4408 if (!f.eof) throw new StdioException(null);
4411 static if (is(typeof(dg(tally, buffer))))
4413 if ((result = dg(tally, buffer)) != 0) break;
4417 if ((result = dg(buffer)) != 0) break;
4427 static import std.file;
4429 scope(failure) printf("Failed test at line %d\n", __LINE__);
4431 auto deleteme = testFilename();
4432 scope(exit) { std.file.remove(deleteme); }
4434 // test looping with an empty file
4435 std.file.write(deleteme, "");
4436 auto f = File(deleteme, "r");
4437 foreach (ubyte[] line; chunks(f, 4))
4443 // test looping with a file with three lines
4444 std.file.write(deleteme, "Line one\nline two\nline three\n");
4445 f = File(deleteme, "r");
4447 foreach (ubyte[] line; chunks(f, 3))
4449 if (i == 0) assert(cast(char[]) line == "Lin");
4450 else if (i == 1) assert(cast(char[]) line == "e o");
4451 else if (i == 2) assert(cast(char[]) line == "ne\n");
4460 Writes an array or range to a file.
4461 Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
4462 Similar to $(REF write, std,file), strings are written as-is,
4463 rather than encoded according to the $(D File)'s $(HTTP
4464 en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
4467 void toFile(T)(T data, string fileName)
4468 if (is(typeof(copy(data, stdout.lockingBinaryWriter))))
4470 copy(data, File(fileName, "wb").lockingBinaryWriter);
4475 static import std.file;
4477 auto deleteme = testFilename();
4478 scope(exit) { std.file.remove(deleteme); }
4480 "Test".toFile(deleteme);
4481 assert(std.file.readText(deleteme) == "Test");
4484 /*********************
4485 * Thrown if I/O errors happen.
4487 class StdioException : Exception
4489 static import core.stdc.errno;
4490 /// Operating system error code.
4494 Initialize with a message and an error code.
4496 this(string message, uint e = core.stdc.errno.errno) @trusted
4498 import std.exception : errnoString;
4500 auto sysmsg = errnoString(errno);
4501 // If e is 0, we don't use the system error message. (The message
4502 // is "Success", which is rather pointless for an exception.)
4503 super(e == 0 ? message
4504 : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
4507 /** Convenience functions that throw an $(D StdioException). */
4508 static void opCall(string msg)
4510 throw new StdioException(msg);
4514 static void opCall()
4516 throw new StdioException(null, core.stdc.errno.errno);
4520 enum StdFileHandle: string
4522 stdin = "core.stdc.stdio.stdin",
4523 stdout = "core.stdc.stdio.stdout",
4524 stderr = "core.stdc.stdio.stderr",
4527 // Undocumented but public because the std* handles are aliasing it.
4528 @property ref File makeGlobal(StdFileHandle _iob)()
4530 __gshared File.Impl impl;
4531 __gshared File result;
4533 // Use an inline spinlock to make sure the initializer is only run once.
4534 // We assume there will be at most uint.max / 2 threads trying to initialize
4535 // `handle` at once and steal the high bit to indicate that the globals have
4536 // been initialized.
4537 static shared uint spinlock;
4538 import core.atomic : atomicLoad, atomicOp, MemoryOrder;
4539 if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
4543 if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2)
4545 if (atomicOp!"+="(spinlock, 1) == 1)
4547 with (StdFileHandle)
4548 assert(_iob == stdin || _iob == stdout || _iob == stderr);
4549 impl.handle = mixin(_iob);
4551 atomicOp!"+="(spinlock, uint.max / 2);
4554 atomicOp!"-="(spinlock, 1);
4560 /** The standard input stream.
4562 Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
4563 it is thread un-safe to reassign `stdin` to a different `File` instance
4566 alias stdin = makeGlobal!(StdFileHandle.stdin);
4571 // Read stdin, sort lines, write to stdout
4572 import std.algorithm.mutation : copy;
4573 import std.algorithm.sorting : sort;
4574 import std.array : array;
4575 import std.typecons : Yes;
4578 stdin // read from stdin
4579 .byLineCopy(Yes.keepTerminator) // copying each line
4580 .array() // convert to array of lines
4581 .sort() // sort the lines
4582 .copy( // copy output of .sort to an OutputRange
4583 stdout.lockingTextWriter()); // the OutputRange
4588 The standard output stream.
4590 Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
4591 it is thread un-safe to reassign `stdout` to a different `File` instance
4594 alias stdout = makeGlobal!(StdFileHandle.stdout);
4597 The standard error stream.
4599 Due to $(LINK2 https://issues.dlang.org/show_bug.cgi?id=15768, bug 15768),
4600 it is thread un-safe to reassign `stderr` to a different `File` instance
4603 alias stderr = makeGlobal!(StdFileHandle.stderr);
4607 static import std.file;
4608 import std.typecons : tuple;
4610 scope(failure) printf("Failed test at line %d\n", __LINE__);
4611 auto deleteme = testFilename();
4613 std.file.write(deleteme, "1 2\n4 1\n5 100");
4614 scope(exit) std.file.remove(deleteme);
4616 File f = File(deleteme);
4617 scope(exit) f.close();
4618 auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
4620 foreach (e; f.byRecord!(int, int)("%s %s"))
4623 assert(e == t[i++]);
4631 // Retain backwards compatibility
4632 // https://issues.dlang.org/show_bug.cgi?id=17472
4633 static assert(is(typeof(stdin) == File));
4634 static assert(is(typeof(stdout) == File));
4635 static assert(is(typeof(stderr) == File));
4638 // roll our own appender, but with "safe" arrays
4639 private struct ReadlnAppender
4643 bool safeAppend = false;
4645 void initialize(char[] b)
4650 @property char[] data() @trusted
4653 assumeSafeAppend(buf.ptr[0 .. pos]);
4654 return buf.ptr[0 .. pos];
4657 bool reserveWithoutAllocating(size_t n)
4659 if (buf.length >= pos + n) // buf is already large enough
4662 immutable curCap = buf.capacity;
4663 if (curCap >= pos + n)
4665 buf.length = curCap;
4666 /* Any extra capacity we end up not using can safely be claimed
4674 void reserve(size_t n) @trusted
4676 import core.stdc.string : memcpy;
4677 if (!reserveWithoutAllocating(n))
4679 size_t ncap = buf.length * 2 + 128 + n;
4680 char[] nbuf = new char[ncap];
4681 memcpy(nbuf.ptr, buf.ptr, pos);
4683 // Allocated a new buffer. No one else knows about it.
4687 void putchar(char c) @trusted
4692 void putdchar(dchar dc) @trusted
4694 import std.utf : encode, UseReplacementDchar;
4697 immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc);
4702 void putonly(char[] b) @trusted
4704 import core.stdc.string : memcpy;
4705 assert(pos == 0); // assume this is the only put call
4706 if (reserveWithoutAllocating(b.length))
4707 memcpy(buf.ptr + pos, b.ptr, b.length);
4714 // Private implementation of readln
4715 version (DIGITAL_MARS_STDIO)
4716 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/)
4719 scope(exit) FUNLOCK(fps);
4721 /* Since fps is now locked, we can create an "unshared" version
4724 auto fp = cast(_iobuf*) fps;
4727 app.initialize(buf);
4729 if (__fhnd_info[fp._file] & FHND_WCHAR)
4730 { /* Stream is in wide characters.
4731 * Read them and convert to chars.
4733 static assert(wchar_t.sizeof == 2);
4734 for (int c = void; (c = FGETWC(fp)) != -1; )
4736 if ((c & ~0x7F) == 0)
4738 app.putchar(cast(char) c);
4739 if (c == terminator)
4744 if (c >= 0xD800 && c <= 0xDBFF)
4747 if ((c2 = FGETWC(fp)) != -1 ||
4748 c2 < 0xDC00 && c2 > 0xDFFF)
4750 StdioException("unpaired UTF-16 surrogate");
4752 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
4754 app.putdchar(cast(dchar) c);
4761 else if (fp._flag & _IONBF)
4763 /* Use this for unbuffered I/O, when running
4764 * across buffer boundaries, or for any but the common
4769 while ((c = FGETC(fp)) != -1)
4771 app.putchar(cast(char) c);
4772 if (c == terminator)
4788 if (fp._flag & _IOTRAN)
4789 { /* Translated mode ignores \r and treats ^Z as end-of-file
4794 if (i == u) // if end of buffer
4800 if (c == terminator)
4807 { if (i != u && p[i] == terminator)
4812 app.putonly(p[0 .. i]);
4813 app.buf[i - 1] = cast(char) terminator;
4814 if (terminator == '\n' && c == '\r')
4821 if (i == u) // if end of buffer
4825 if (c == terminator)
4828 app.putonly(p[0 .. i]);
4838 version (MICROSOFT_STDIO)
4839 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation /*ignored*/)
4842 scope(exit) FUNLOCK(fps);
4844 /* Since fps is now locked, we can create an "unshared" version
4847 auto fp = cast(_iobuf*) fps;
4850 app.initialize(buf);
4853 while ((c = FGETC(fp)) != -1)
4855 app.putchar(cast(char) c);
4856 if (c == terminator)
4870 version (HAS_GETDELIM)
4871 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
4873 import core.stdc.stdlib : free;
4874 import core.stdc.wchar_ : fwide;
4876 if (orientation == File.Orientation.wide)
4878 /* Stream is in wide characters.
4879 * Read them and convert to chars.
4882 scope(exit) FUNLOCK(fps);
4883 auto fp = cast(_iobuf*) fps;
4887 for (int c = void; (c = FGETWC(fp)) != -1; )
4889 if ((c & ~0x7F) == 0)
4891 if (c == terminator)
4896 if (c >= 0xD800 && c <= 0xDBFF)
4899 if ((c2 = FGETWC(fp)) != -1 ||
4900 c2 < 0xDC00 && c2 > 0xDFFF)
4902 StdioException("unpaired UTF-16 surrogate");
4904 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
4906 import std.utf : encode;
4914 else version (Posix)
4917 for (int c; (c = FGETWC(fp)) != -1; )
4919 import std.utf : encode;
4921 if ((c & ~0x7F) == 0)
4922 buf ~= cast(char) c;
4924 encode(buf, cast(dchar) c);
4925 if (c == terminator)
4938 static char *lineptr = null;
4939 static size_t n = 0;
4944 // Bound memory used by readln
4951 auto s = getdelim(&lineptr, &n, terminator, fps);
4956 buf.length = 0; // end of file
4960 if (s <= buf.length)
4963 buf[] = lineptr[0 .. s];
4967 buf = lineptr[0 .. s].dup;
4972 version (NO_GETDELIM)
4973 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
4975 import core.stdc.wchar_ : fwide;
4978 scope(exit) FUNLOCK(fps);
4979 auto fp = cast(_iobuf*) fps;
4980 if (orientation == File.Orientation.wide)
4982 /* Stream is in wide characters.
4983 * Read them and convert to chars.
4988 for (int c; (c = FGETWC(fp)) != -1; )
4990 if ((c & ~0x7F) == 0)
4992 if (c == terminator)
4997 if (c >= 0xD800 && c <= 0xDBFF)
5000 if ((c2 = FGETWC(fp)) != -1 ||
5001 c2 < 0xDC00 && c2 > 0xDFFF)
5003 StdioException("unpaired UTF-16 surrogate");
5005 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5007 import std.utf : encode;
5015 else version (Posix)
5017 import std.utf : encode;
5019 for (int c; (c = FGETWC(fp)) != -1; )
5021 if ((c & ~0x7F) == 0)
5022 buf ~= cast(char) c;
5024 encode(buf, cast(dchar) c);
5025 if (c == terminator)
5039 // First, fill the existing buffer
5040 for (size_t bufPos = 0; bufPos < buf.length; )
5042 immutable c = FGETC(fp);
5045 buf.length = bufPos;
5048 buf[bufPos++] = cast(char) c;
5049 if (c == terminator)
5051 // No need to test for errors in file
5052 buf.length = bufPos;
5056 // Then, append to it
5057 for (int c; (c = FGETC(fp)) != -1; )
5059 buf ~= cast(char) c;
5060 if (c == terminator)
5062 // No need to test for errors in file
5075 static import std.file;
5076 auto deleteme = testFilename();
5077 scope(exit) std.file.remove(deleteme);
5079 std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n");
5080 File f = File(deleteme, "rb");
5082 char[] ln = new char[2];
5083 char* lnptr = ln.ptr;
5086 assert(ln == "abcd\n");
5087 char[] t = ln[0 .. 2];
5090 assert(ln == "abcd\n"); // bug 13856: ln stomped to "abtd"
5092 // it can also stomp the array length
5096 assert(ln == "0123456789abcde\n");
5101 assert(ln == "1234\n");
5102 assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough
5105 /** Experimental network access via the File interface
5107 Opens a TCP connection to the given host and port, then returns
5108 a File struct with read and write access through the same interface
5109 as any other file (meaning writef and the byLine ranges work!).
5119 File openNetwork(string host, ushort port)
5121 import core.stdc.string : memcpy;
5122 import core.sys.posix.arpa.inet : htons;
5123 import core.sys.posix.netdb : gethostbyname;
5124 import core.sys.posix.netinet.in_ : sockaddr_in;
5125 static import core.sys.posix.unistd;
5126 static import sock = core.sys.posix.sys.socket;
5127 import std.conv : to;
5128 import std.exception : enforce;
5129 import std.internal.cstring : tempCString;
5131 auto h = enforce( gethostbyname(host.tempCString()),
5132 new StdioException("gethostbyname"));
5134 int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
5135 enforce(s != -1, new StdioException("socket"));
5139 // want to make sure it doesn't dangle if something throws. Upon
5140 // normal exit, the File struct's reference counting takes care of
5141 // closing, so we don't need to worry about success
5142 core.sys.posix.unistd.close(s);
5147 addr.sin_family = sock.AF_INET;
5148 addr.sin_port = htons(port);
5149 memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
5151 enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
5152 new StdioException("Connect failed"));
5155 f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
5160 version (unittest) string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
5162 import std.conv : text;
5163 import std.file : deleteme;
5164 import std.path : baseName;
5166 // filename intentionally contains non-ASCII (Russian) characters for test Issue 7648
5167 return text(deleteme, "-детка.", baseName(file), ".", line);