Daily bump.
[gcc.git] / libphobos / src / std / stdio.d
blob4734c1b7165829c8d3f3a0e41550a0d889ed906a
1 // Written in the D programming language.
3 /**
4 $(SCRIPT inhibitQuickIndex = 1;)
5 $(DIVC quickindex,
6 $(BOOKTABLE,
7 $(TR $(TH Category) $(TH Symbols))
8 $(TR $(TD File handles) $(TD
9 $(MYREF __popen)
10 $(MYREF File)
11 $(MYREF isFileHandle)
12 $(MYREF openNetwork)
13 $(MYREF stderr)
14 $(MYREF stdin)
15 $(MYREF stdout)
17 $(TR $(TD Reading) $(TD
18 $(MYREF chunks)
19 $(MYREF lines)
20 $(MYREF readf)
21 $(MYREF readln)
23 $(TR $(TD Writing) $(TD
24 $(MYREF toFile)
25 $(MYREF write)
26 $(MYREF writef)
27 $(MYREF writefln)
28 $(MYREF writeln)
30 $(TR $(TD Misc) $(TD
31 $(MYREF KeepTerminator)
32 $(MYREF LockType)
33 $(MYREF StdioException)
37 Standard I/O functions that extend $(LINK2 https://dlang.org/phobos/core_stdc_stdio.html, core.stdc.stdio). $(B core.stdc.stdio)
38 is $(D_PARAM public)ally imported when importing $(B std.stdio).
40 There are three layers of I/O:
41 $(OL
42 $(LI The lowest layer is the operating system layer. The two main schemes are Windows and Posix.)
43 $(LI C's $(TT stdio.h) which unifies the two operating system schemes.)
44 $(LI $(TT std.stdio), this module, unifies the various $(TT stdio.h) implementations into
45 a high level package for D programs.)
48 Source: $(PHOBOSSRC std/stdio.d)
49 Copyright: Copyright The D Language Foundation 2007-.
50 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
51 Authors: $(HTTP digitalmars.com, Walter Bright),
52 $(HTTP erdani.org, Andrei Alexandrescu),
53 Alex Rønne Petersen
54 Macros:
55 CSTDIO=$(HTTP cplusplus.com/reference/cstdio/$1/, $1)
57 module std.stdio;
60 # Glossary
62 The three layers have many terms for their data structures and types.
63 Here we try to bring some sanity to them for the intrepid code spelunker.
65 ## Windows
67 Handle
69 A Windows handle is an opaque object of type HANDLE.
70 The `HANDLE` for standard devices can be retrieved with
71 Windows `GetStdHandle()`.
73 ## Posix
75 file descriptor, aka fileno, aka fildes
77 An int from 0..`FOPEN_MAX`, which is an index into some internal data
78 structure.
79 0 is for `stdin`, 1 for `stdout`, 2 for `stderr`.
80 Negative values usually indicate an error.
82 ## stdio.h
84 `FILE`
86 A struct that encapsulates the C library's view of the operating system
87 files. A `FILE` should only be referred to via a pointer.
89 `fileno`
91 A field of `FILE` which is the Posix file descriptor for Posix systems, and
92 and an index into an array of file `HANDLE`s for Windows.
93 This array is how Posix behavior is emulated on Windows.
94 For Digital Mars C, that array is `__osfhnd[]`, and is initialized
95 at program start by the C runtime library.
96 In this module, they are typed as `fileno_t`.
98 `stdin`, `stdout`, `stderr`
100 Global pointers to `FILE` representing standard input, output, and error streams.
101 Being global means there are synchronization issues when multiple threads
102 are doing I/O on the same streams.
104 ## std.stdio
108 import core.stdc.stddef : wchar_t;
109 public import core.stdc.stdio;
110 import std.algorithm.mutation : copy;
111 import std.meta : allSatisfy;
112 import std.range : ElementEncodingType, empty, front, isBidirectionalRange,
113 isInputRange, isSomeFiniteCharInputRange, put;
114 import std.traits : isSomeChar, isSomeString, Unqual;
115 import std.typecons : Flag, No, Yes;
118 If flag `KeepTerminator` is set to `KeepTerminator.yes`, then the delimiter
119 is included in the strings returned.
121 alias KeepTerminator = Flag!"keepTerminator";
123 version (CRuntime_Microsoft)
126 else version (CRuntime_Glibc)
129 else version (CRuntime_Bionic)
131 version = GENERIC_IO;
133 else version (CRuntime_Musl)
135 version = GENERIC_IO;
137 else version (CRuntime_UClibc)
139 version = GENERIC_IO;
141 else version (OSX)
143 version = GENERIC_IO;
144 version = Darwin;
146 else version (iOS)
148 version = GENERIC_IO;
149 version = Darwin;
151 else version (TVOS)
153 version = GENERIC_IO;
154 version = Darwin;
156 else version (WatchOS)
158 version = GENERIC_IO;
159 version = Darwin;
161 else version (FreeBSD)
163 version = GENERIC_IO;
165 else version (NetBSD)
167 version = GENERIC_IO;
169 else version (OpenBSD)
171 version = GENERIC_IO;
173 else version (DragonFlyBSD)
175 version = GENERIC_IO;
177 else version (Solaris)
179 version = GENERIC_IO;
181 else
183 static assert(0, "unsupported operating system");
186 // Character type used for operating system filesystem APIs
187 version (Windows)
189 private alias FSChar = wchar;
191 else
193 private alias FSChar = char;
196 private alias fileno_t = int; // file descriptor, fildes, fileno
198 version (Windows)
200 // core.stdc.stdio.fopen expects file names to be
201 // encoded in CP_ACP on Windows instead of UTF-8.
202 /+ Waiting for druntime pull 299
204 extern (C) nothrow @nogc FILE* _wfopen(scope const wchar* filename, scope const wchar* mode);
205 extern (C) nothrow @nogc FILE* _wfreopen(scope const wchar* filename, scope const wchar* mode, FILE* fp);
207 import core.sys.windows.basetsd : HANDLE;
210 version (Posix)
212 static import core.sys.posix.stdio; // getdelim, flockfile
215 version (CRuntime_Microsoft)
217 private alias _FPUTC = _fputc_nolock;
218 private alias _FPUTWC = _fputwc_nolock;
219 private alias _FGETC = _fgetc_nolock;
220 private alias _FGETWC = _fgetwc_nolock;
221 private alias _FLOCK = _lock_file;
222 private alias _FUNLOCK = _unlock_file;
224 else version (CRuntime_Glibc)
226 private alias _FPUTC = fputc_unlocked;
227 private alias _FPUTWC = fputwc_unlocked;
228 private alias _FGETC = fgetc_unlocked;
229 private alias _FGETWC = fgetwc_unlocked;
230 private alias _FLOCK = core.sys.posix.stdio.flockfile;
231 private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
233 else version (GENERIC_IO)
235 nothrow:
236 @nogc:
238 extern (C) private
240 static import core.stdc.wchar_;
242 pragma(mangle, fputc.mangleof) int _FPUTC(int c, _iobuf* fp);
243 pragma(mangle, core.stdc.wchar_.fputwc.mangleof) int _FPUTWC(wchar_t c, _iobuf* fp);
244 pragma(mangle, fgetc.mangleof) int _FGETC(_iobuf* fp);
245 pragma(mangle, core.stdc.wchar_.fgetwc.mangleof) int _FGETWC(_iobuf* fp);
248 version (Posix)
250 private alias _FLOCK = core.sys.posix.stdio.flockfile;
251 private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
253 else
255 static assert(0, "don't know how to lock files on GENERIC_IO");
258 else
260 static assert(0, "unsupported C I/O system");
263 private extern (C) @nogc nothrow
265 pragma(mangle, _FPUTC.mangleof) int trustedFPUTC(int ch, _iobuf* h) @trusted;
266 pragma(mangle, _FPUTWC.mangleof) int trustedFPUTWC(wchar_t ch, _iobuf* h) @trusted;
269 //------------------------------------------------------------------------------
270 private struct ByRecordImpl(Fields...)
272 private:
273 import std.typecons : Tuple;
275 File file;
276 char[] line;
277 Tuple!(Fields) current;
278 string format;
280 public:
281 this(File f, string format)
283 assert(f.isOpen);
284 file = f;
285 this.format = format;
286 popFront(); // prime the range
289 /// Range primitive implementations.
290 @property bool empty()
292 return !file.isOpen;
295 /// Ditto
296 @property ref Tuple!(Fields) front()
298 return current;
301 /// Ditto
302 void popFront()
304 import std.conv : text;
305 import std.exception : enforce;
306 import std.format.read : formattedRead;
307 import std.string : chomp;
309 enforce(file.isOpen, "ByRecord: File must be open");
310 file.readln(line);
311 if (!line.length)
313 file.detach();
315 else
317 line = chomp(line);
318 formattedRead(line, format, &current);
319 enforce(line.empty, text("Leftover characters in record: `",
320 line, "'"));
325 template byRecord(Fields...)
327 auto byRecord(File f, string format)
329 return typeof(return)(f, format);
334 Encapsulates a `FILE*`. Generally D does not attempt to provide
335 thin wrappers over equivalent functions in the C standard library, but
336 manipulating `FILE*` values directly is unsafe and error-prone in
337 many ways. The `File` type ensures safe manipulation, automatic
338 file closing, and a lot of convenience.
340 The underlying `FILE*` handle is maintained in a reference-counted
341 manner, such that as soon as the last `File` variable bound to a
342 given `FILE*` goes out of scope, the underlying `FILE*` is
343 automatically closed.
345 Example:
346 ----
347 // test.d
348 import std.stdio;
350 void main(string[] args)
352 auto f = File("test.txt", "w"); // open for writing
353 f.write("Hello");
354 if (args.length > 1)
356 auto g = f; // now g and f write to the same file
357 // internal reference count is 2
358 g.write(", ", args[1]);
359 // g exits scope, reference count decreases to 1
361 f.writeln("!");
362 // f exits scope, reference count falls to zero,
363 // underlying `FILE*` is closed.
365 ----
366 $(CONSOLE
367 % rdmd test.d Jimmy
368 % cat test.txt
369 Hello, Jimmy!
370 % __
373 struct File
375 import core.atomic : atomicOp, atomicStore, atomicLoad;
376 import std.range.primitives : ElementEncodingType;
377 import std.traits : isScalarType, isArray;
378 enum Orientation { unknown, narrow, wide }
380 private struct Impl
382 FILE * handle = null; // Is null iff this Impl is closed by another File
383 shared uint refs = uint.max / 2;
384 bool isPopened; // true iff the stream has been created by popen()
385 Orientation orientation;
387 private Impl* _p;
388 private string _name;
390 package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted @nogc nothrow
392 import core.stdc.stdlib : malloc;
394 assert(!_p);
395 _p = cast(Impl*) malloc(Impl.sizeof);
396 if (!_p)
398 import core.exception : onOutOfMemoryError;
399 onOutOfMemoryError();
401 initImpl(handle, name, refs, isPopened);
404 private void initImpl(FILE* handle, string name, uint refs = 1, bool isPopened = false) @nogc nothrow pure @safe
406 assert(_p);
407 _p.handle = handle;
408 atomicStore(_p.refs, refs);
409 _p.isPopened = isPopened;
410 _p.orientation = Orientation.unknown;
411 _name = name;
415 Constructor taking the name of the file to open and the open mode.
417 Copying one `File` object to another results in the two `File`
418 objects referring to the same underlying file.
420 The destructor automatically closes the file as soon as no `File`
421 object refers to it anymore.
423 Params:
424 name = range or string representing the file _name
425 stdioOpenmode = range or string represting the open mode
426 (with the same semantics as in the C standard library
427 $(CSTDIO fopen) function)
429 Throws: `ErrnoException` if the file could not be opened.
431 this(string name, scope const(char)[] stdioOpenmode = "rb") @safe
433 import std.conv : text;
434 import std.exception : errnoEnforce;
436 this(errnoEnforce(_fopen(name, stdioOpenmode),
437 text("Cannot open file `", name, "' in mode `",
438 stdioOpenmode, "'")),
439 name);
441 // MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422)
442 version (CRuntime_Microsoft)
444 setAppendWin(stdioOpenmode);
448 /// ditto
449 this(R1, R2)(R1 name)
450 if (isSomeFiniteCharInputRange!R1)
452 import std.conv : to;
453 this(name.to!string, "rb");
456 /// ditto
457 this(R1, R2)(R1 name, R2 mode)
458 if (isSomeFiniteCharInputRange!R1 &&
459 isSomeFiniteCharInputRange!R2)
461 import std.conv : to;
462 this(name.to!string, mode.to!string);
465 @safe unittest
467 static import std.file;
468 import std.utf : byChar;
469 auto deleteme = testFilename();
470 auto f = File(deleteme.byChar, "w".byChar);
471 f.close();
472 std.file.remove(deleteme);
475 ~this() @safe
477 detach();
480 this(this) @safe pure nothrow @nogc
482 if (!_p) return;
483 assert(atomicLoad(_p.refs));
484 atomicOp!"+="(_p.refs, 1);
488 Assigns a file to another. The target of the assignment gets detached
489 from whatever file it was attached to, and attaches itself to the new
490 file.
492 ref File opAssign(File rhs) @safe return
494 import std.algorithm.mutation : swap;
496 swap(this, rhs);
497 return this;
500 // https://issues.dlang.org/show_bug.cgi?id=20129
501 @safe unittest
503 File[int] aa;
504 aa.require(0, File.init);
508 Detaches from the current file (throwing on failure), and then attempts to
509 _open file `name` with mode `stdioOpenmode`. The mode has the
510 same semantics as in the C standard library $(CSTDIO fopen) function.
512 Throws: `ErrnoException` in case of error.
514 void open(string name, scope const(char)[] stdioOpenmode = "rb") @trusted
516 resetFile(name, stdioOpenmode, false);
519 // https://issues.dlang.org/show_bug.cgi?id=20585
520 @system unittest
522 File f;
524 f.open("doesn't exist");
525 catch (Exception _e)
529 assert(!f.isOpen);
531 f.close(); // to check not crash here
534 private void resetFile(string name, scope const(char)[] stdioOpenmode, bool isPopened) @trusted
536 import core.stdc.stdlib : malloc;
537 import std.exception : enforce;
538 import std.conv : text;
539 import std.exception : errnoEnforce;
541 if (_p !is null)
543 detach();
546 FILE* handle;
547 version (Posix)
549 if (isPopened)
551 errnoEnforce(handle = _popen(name, stdioOpenmode),
552 "Cannot run command `"~name~"'");
554 else
556 errnoEnforce(handle = _fopen(name, stdioOpenmode),
557 text("Cannot open file `", name, "' in mode `",
558 stdioOpenmode, "'"));
561 else
563 assert(isPopened == false);
564 errnoEnforce(handle = _fopen(name, stdioOpenmode),
565 text("Cannot open file `", name, "' in mode `",
566 stdioOpenmode, "'"));
568 _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
569 initImpl(handle, name, 1, isPopened);
570 version (CRuntime_Microsoft)
572 setAppendWin(stdioOpenmode);
576 private void closeHandles() @trusted
578 assert(_p);
579 import std.exception : errnoEnforce;
581 version (Posix)
583 import core.sys.posix.stdio : pclose;
584 import std.format : format;
586 if (_p.isPopened)
588 auto res = pclose(_p.handle);
589 errnoEnforce(res != -1,
590 "Could not close pipe `"~_name~"'");
591 _p.handle = null;
592 return;
595 if (_p.handle)
597 auto handle = _p.handle;
598 _p.handle = null;
599 // fclose disassociates the FILE* even in case of error (https://issues.dlang.org/show_bug.cgi?id=19751)
600 errnoEnforce(.fclose(handle) == 0,
601 "Could not close file `"~_name~"'");
605 version (CRuntime_Microsoft)
607 private void setAppendWin(scope const(char)[] stdioOpenmode) @safe
609 bool append, update;
610 foreach (c; stdioOpenmode)
611 if (c == 'a')
612 append = true;
613 else
614 if (c == '+')
615 update = true;
616 if (append && !update)
617 seek(size);
622 Reuses the `File` object to either open a different file, or change
623 the file mode. If `name` is `null`, the mode of the currently open
624 file is changed; otherwise, a new file is opened, reusing the C
625 `FILE*`. The function has the same semantics as in the C standard
626 library $(CSTDIO freopen) function.
628 Note: Calling `reopen` with a `null` `name` is not implemented
629 in all C runtimes.
631 Throws: `ErrnoException` in case of error.
633 void reopen(string name, scope const(char)[] stdioOpenmode = "rb") @trusted
635 import std.conv : text;
636 import std.exception : enforce, errnoEnforce;
637 import std.internal.cstring : tempCString;
639 enforce(isOpen, "Attempting to reopen() an unopened file");
641 auto namez = (name == null ? _name : name).tempCString!FSChar();
642 auto modez = stdioOpenmode.tempCString!FSChar();
644 FILE* fd = _p.handle;
645 version (Windows)
646 fd = _wfreopen(namez, modez, fd);
647 else
648 fd = freopen(namez, modez, fd);
650 errnoEnforce(fd, name
651 ? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'")
652 : text("Cannot reopen file in mode `", stdioOpenmode, "'"));
654 if (name !is null)
655 _name = name;
658 @safe unittest // Test changing filename
660 import std.exception : assertThrown, assertNotThrown;
661 static import std.file;
663 auto deleteme = testFilename();
664 std.file.write(deleteme, "foo");
665 scope(exit) std.file.remove(deleteme);
666 auto f = File(deleteme);
667 assert(f.readln() == "foo");
669 auto deleteme2 = testFilename();
670 std.file.write(deleteme2, "bar");
671 scope(exit) std.file.remove(deleteme2);
672 f.reopen(deleteme2);
673 assert(f.name == deleteme2);
674 assert(f.readln() == "bar");
675 f.close();
678 version (CRuntime_Microsoft) {} else // Not implemented
679 @safe unittest // Test changing mode
681 import std.exception : assertThrown, assertNotThrown;
682 static import std.file;
684 auto deleteme = testFilename();
685 std.file.write(deleteme, "foo");
686 scope(exit) std.file.remove(deleteme);
687 auto f = File(deleteme, "r+");
688 assert(f.readln() == "foo");
689 f.reopen(null, "w");
690 f.write("bar");
691 f.seek(0);
692 f.reopen(null, "a");
693 f.write("baz");
694 assert(f.name == deleteme);
695 f.close();
696 assert(std.file.readText(deleteme) == "barbaz");
700 Detaches from the current file (throwing on failure), and then runs a command
701 by calling the C standard library function $(HTTP
702 pubs.opengroup.org/onlinepubs/7908799/xsh/popen.html, popen).
704 Throws: `ErrnoException` in case of error.
706 version (Posix) void popen(string command, scope const(char)[] stdioOpenmode = "r") @safe
708 resetFile(command, stdioOpenmode ,true);
712 First calls `detach` (throwing on failure), then attempts to
713 associate the given file descriptor with the `File`, and sets the file's name to `null`.
715 The mode must be compatible with the mode of the file descriptor.
717 Throws: `ErrnoException` in case of error.
718 Params:
719 fd = File descriptor to associate with this `File`.
720 stdioOpenmode = Mode to associate with this File. The mode has the same
721 semantics as in the POSIX library function $(HTTP
722 pubs.opengroup.org/onlinepubs/7908799/xsh/fdopen.html, fdopen)
723 and must be compatible with `fd`.
725 void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe
727 fdopen(fd, stdioOpenmode, null);
730 package void fdopen(int fd, scope const(char)[] stdioOpenmode, string name) @trusted
732 import std.exception : errnoEnforce;
733 import std.internal.cstring : tempCString;
735 auto modez = stdioOpenmode.tempCString();
736 detach();
738 version (CRuntime_Microsoft)
740 auto fp = _fdopen(fd, modez);
741 errnoEnforce(fp);
743 else version (Posix)
745 import core.sys.posix.stdio : fdopen;
746 auto fp = fdopen(fd, modez);
747 errnoEnforce(fp);
749 else
750 static assert(0, "no fdopen() available");
752 this = File(fp, name);
755 // Declare a dummy HANDLE to allow generating documentation
756 // for Windows-only methods.
757 version (StdDdoc) { version (Windows) {} else alias HANDLE = int; }
760 First calls `detach` (throwing on failure), and then attempts to
761 associate the given Windows `HANDLE` with the `File`. The mode must
762 be compatible with the access attributes of the handle. Windows only.
764 Throws: `ErrnoException` in case of error.
766 version (StdDdoc)
767 void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode);
769 version (Windows)
770 void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode)
772 import core.stdc.stdint : intptr_t;
773 import std.exception : errnoEnforce;
774 import std.format : format;
776 // Create file descriptors from the handles
777 int mode;
778 modeLoop:
779 foreach (c; stdioOpenmode)
780 switch (c)
782 case 'r': mode |= _O_RDONLY; break;
783 case '+': mode &=~_O_RDONLY; break;
784 case 'a': mode |= _O_APPEND; break;
785 case 'b': mode |= _O_BINARY; break;
786 case 't': mode |= _O_TEXT; break;
787 case ',': break modeLoop;
788 default: break;
791 auto fd = _open_osfhandle(cast(intptr_t) handle, mode);
793 errnoEnforce(fd >= 0, "Cannot open Windows HANDLE");
794 fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle));
798 /** Returns `true` if the file is opened. */
799 @property bool isOpen() const @safe pure nothrow
801 return _p !is null && _p.handle;
805 Returns `true` if the file is at end (see $(CSTDIO feof)).
807 Throws: `Exception` if the file is not opened.
809 @property bool eof() const @trusted pure
811 import std.exception : enforce;
813 enforce(_p && _p.handle, "Calling eof() against an unopened file.");
814 return .feof(cast(FILE*) _p.handle) != 0;
818 Returns the name last used to initialize this `File`, if any.
820 Some functions that create or initialize the `File` set the name field to `null`.
821 Examples include $(LREF tmpfile), $(LREF wrapFile), and $(LREF fdopen). See the
822 documentation of those functions for details.
824 Returns: The name last used to initialize this this file, or `null` otherwise.
826 @property string name() const @safe pure nothrow return
828 return _name;
832 If the file is closed or not yet opened, returns `true`. Otherwise, returns
833 $(CSTDIO ferror) for the file handle.
835 @property bool error() const @trusted pure nothrow
837 return !isOpen || .ferror(cast(FILE*) _p.handle);
840 @safe unittest
842 // https://issues.dlang.org/show_bug.cgi?id=12349
843 static import std.file;
844 auto deleteme = testFilename();
845 auto f = File(deleteme, "w");
846 scope(exit) std.file.remove(deleteme);
848 f.close();
849 assert(f.error);
853 Detaches from the underlying file. If the sole owner, calls `close`.
855 Throws: `ErrnoException` on failure if closing the file.
857 void detach() @trusted
859 import core.stdc.stdlib : free;
861 if (!_p) return;
862 scope(exit) _p = null;
864 if (atomicOp!"-="(_p.refs, 1) == 0)
866 scope(exit) free(_p);
867 closeHandles();
871 @safe unittest
873 static import std.file;
875 auto deleteme = testFilename();
876 scope(exit) std.file.remove(deleteme);
877 auto f = File(deleteme, "w");
879 auto f2 = f;
880 f2.detach();
882 assert(f._p.refs == 1);
883 f.close();
887 If the file was closed or not yet opened, succeeds vacuously. Otherwise
888 closes the file (by calling $(CSTDIO fclose)),
889 throwing on error. Even if an exception is thrown, afterwards the $(D
890 File) object is empty. This is different from `detach` in that it
891 always closes the file; consequently, all other `File` objects
892 referring to the same handle will see a closed file henceforth.
894 Throws: `ErrnoException` on error.
896 void close() @trusted
898 import core.stdc.stdlib : free;
899 import std.exception : errnoEnforce;
901 if (!_p) return; // succeed vacuously
902 scope(exit)
904 if (atomicOp!"-="(_p.refs, 1) == 0)
905 free(_p);
906 _p = null; // start a new life
908 if (!_p.handle) return; // Impl is closed by another File
910 scope(exit) _p.handle = null; // nullify the handle anyway
911 closeHandles();
915 If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns
916 $(CSTDIO clearerr) for the file handle.
918 void clearerr() @safe pure nothrow
920 _p is null || _p.handle is null ||
921 .clearerr(_p.handle);
925 Flushes the C `FILE` buffers.
927 Calls $(CSTDIO fflush) for the file handle.
929 Throws: `Exception` if the file is not opened or if the call to `fflush` fails.
931 void flush() @trusted
933 import std.exception : enforce, errnoEnforce;
935 enforce(isOpen, "Attempting to flush() in an unopened file");
936 errnoEnforce(.fflush(_p.handle) == 0);
939 @safe unittest
941 // https://issues.dlang.org/show_bug.cgi?id=12349
942 import std.exception : assertThrown;
943 static import std.file;
945 auto deleteme = testFilename();
946 auto f = File(deleteme, "w");
947 scope(exit) std.file.remove(deleteme);
949 f.close();
950 assertThrown(f.flush());
954 Forces any data buffered by the OS to be written to disk.
955 Call $(LREF flush) before calling this function to flush the C `FILE` buffers first.
957 This function calls
958 $(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx,
959 `FlushFileBuffers`) on Windows,
960 $(HTTP developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html,
961 `F_FULLFSYNC fcntl`) on Darwin and
962 $(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html,
963 `fsync`) on POSIX for the file handle.
965 Throws: `Exception` if the file is not opened or if the OS call fails.
967 void sync() @trusted
969 import std.exception : enforce;
971 enforce(isOpen, "Attempting to sync() an unopened file");
973 version (Windows)
975 import core.sys.windows.winbase : FlushFileBuffers;
976 wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed");
978 else version (Darwin)
980 import core.sys.darwin.fcntl : fcntl, F_FULLFSYNC;
981 import std.exception : errnoEnforce;
982 errnoEnforce(fcntl(fileno, F_FULLFSYNC, 0) != -1, "fcntl failed");
984 else
986 import core.sys.posix.unistd : fsync;
987 import std.exception : errnoEnforce;
988 errnoEnforce(fsync(fileno) == 0, "fsync failed");
993 Calls $(CSTDIO fread) for the
994 file handle. The number of items to read and the size of
995 each item is inferred from the size and type of the input array, respectively.
997 Returns: The slice of `buffer` containing the data that was actually read.
998 This will be shorter than `buffer` if EOF was reached before the buffer
999 could be filled. If the buffer is empty, it will be returned.
1001 Throws: `ErrnoException` if the file is not opened or the call to `fread` fails.
1003 `rawRead` always reads in binary mode on Windows.
1005 T[] rawRead(T)(T[] buffer)
1007 import std.exception : enforce, errnoEnforce;
1009 if (!buffer.length)
1010 return buffer;
1011 enforce(isOpen, "Attempting to read from an unopened file");
1012 version (Windows)
1014 immutable fileno_t fd = .fileno(_p.handle);
1015 immutable mode = ._setmode(fd, _O_BINARY);
1016 scope(exit) ._setmode(fd, mode);
1018 immutable freadResult = trustedFread(_p.handle, buffer);
1019 assert(freadResult <= buffer.length); // fread return guarantee
1020 if (freadResult != buffer.length) // error or eof
1022 errnoEnforce(!error);
1023 return buffer[0 .. freadResult];
1025 return buffer;
1029 @system unittest
1031 static import std.file;
1033 auto testFile = std.file.deleteme();
1034 std.file.write(testFile, "\r\n\n\r\n");
1035 scope(exit) std.file.remove(testFile);
1037 auto f = File(testFile, "r");
1038 auto buf = f.rawRead(new char[5]);
1039 f.close();
1040 assert(buf == "\r\n\n\r\n");
1043 // https://issues.dlang.org/show_bug.cgi?id=24685
1044 static assert(!__traits(compiles, (File f) @safe { int*[1] bar; f.rawRead(bar[]); }));
1046 // https://issues.dlang.org/show_bug.cgi?id=21729
1047 @system unittest
1049 import std.exception : assertThrown;
1051 File f;
1052 ubyte[1] u;
1053 assertThrown(f.rawRead(u));
1056 // https://issues.dlang.org/show_bug.cgi?id=21728
1057 @system unittest
1059 static if (__traits(compiles, { import std.process : pipe; })) // not available for iOS
1061 import std.process : pipe;
1062 import std.exception : assertThrown;
1064 auto p = pipe();
1065 p.readEnd.close;
1066 ubyte[1] u;
1067 assertThrown(p.readEnd.rawRead(u));
1071 // https://issues.dlang.org/show_bug.cgi?id=13893
1072 @system unittest
1074 import std.exception : assertNotThrown;
1076 File f;
1077 ubyte[0] u;
1078 assertNotThrown(f.rawRead(u));
1082 Calls $(CSTDIO fwrite) for the file
1083 handle. The number of items to write and the size of each
1084 item is inferred from the size and type of the input array, respectively. An
1085 error is thrown if the buffer could not be written in its entirety.
1087 `rawWrite` always writes in binary mode on Windows.
1089 Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fails.
1091 void rawWrite(T)(in T[] buffer)
1093 import std.conv : text;
1094 import std.exception : errnoEnforce;
1096 version (Windows)
1098 immutable fileno_t fd = .fileno(_p.handle);
1099 immutable oldMode = ._setmode(fd, _O_BINARY);
1101 if (oldMode != _O_BINARY)
1103 // need to flush the data that was written with the original mode
1104 ._setmode(fd, oldMode);
1105 flush(); // before changing translation mode ._setmode(fd, _O_BINARY);
1106 ._setmode(fd, _O_BINARY);
1109 scope (exit)
1111 if (oldMode != _O_BINARY)
1113 flush();
1114 ._setmode(fd, oldMode);
1119 auto result = trustedFwrite(_p.handle, buffer);
1120 if (result == result.max) result = 0;
1121 errnoEnforce(result == buffer.length,
1122 text("Wrote ", result, " instead of ", buffer.length,
1123 " objects of type ", T.stringof, " to file `",
1124 _name, "'"));
1128 @system unittest
1130 static import std.file;
1132 auto testFile = std.file.deleteme();
1133 auto f = File(testFile, "w");
1134 scope(exit) std.file.remove(testFile);
1136 f.rawWrite("\r\n\n\r\n");
1137 f.close();
1138 assert(std.file.read(testFile) == "\r\n\n\r\n");
1142 Calls $(CSTDIO fseek)
1143 for the file handle to move its position indicator.
1145 Params:
1146 offset = Binary files: Number of bytes to offset from origin.$(BR)
1147 Text files: Either zero, or a value returned by $(LREF tell).
1148 origin = Binary files: Position used as reference for the offset, must be
1149 one of $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio),
1150 $(REF_ALTTEXT SEEK_CUR, SEEK_CUR, core,stdc,stdio) or
1151 $(REF_ALTTEXT SEEK_END, SEEK_END, core,stdc,stdio).$(BR)
1152 Text files: Shall necessarily be
1153 $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio).
1155 Throws: `Exception` if the file is not opened.
1156 `ErrnoException` if the call to `fseek` fails.
1158 void seek(long offset, int origin = SEEK_SET) @trusted
1160 import std.conv : to, text;
1161 import std.exception : enforce, errnoEnforce;
1163 // Some libc sanitize the whence input (e.g. glibc), but some don't,
1164 // e.g. Microsoft runtime crashes on an invalid origin,
1165 // and Musl additionally accept SEEK_DATA & SEEK_HOLE (Linux extension).
1166 // To provide a consistent behavior cross platform, we use the glibc check
1167 // See also https://issues.dlang.org/show_bug.cgi?id=19797
1168 enforce(origin == SEEK_SET || origin == SEEK_CUR || origin == SEEK_END,
1169 "Invalid `origin` argument passed to `seek`, must be one of: SEEK_SET, SEEK_CUR, SEEK_END");
1171 enforce(isOpen, "Attempting to seek() in an unopened file");
1172 version (Windows)
1174 version (CRuntime_Microsoft)
1176 alias fseekFun = _fseeki64;
1177 alias off_t = long;
1179 else
1181 alias fseekFun = fseek;
1182 alias off_t = int;
1185 else version (Posix)
1187 import core.sys.posix.stdio : fseeko, off_t;
1188 alias fseekFun = fseeko;
1190 errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0,
1191 "Could not seek in file `"~_name~"'");
1194 @system unittest
1196 import std.conv : text;
1197 static import std.file;
1198 import std.exception;
1200 auto deleteme = testFilename();
1201 auto f = File(deleteme, "w+");
1202 scope(exit) { f.close(); std.file.remove(deleteme); }
1203 f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1204 f.seek(7);
1205 assert(f.readln() == "hijklmnopqrstuvwxyz");
1207 version (CRuntime_Bionic)
1208 auto bigOffset = int.max - 100;
1209 else
1210 auto bigOffset = cast(ulong) int.max + 100;
1211 f.seek(bigOffset);
1212 assert(f.tell == bigOffset, text(f.tell));
1213 // Uncomment the tests below only if you want to wait for
1214 // a long time
1215 // f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1216 // f.seek(-3, SEEK_END);
1217 // assert(f.readln() == "xyz");
1219 assertThrown(f.seek(0, ushort.max));
1223 Calls $(CSTDIO ftell)
1224 for the managed file handle, which returns the current value of
1225 the position indicator of the file handle.
1227 Throws: `Exception` if the file is not opened.
1228 `ErrnoException` if the call to `ftell` fails.
1230 @property ulong tell() const @trusted
1232 import std.exception : enforce, errnoEnforce;
1234 enforce(isOpen, "Attempting to tell() in an unopened file");
1235 version (Windows)
1237 version (CRuntime_Microsoft)
1238 immutable result = _ftelli64(cast(FILE*) _p.handle);
1239 else
1240 immutable result = ftell(cast(FILE*) _p.handle);
1242 else version (Posix)
1244 import core.sys.posix.stdio : ftello;
1245 immutable result = ftello(cast(FILE*) _p.handle);
1247 errnoEnforce(result != -1,
1248 "Query ftell() failed for file `"~_name~"'");
1249 return result;
1253 @system unittest
1255 import std.conv : text;
1256 static import std.file;
1258 auto testFile = std.file.deleteme();
1259 std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz");
1260 scope(exit) { std.file.remove(testFile); }
1262 auto f = File(testFile);
1263 auto a = new ubyte[4];
1264 f.rawRead(a);
1265 assert(f.tell == 4, text(f.tell));
1269 Calls $(CSTDIO rewind) for the file handle.
1271 Throws: `Exception` if the file is not opened.
1273 void rewind() @safe
1275 import std.exception : enforce;
1277 enforce(isOpen, "Attempting to rewind() an unopened file");
1278 .rewind(_p.handle);
1282 Calls $(CSTDIO setvbuf) for the file handle.
1284 Throws: `Exception` if the file is not opened.
1285 `ErrnoException` if the call to `setvbuf` fails.
1287 void setvbuf(size_t size, int mode = _IOFBF) @trusted
1289 import std.exception : enforce, errnoEnforce;
1291 enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1292 errnoEnforce(.setvbuf(_p.handle, null, mode, size) == 0,
1293 "Could not set buffering for file `"~_name~"'");
1297 Calls $(CSTDIO setvbuf) for the file handle.
1299 Throws: `Exception` if the file is not opened.
1300 `ErrnoException` if the call to `setvbuf` fails.
1302 void setvbuf(void[] buf, int mode = _IOFBF) @trusted
1304 import std.exception : enforce, errnoEnforce;
1306 enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1307 errnoEnforce(.setvbuf(_p.handle,
1308 cast(char*) buf.ptr, mode, buf.length) == 0,
1309 "Could not set buffering for file `"~_name~"'");
1313 version (Windows)
1315 import core.sys.windows.winbase : OVERLAPPED;
1316 import core.sys.windows.winnt : BOOL, ULARGE_INTEGER;
1317 import std.windows.syserror : wenforce;
1319 private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
1320 Flags flags)
1322 if (!start && !length)
1323 length = ulong.max;
1324 ULARGE_INTEGER liStart = void, liLength = void;
1325 liStart.QuadPart = start;
1326 liLength.QuadPart = length;
1327 OVERLAPPED overlapped;
1328 overlapped.Offset = liStart.LowPart;
1329 overlapped.OffsetHigh = liStart.HighPart;
1330 overlapped.hEvent = null;
1331 return F(windowsHandle, flags, 0, liLength.LowPart,
1332 liLength.HighPart, &overlapped);
1335 version (Posix)
1337 private int lockImpl(int operation, short l_type,
1338 ulong start, ulong length)
1340 import core.sys.posix.fcntl : fcntl, flock, off_t;
1341 import core.sys.posix.unistd : getpid;
1342 import std.conv : to;
1344 flock fl = void;
1345 fl.l_type = l_type;
1346 fl.l_whence = SEEK_SET;
1347 fl.l_start = to!off_t(start);
1348 fl.l_len = to!off_t(length);
1349 fl.l_pid = getpid();
1350 return fcntl(fileno, operation, &fl);
1355 Locks the specified file segment. If the file segment is already locked
1356 by another process, waits until the existing lock is released.
1357 If both `start` and `length` are zero, the entire file is locked.
1359 Locks created using `lock` and `tryLock` have the following properties:
1360 $(UL
1361 $(LI All locks are automatically released when the process terminates.)
1362 $(LI Locks are not inherited by child processes.)
1363 $(LI Closing a file will release all locks associated with the file. On POSIX,
1364 even locks acquired via a different `File` will be released as well.)
1365 $(LI Not all NFS implementations correctly implement file locking.)
1368 void lock(LockType lockType = LockType.readWrite,
1369 ulong start = 0, ulong length = 0)
1371 import std.exception : enforce;
1373 enforce(isOpen, "Attempting to call lock() on an unopened file");
1374 version (Posix)
1376 import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK;
1377 import std.exception : errnoEnforce;
1378 immutable short type = lockType == LockType.readWrite
1379 ? F_WRLCK : F_RDLCK;
1380 errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1,
1381 "Could not set lock for file `"~_name~"'");
1383 else
1384 version (Windows)
1386 import core.sys.windows.winbase : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK;
1387 immutable type = lockType == LockType.readWrite ?
1388 LOCKFILE_EXCLUSIVE_LOCK : 0;
1389 wenforce(lockImpl!LockFileEx(start, length, type),
1390 "Could not set lock for file `"~_name~"'");
1392 else
1393 static assert(false);
1397 Attempts to lock the specified file segment.
1398 If both `start` and `length` are zero, the entire file is locked.
1399 Returns: `true` if the lock was successful, and `false` if the
1400 specified file segment was already locked.
1402 bool tryLock(LockType lockType = LockType.readWrite,
1403 ulong start = 0, ulong length = 0)
1405 import std.exception : enforce;
1407 enforce(isOpen, "Attempting to call tryLock() on an unopened file");
1408 version (Posix)
1410 import core.stdc.errno : EACCES, EAGAIN, errno;
1411 import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK;
1412 import std.exception : errnoEnforce;
1413 immutable short type = lockType == LockType.readWrite
1414 ? F_WRLCK : F_RDLCK;
1415 immutable res = lockImpl(F_SETLK, type, start, length);
1416 if (res == -1 && (errno == EACCES || errno == EAGAIN))
1417 return false;
1418 errnoEnforce(res != -1, "Could not set lock for file `"~_name~"'");
1419 return true;
1421 else
1422 version (Windows)
1424 import core.sys.windows.winbase : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK,
1425 LOCKFILE_FAIL_IMMEDIATELY;
1426 import core.sys.windows.winerror : ERROR_IO_PENDING, ERROR_LOCK_VIOLATION;
1427 immutable type = lockType == LockType.readWrite
1428 ? LOCKFILE_EXCLUSIVE_LOCK : 0;
1429 immutable res = lockImpl!LockFileEx(start, length,
1430 type | LOCKFILE_FAIL_IMMEDIATELY);
1431 if (!res && (GetLastError() == ERROR_IO_PENDING
1432 || GetLastError() == ERROR_LOCK_VIOLATION))
1433 return false;
1434 wenforce(res, "Could not set lock for file `"~_name~"'");
1435 return true;
1437 else
1438 static assert(false);
1442 Removes the lock over the specified file segment.
1444 void unlock(ulong start = 0, ulong length = 0)
1446 import std.exception : enforce;
1448 enforce(isOpen, "Attempting to call unlock() on an unopened file");
1449 version (Posix)
1451 import core.sys.posix.fcntl : F_SETLK, F_UNLCK;
1452 import std.exception : errnoEnforce;
1453 errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1,
1454 "Could not remove lock for file `"~_name~"'");
1456 else
1457 version (Windows)
1459 import core.sys.windows.winbase : UnlockFileEx;
1460 wenforce(lockImpl!UnlockFileEx(start, length),
1461 "Could not remove lock for file `"~_name~"'");
1463 else
1464 static assert(false);
1467 version (Windows)
1468 @system unittest
1470 static import std.file;
1471 auto deleteme = testFilename();
1472 scope(exit) std.file.remove(deleteme);
1473 auto f = File(deleteme, "wb");
1474 assert(f.tryLock());
1475 auto g = File(deleteme, "wb");
1476 assert(!g.tryLock());
1477 assert(!g.tryLock(LockType.read));
1478 f.unlock();
1479 f.lock(LockType.read);
1480 assert(!g.tryLock());
1481 assert(g.tryLock(LockType.read));
1482 f.unlock();
1483 g.unlock();
1486 version (Posix)
1487 @system unittest
1489 static if (__traits(compiles, { import std.process : spawnProcess; }))
1491 static import std.file;
1492 auto deleteme = testFilename();
1493 scope(exit) std.file.remove(deleteme);
1495 // Since locks are per-process, we cannot test lock failures within
1496 // the same process. fork() is used to create a second process.
1497 static void runForked(void delegate() code)
1499 import core.sys.posix.sys.wait : waitpid;
1500 import core.sys.posix.unistd : fork, _exit;
1501 int child, status;
1502 if ((child = fork()) == 0)
1504 code();
1505 _exit(0);
1507 else
1509 assert(waitpid(child, &status, 0) != -1);
1510 assert(status == 0, "Fork crashed");
1514 auto f = File(deleteme, "w+b");
1516 runForked
1518 auto g = File(deleteme, "a+b");
1519 assert(g.tryLock());
1520 g.unlock();
1521 assert(g.tryLock(LockType.read));
1524 assert(f.tryLock());
1525 runForked
1527 auto g = File(deleteme, "a+b");
1528 assert(!g.tryLock());
1529 assert(!g.tryLock(LockType.read));
1531 f.unlock();
1533 f.lock(LockType.read);
1534 runForked
1536 auto g = File(deleteme, "a+b");
1537 assert(!g.tryLock());
1538 assert(g.tryLock(LockType.read));
1539 g.unlock();
1541 f.unlock();
1542 } // static if
1543 } // unittest
1547 Writes its arguments in text format to the file.
1549 Throws: `Exception` if the file is not opened.
1550 `ErrnoException` on an error writing to the file.
1552 void write(S...)(S args)
1554 import std.traits : isBoolean, isIntegral, isAggregateType;
1555 import std.utf : UTFException;
1556 auto w = lockingTextWriter();
1557 foreach (arg; args)
1561 alias A = typeof(arg);
1562 static if (isAggregateType!A || is(A == enum))
1564 import std.format.write : formattedWrite;
1566 formattedWrite(w, "%s", arg);
1568 else static if (isSomeString!A)
1570 put(w, arg);
1572 else static if (isIntegral!A)
1574 import std.conv : toTextRange;
1576 toTextRange(arg, w);
1578 else static if (isBoolean!A)
1580 put(w, arg ? "true" : "false");
1582 else static if (isSomeChar!A)
1584 put(w, arg);
1586 else
1588 import std.format.write : formattedWrite;
1590 // Most general case
1591 formattedWrite(w, "%s", arg);
1594 catch (UTFException e)
1596 /* Reset the writer so that it doesn't throw another
1597 UTFException on destruction. */
1598 w.highSurrogate = '\0';
1599 throw e;
1605 Writes its arguments in text format to the file, followed by a newline.
1607 Throws: `Exception` if the file is not opened.
1608 `ErrnoException` on an error writing to the file.
1610 void writeln(S...)(S args)
1612 write(args, '\n');
1616 Writes its arguments in text format to the file, according to the
1617 format string fmt.
1619 Params:
1620 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
1621 When passed as a compile-time argument, the string will be statically checked
1622 against the argument types passed.
1623 args = Items to write.
1625 Throws: `Exception` if the file is not opened.
1626 `ErrnoException` on an error writing to the file.
1628 void writef(alias fmt, A...)(A args)
1629 if (isSomeString!(typeof(fmt)))
1631 import std.format : checkFormatException;
1633 alias e = checkFormatException!(fmt, A);
1634 static assert(!e, e);
1635 return this.writef(fmt, args);
1638 /// ditto
1639 void writef(Char, A...)(in Char[] fmt, A args)
1641 import std.format.write : formattedWrite;
1643 formattedWrite(lockingTextWriter(), fmt, args);
1646 /// Equivalent to `file.writef(fmt, args, '\n')`.
1647 void writefln(alias fmt, A...)(A args)
1648 if (isSomeString!(typeof(fmt)))
1650 import std.format : checkFormatException;
1652 alias e = checkFormatException!(fmt, A);
1653 static assert(!e, e);
1654 return this.writefln(fmt, args);
1657 /// ditto
1658 void writefln(Char, A...)(in Char[] fmt, A args)
1660 import std.format.write : formattedWrite;
1662 auto w = lockingTextWriter();
1663 formattedWrite(w, fmt, args);
1664 w.put('\n');
1668 Read line from the file handle and return it as a specified type.
1670 This version manages its own read buffer, which means one memory allocation per call. If you are not
1671 retaining a reference to the read data, consider the `File.readln(buf)` version, which may offer
1672 better performance as it can reuse its read buffer.
1674 Params:
1675 S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
1676 terminator = Line terminator (by default, `'\n'`).
1678 Note:
1679 String terminators are not supported due to ambiguity with readln(buf) below.
1681 Returns:
1682 The line that was read, including the line terminator character.
1684 Throws:
1685 `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
1687 Example:
1689 // Reads `stdin` and writes it to `stdout`.
1690 import std.stdio;
1692 void main()
1694 string line;
1695 while ((line = stdin.readln()) !is null)
1696 write(line);
1700 S readln(S = string)(dchar terminator = '\n') @safe
1701 if (isSomeString!S)
1703 Unqual!(ElementEncodingType!S)[] buf;
1704 readln(buf, terminator);
1705 return (() @trusted => cast(S) buf)();
1708 @safe unittest
1710 import std.algorithm.comparison : equal;
1711 static import std.file;
1712 import std.meta : AliasSeq;
1714 auto deleteme = testFilename();
1715 std.file.write(deleteme, "hello\nworld\n");
1716 scope(exit) std.file.remove(deleteme);
1717 static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
1719 auto witness = [ "hello\n", "world\n" ];
1720 auto f = File(deleteme);
1721 uint i = 0;
1722 String buf;
1723 while ((buf = f.readln!String()).length)
1725 assert(i < witness.length);
1726 assert(equal(buf, witness[i++]));
1728 assert(i == witness.length);
1732 @safe unittest
1734 static import std.file;
1735 import std.typecons : Tuple;
1737 auto deleteme = testFilename();
1738 std.file.write(deleteme, "cześć \U0002000D");
1739 scope(exit) std.file.remove(deleteme);
1740 uint[] lengths = [12,8,7];
1741 static foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
1743 immutable(C)[] witness = "cześć \U0002000D";
1744 auto buf = File(deleteme).readln!(immutable(C)[])();
1745 assert(buf.length == lengths[i]);
1746 assert(buf == witness);
1751 Read line from the file handle and write it to `buf[]`, including
1752 terminating character.
1754 This can be faster than $(D line = File.readln()) because you can reuse
1755 the buffer for each call. Note that reusing the buffer means that you
1756 must copy the previous contents if you wish to retain them.
1758 Params:
1759 buf = Buffer used to store the resulting line data. buf is
1760 enlarged if necessary, then set to the slice exactly containing the line.
1761 terminator = Line terminator (by default, `'\n'`). Use
1762 $(REF newline, std,ascii) for portability (unless the file was opened in
1763 text mode).
1765 Returns:
1766 0 for end of file, otherwise number of characters read.
1767 The return value will always be equal to `buf.length`.
1769 Throws: `StdioException` on I/O error, or `UnicodeException` on Unicode
1770 conversion error.
1772 Example:
1774 // Read lines from `stdin` into a string
1775 // Ignore lines starting with '#'
1776 // Write the string to `stdout`
1777 import std.stdio;
1779 void main()
1781 string output;
1782 char[] buf;
1784 while (stdin.readln(buf))
1786 if (buf[0] == '#')
1787 continue;
1789 output ~= buf;
1792 write(output);
1796 This method can be more efficient than the one in the previous example
1797 because `stdin.readln(buf)` reuses (if possible) memory allocated
1798 for `buf`, whereas $(D line = stdin.readln()) makes a new memory allocation
1799 for every line.
1801 For even better performance you can help `readln` by passing in a
1802 large buffer to avoid memory reallocations. This can be done by reusing the
1803 largest buffer returned by `readln`:
1805 Example:
1807 // Read lines from `stdin` and count words
1808 import std.array, std.stdio;
1810 void main()
1812 char[] buf;
1813 size_t words = 0;
1815 while (!stdin.eof)
1817 char[] line = buf;
1818 stdin.readln(line);
1819 if (line.length > buf.length)
1820 buf = line;
1822 words += line.split.length;
1825 writeln(words);
1828 This is actually what $(LREF byLine) does internally, so its usage
1829 is recommended if you want to process a complete file.
1831 size_t readln(C)(ref C[] buf, dchar terminator = '\n') @safe
1832 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
1834 import std.exception : enforce;
1836 static if (is(C == char))
1838 enforce(_p && _p.handle, "Attempt to read from an unopened file.");
1839 if (_p.orientation == Orientation.unknown)
1841 import core.stdc.wchar_ : fwide;
1842 auto w = fwide(_p.handle, 0);
1843 if (w < 0) _p.orientation = Orientation.narrow;
1844 else if (w > 0) _p.orientation = Orientation.wide;
1846 return readlnImpl(_p.handle, buf, terminator, _p.orientation);
1848 else
1850 string s = readln(terminator);
1851 if (!s.length)
1853 buf = buf[0 .. 0];
1854 return 0;
1857 import std.utf : codeLength;
1858 buf.length = codeLength!C(s);
1859 size_t idx;
1860 foreach (C c; s)
1861 buf[idx++] = c;
1863 return buf.length;
1867 @safe unittest
1869 static import std.file;
1870 auto deleteme = testFilename();
1871 std.file.write(deleteme, "123\n456789");
1872 scope(exit) std.file.remove(deleteme);
1874 auto file = File(deleteme);
1875 char[] buffer = new char[10];
1876 char[] line = buffer;
1877 file.readln(line);
1878 auto beyond = line.length;
1879 buffer[beyond] = 'a';
1880 file.readln(line); // should not write buffer beyond line
1881 assert(buffer[beyond] == 'a');
1884 // https://issues.dlang.org/show_bug.cgi?id=15293
1885 @safe unittest
1887 // @system due to readln
1888 static import std.file;
1889 auto deleteme = testFilename();
1890 std.file.write(deleteme, "a\n\naa");
1891 scope(exit) std.file.remove(deleteme);
1893 auto file = File(deleteme);
1894 char[] buffer;
1895 char[] line;
1897 file.readln(buffer, '\n');
1899 line = buffer;
1900 file.readln(line, '\n');
1902 line = buffer;
1903 file.readln(line, '\n');
1905 assert(line[0 .. 1].capacity == 0);
1908 /** ditto */
1909 size_t readln(C, R)(ref C[] buf, R terminator) @safe
1910 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
1911 isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
1913 import std.algorithm.mutation : swap;
1914 import std.algorithm.searching : endsWith;
1915 import std.range.primitives : back;
1917 auto last = terminator.back;
1918 C[] buf2;
1919 swap(buf, buf2);
1920 for (;;)
1922 if (!readln(buf2, last) || endsWith(buf2, terminator))
1924 if (buf.empty)
1926 buf = buf2;
1928 else
1930 buf ~= buf2;
1932 break;
1934 buf ~= buf2;
1936 return buf.length;
1939 @safe unittest
1941 static import std.file;
1942 import std.typecons : Tuple;
1944 auto deleteme = testFilename();
1945 std.file.write(deleteme, "hello\n\rworld\nhow\n\rare ya");
1946 scope(exit) std.file.remove(deleteme);
1947 foreach (C; Tuple!(char, wchar, dchar).Types)
1949 immutable(C)[][] witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
1950 auto f = File(deleteme);
1951 uint i = 0;
1952 C[] buf;
1953 while (f.readln(buf, "\n\r"))
1955 assert(i < witness.length);
1956 assert(buf == witness[i++]);
1958 assert(buf.length == 0);
1963 * Reads formatted _data from the file using $(REF formattedRead, std,_format).
1964 * Params:
1965 * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
1966 * When passed as a compile-time argument, the string will be statically checked
1967 * against the argument types passed.
1968 * data = Items to be read.
1969 * Returns:
1970 * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
1971 * this number will be less than the number of variables provided.
1972 * Example:
1973 ----
1974 // test.d
1975 void main()
1977 import std.stdio;
1978 auto f = File("input");
1979 foreach (_; 0 .. 3)
1981 int a;
1982 f.readf!" %d"(a);
1983 writeln(++a);
1986 ----
1987 $(CONSOLE
1988 % echo "1 2 3" > input
1989 % rdmd test.d
1995 uint readf(alias format, Data...)(auto ref Data data)
1996 if (isSomeString!(typeof(format)))
1998 import std.format : checkFormatException;
2000 alias e = checkFormatException!(format, Data);
2001 static assert(!e, e);
2002 return this.readf(format, data);
2005 /// ditto
2006 uint readf(Data...)(scope const(char)[] format, auto ref Data data)
2008 import std.format.read : formattedRead;
2010 assert(isOpen);
2011 auto input = LockingTextReader(this);
2012 return formattedRead(input, format, data);
2016 @system unittest
2018 static import std.file;
2020 auto deleteme = std.file.deleteme();
2021 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2022 scope(exit) std.file.remove(deleteme);
2023 string s;
2024 auto f = File(deleteme);
2025 f.readf!"%s\n"(s);
2026 assert(s == "hello", "["~s~"]");
2027 f.readf("%s\n", s);
2028 assert(s == "world", "["~s~"]");
2030 bool b1, b2;
2031 f.readf("%s\n%s\n", b1, b2);
2032 assert(b1 == true && b2 == false);
2035 // backwards compatibility with pointers
2036 @system unittest
2038 // @system due to readf
2039 static import std.file;
2041 auto deleteme = testFilename();
2042 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2043 scope(exit) std.file.remove(deleteme);
2044 string s;
2045 auto f = File(deleteme);
2046 f.readf("%s\n", &s);
2047 assert(s == "hello", "["~s~"]");
2048 f.readf("%s\n", &s);
2049 assert(s == "world", "["~s~"]");
2051 // https://issues.dlang.org/show_bug.cgi?id=11698
2052 bool b1, b2;
2053 f.readf("%s\n%s\n", &b1, &b2);
2054 assert(b1 == true && b2 == false);
2057 // backwards compatibility (mixed)
2058 @system unittest
2060 // @system due to readf
2061 static import std.file;
2063 auto deleteme = testFilename();
2064 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2065 scope(exit) std.file.remove(deleteme);
2066 string s1, s2;
2067 auto f = File(deleteme);
2068 f.readf("%s\n%s\n", s1, &s2);
2069 assert(s1 == "hello");
2070 assert(s2 == "world");
2072 // https://issues.dlang.org/show_bug.cgi?id=11698
2073 bool b1, b2;
2074 f.readf("%s\n%s\n", &b1, b2);
2075 assert(b1 == true && b2 == false);
2078 // Nice error of std.stdio.readf with newlines
2079 // https://issues.dlang.org/show_bug.cgi?id=12260
2080 @system unittest
2082 static import std.file;
2084 auto deleteme = testFilename();
2085 std.file.write(deleteme, "1\n2");
2086 scope(exit) std.file.remove(deleteme);
2087 int input;
2088 auto f = File(deleteme);
2089 f.readf("%s", &input);
2091 import std.conv : ConvException;
2092 import std.exception : collectException;
2093 assert(collectException!ConvException(f.readf("%s", &input)).msg ==
2094 "Unexpected '\\n' when converting from type LockingTextReader to type int");
2098 Returns a temporary file by calling $(CSTDIO tmpfile).
2099 Note that the created file has no $(LREF name).*/
2100 static File tmpfile() @safe
2102 import std.exception : errnoEnforce;
2104 return File(errnoEnforce(.tmpfile(),
2105 "Could not create temporary file with tmpfile()"),
2106 null);
2110 Unsafe function that wraps an existing `FILE*`. The resulting $(D
2111 File) never takes the initiative in closing the file.
2112 Note that the created file has no $(LREF name)*/
2113 /*private*/ static File wrapFile(FILE* f) @safe
2115 import std.exception : enforce;
2117 return File(enforce(f, "Could not wrap null FILE*"),
2118 null, /*uint.max / 2*/ 9999);
2122 Returns the `FILE*` corresponding to this object.
2124 FILE* getFP() @safe pure
2126 import std.exception : enforce;
2128 enforce(_p && _p.handle,
2129 "Attempting to call getFP() on an unopened file");
2130 return _p.handle;
2133 @system unittest
2135 static import core.stdc.stdio;
2136 assert(stdout.getFP() == core.stdc.stdio.stdout);
2140 Returns the file number corresponding to this object.
2142 @property fileno_t fileno() const @trusted
2144 import std.exception : enforce;
2146 enforce(isOpen, "Attempting to call fileno() on an unopened file");
2147 return .fileno(cast(FILE*) _p.handle);
2151 Returns the underlying operating system `HANDLE` (Windows only).
2153 version (StdDdoc)
2154 @property HANDLE windowsHandle();
2156 version (Windows)
2157 @property HANDLE windowsHandle()
2159 return cast(HANDLE)_get_osfhandle(fileno);
2163 // Note: This was documented until 2013/08
2165 Range that reads one line at a time. Returned by $(LREF byLine).
2167 Allows to directly use range operations on lines of a file.
2169 private struct ByLineImpl(Char, Terminator)
2171 private:
2172 import std.typecons : borrow, RefCountedAutoInitialize, SafeRefCounted;
2174 /* Ref-counting stops the source range's Impl
2175 * from getting out of sync after the range is copied, e.g.
2176 * when accessing range.front, then using std.range.take,
2177 * then accessing range.front again. */
2178 alias PImpl = SafeRefCounted!(Impl, RefCountedAutoInitialize.no);
2179 PImpl impl;
2181 static if (isScalarType!Terminator)
2182 enum defTerm = '\n';
2183 else
2184 enum defTerm = cast(Terminator)"\n";
2186 public:
2187 this(File f, KeepTerminator kt = No.keepTerminator,
2188 Terminator terminator = defTerm)
2190 impl = PImpl(f, kt, terminator);
2193 /* Verifiably `@safe` when built with -preview=DIP1000. */
2194 @property bool empty() @trusted
2196 // Using `ref` is actually necessary here.
2197 return impl.borrow!((ref i) => i.empty);
2200 /* Verifiably `@safe` when built with -preview=DIP1000. */
2201 @property Char[] front() @trusted
2203 // Using `ref` is likely optional here.
2204 return impl.borrow!((ref i) => i.front);
2207 /* Verifiably `@safe` when built with -preview=DIP1000. */
2208 void popFront() @trusted
2210 return impl.borrow!((ref i) => i.popFront());
2213 private:
2214 struct Impl
2216 private:
2217 File file;
2218 Char[] line;
2219 Char[] buffer;
2220 Terminator terminator;
2221 KeepTerminator keepTerminator;
2222 bool haveLine;
2224 @safe:
2225 public:
2226 this(File f, KeepTerminator kt, Terminator terminator)
2228 file = f;
2229 this.terminator = terminator;
2230 keepTerminator = kt;
2233 // Range primitive implementations.
2234 @property bool empty()
2236 needLine();
2237 return line is null;
2240 @property Char[] front()
2242 needLine();
2243 return line;
2246 void popFront()
2248 needLine();
2249 haveLine = false;
2252 private:
2253 void needLine()
2255 if (haveLine)
2256 return;
2257 import std.algorithm.searching : endsWith;
2258 assert(file.isOpen);
2259 line = buffer;
2260 file.readln(line, terminator);
2261 if (line.length > buffer.length)
2263 buffer = line;
2265 if (line.empty)
2267 file.detach();
2268 line = null;
2270 else if (keepTerminator == No.keepTerminator
2271 && endsWith(line, terminator))
2273 static if (isScalarType!Terminator)
2274 enum tlen = 1;
2275 else static if (isArray!Terminator)
2277 static assert(
2278 is(immutable ElementEncodingType!Terminator == immutable Char));
2279 const tlen = terminator.length;
2281 else
2282 static assert(false);
2283 line = line[0 .. line.length - tlen];
2285 haveLine = true;
2291 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2292 set up to read from the file handle one line at a time.
2294 The element type for the range will be `Char[]`. Range primitives
2295 may throw `StdioException` on I/O error.
2297 Note:
2298 Each `front` will not persist after $(D
2299 popFront) is called, so the caller must copy its contents (e.g. by
2300 calling `to!string`) when retention is needed. If the caller needs
2301 to retain a copy of every line, use the $(LREF byLineCopy) function
2302 instead.
2304 Params:
2305 Char = Character type for each line, defaulting to `char`.
2306 keepTerminator = Use `Yes.keepTerminator` to include the
2307 terminator at the end of each line.
2308 terminator = Line separator (`'\n'` by default). Use
2309 $(REF newline, std,ascii) for portability (unless the file was opened in
2310 text mode).
2312 Example:
2313 ----
2314 import std.algorithm, std.stdio, std.string;
2315 // Count words in a file using ranges.
2316 void main()
2318 auto file = File("file.txt"); // Open for reading
2319 const wordCount = file.byLine() // Read lines
2320 .map!split // Split into words
2321 .map!(a => a.length) // Count words per line
2322 .sum(); // Total word count
2323 writeln(wordCount);
2325 ----
2327 Example:
2328 ----
2329 import std.range, std.stdio;
2330 // Read lines using foreach.
2331 void main()
2333 auto file = File("file.txt"); // Open for reading
2334 auto range = file.byLine();
2335 // Print first three lines
2336 foreach (line; range.take(3))
2337 writeln(line);
2338 // Print remaining lines beginning with '#'
2339 foreach (line; range)
2341 if (!line.empty && line[0] == '#')
2342 writeln(line);
2345 ----
2346 Notice that neither example accesses the line data returned by
2347 `front` after the corresponding `popFront` call is made (because
2348 the contents may well have changed).
2349 ----
2351 Windows specific Example:
2352 ----
2353 import std.stdio;
2355 version (Windows)
2356 void main()
2359 foreach (line; File("file.txt").byLine(No.keepTerminator, "\r\n"))
2361 writeln("|"~line~"|");
2362 if (line == "HelloWorld")
2363 writeln("^This Line is here.");
2368 auto byLine(Terminator = char, Char = char)
2369 (KeepTerminator keepTerminator = No.keepTerminator,
2370 Terminator terminator = '\n')
2371 if (isScalarType!Terminator)
2373 return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
2376 /// ditto
2377 auto byLine(Terminator, Char = char)
2378 (KeepTerminator keepTerminator, Terminator terminator)
2379 if (is(immutable ElementEncodingType!Terminator == immutable Char))
2381 return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
2384 @safe unittest
2386 static import std.file;
2387 auto deleteme = testFilename();
2388 std.file.write(deleteme, "hi");
2389 scope(success) std.file.remove(deleteme);
2391 import std.meta : AliasSeq;
2392 static foreach (T; AliasSeq!(char, wchar, dchar))
2394 auto blc = File(deleteme).byLine!(T, T);
2395 assert(blc.front == "hi");
2396 // check front is cached
2397 assert(blc.front is blc.front);
2401 // https://issues.dlang.org/show_bug.cgi?id=19980
2402 @safe unittest
2404 static import std.file;
2405 auto deleteme = testFilename();
2406 std.file.write(deleteme, "Line 1\nLine 2\nLine 3\n");
2407 scope(success) std.file.remove(deleteme);
2409 auto f = File(deleteme);
2410 f.byLine();
2411 f.byLine();
2412 assert(f.byLine().front == "Line 1");
2415 private struct ByLineCopy(Char, Terminator)
2417 private:
2418 import std.typecons : borrow, RefCountedAutoInitialize, SafeRefCounted;
2420 /* Ref-counting stops the source range's ByLineCopyImpl
2421 * from getting out of sync after the range is copied, e.g.
2422 * when accessing range.front, then using std.range.take,
2423 * then accessing range.front again. */
2424 alias Impl = SafeRefCounted!(ByLineCopyImpl!(Char, Terminator),
2425 RefCountedAutoInitialize.no);
2426 Impl impl;
2428 public:
2429 this(File f, KeepTerminator kt, Terminator terminator)
2431 impl = Impl(f, kt, terminator);
2434 /* Verifiably `@safe` when built with -preview=DIP1000. */
2435 @property bool empty() @trusted
2437 // Using `ref` is actually necessary here.
2438 return impl.borrow!((ref i) => i.empty);
2441 /* Verifiably `@safe` when built with -preview=DIP1000. */
2442 @property Char[] front() @trusted
2444 // Using `ref` is likely optional here.
2445 return impl.borrow!((ref i) => i.front);
2448 /* Verifiably `@safe` when built with -preview=DIP1000. */
2449 void popFront() @trusted
2451 impl.borrow!((ref i) => i.popFront());
2455 private struct ByLineCopyImpl(Char, Terminator)
2457 ByLineImpl!(Unqual!Char, Terminator).Impl impl;
2458 bool gotFront;
2459 Char[] line;
2461 public:
2462 this(File f, KeepTerminator kt, Terminator terminator)
2464 impl = ByLineImpl!(Unqual!Char, Terminator).Impl(f, kt, terminator);
2467 @property bool empty()
2469 return impl.empty;
2472 @property front()
2474 if (!gotFront)
2476 line = impl.front.dup;
2477 gotFront = true;
2479 return line;
2482 void popFront()
2484 impl.popFront();
2485 gotFront = false;
2490 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2491 set up to read from the file handle one line
2492 at a time. Each line will be newly allocated. `front` will cache
2493 its value to allow repeated calls without unnecessary allocations.
2495 Note: Due to caching byLineCopy can be more memory-efficient than
2496 `File.byLine.map!idup`.
2498 The element type for the range will be `Char[]`. Range
2499 primitives may throw `StdioException` on I/O error.
2501 Params:
2502 Char = Character type for each line, defaulting to $(D immutable char).
2503 keepTerminator = Use `Yes.keepTerminator` to include the
2504 terminator at the end of each line.
2505 terminator = Line separator (`'\n'` by default). Use
2506 $(REF newline, std,ascii) for portability (unless the file was opened in
2507 text mode).
2509 Example:
2510 ----
2511 import std.algorithm, std.array, std.stdio;
2512 // Print sorted lines of a file.
2513 void main()
2515 auto sortedLines = File("file.txt") // Open for reading
2516 .byLineCopy() // Read persistent lines
2517 .array() // into an array
2518 .sort(); // then sort them
2519 foreach (line; sortedLines)
2520 writeln(line);
2522 ----
2523 See_Also:
2524 $(REF readText, std,file)
2526 auto byLineCopy(Terminator = char, Char = immutable char)
2527 (KeepTerminator keepTerminator = No.keepTerminator,
2528 Terminator terminator = '\n')
2529 if (isScalarType!Terminator)
2531 return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2534 /// ditto
2535 auto byLineCopy(Terminator, Char = immutable char)
2536 (KeepTerminator keepTerminator, Terminator terminator)
2537 if (is(immutable ElementEncodingType!Terminator == immutable Char))
2539 return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2542 @safe unittest
2544 static assert(is(typeof(File("").byLine.front) == char[]));
2545 static assert(is(typeof(File("").byLineCopy.front) == string));
2546 static assert(
2547 is(typeof(File("").byLineCopy!(char, char).front) == char[]));
2550 @safe unittest
2552 import std.algorithm.comparison : equal;
2553 static import std.file;
2555 auto deleteme = testFilename();
2556 std.file.write(deleteme, "");
2557 scope(success) std.file.remove(deleteme);
2559 // Test empty file
2560 auto f = File(deleteme);
2561 foreach (line; f.byLine())
2563 assert(false);
2565 f.detach();
2566 assert(!f.isOpen);
2568 void test(Terminator)(string txt, in string[] witness,
2569 KeepTerminator kt, Terminator term, bool popFirstLine = false)
2571 import std.algorithm.sorting : sort;
2572 import std.array : array;
2573 import std.conv : text;
2574 import std.range.primitives : walkLength;
2576 uint i;
2577 std.file.write(deleteme, txt);
2578 auto f = File(deleteme);
2579 scope(exit)
2581 f.close();
2582 assert(!f.isOpen);
2584 auto lines = f.byLine(kt, term);
2585 if (popFirstLine)
2587 lines.popFront();
2588 i = 1;
2590 assert(lines.empty || lines.front is lines.front);
2591 foreach (line; lines)
2593 assert(line == witness[i++]);
2595 assert(i == witness.length, text(i, " != ", witness.length));
2597 // https://issues.dlang.org/show_bug.cgi?id=11830
2598 auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
2599 assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
2601 // test persistent lines
2602 assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
2605 KeepTerminator kt = No.keepTerminator;
2606 test("", null, kt, '\n');
2607 test("\n", [ "" ], kt, '\n');
2608 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n');
2609 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true);
2610 test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n');
2611 test("foo", [ "foo" ], kt, '\n', true);
2612 test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
2613 kt, "\r\n");
2614 test("sue\r", ["sue"], kt, '\r');
2616 kt = Yes.keepTerminator;
2617 test("", null, kt, '\n');
2618 test("\n", [ "\n" ], kt, '\n');
2619 test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n');
2620 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n');
2621 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true);
2622 test("foo", [ "foo" ], kt, '\n');
2623 test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
2624 kt, "\r\n");
2625 test("sue\r", ["sue\r"], kt, '\r');
2628 @safe unittest
2630 import std.algorithm.comparison : equal;
2631 import std.range : drop, take;
2633 version (Win64)
2635 static import std.file;
2637 /* the C function tmpfile doesn't seem to work, even when called from C */
2638 auto deleteme = testFilename();
2639 auto file = File(deleteme, "w+");
2640 scope(success) std.file.remove(deleteme);
2642 else version (CRuntime_Bionic)
2644 static import std.file;
2646 /* the C function tmpfile doesn't work when called from a shared
2647 library apk:
2648 https://code.google.com/p/android/issues/detail?id=66815 */
2649 auto deleteme = testFilename();
2650 auto file = File(deleteme, "w+");
2651 scope(success) std.file.remove(deleteme);
2653 else
2654 auto file = File.tmpfile();
2655 file.write("1\n2\n3\n");
2657 // https://issues.dlang.org/show_bug.cgi?id=9599
2658 file.rewind();
2659 File.ByLineImpl!(char, char) fbl = file.byLine();
2660 auto fbl2 = fbl;
2661 assert(fbl.front == "1");
2662 assert(fbl.front is fbl2.front);
2663 assert(fbl.take(1).equal(["1"]));
2664 assert(fbl.equal(["2", "3"]));
2665 assert(fbl.empty);
2666 assert(file.isOpen); // we still have a valid reference
2668 file.rewind();
2669 fbl = file.byLine();
2670 assert(!fbl.drop(2).empty);
2671 assert(fbl.equal(["3"]));
2672 assert(fbl.empty);
2673 assert(file.isOpen);
2675 file.detach();
2676 assert(!file.isOpen);
2679 @safe unittest
2681 static import std.file;
2682 auto deleteme = testFilename();
2683 std.file.write(deleteme, "hi");
2684 scope(success) std.file.remove(deleteme);
2686 auto blc = File(deleteme).byLineCopy;
2687 assert(!blc.empty);
2688 // check front is cached
2689 assert(blc.front is blc.front);
2693 Creates an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2694 set up to parse one line at a time from the file into a tuple.
2696 Range primitives may throw `StdioException` on I/O error.
2698 Params:
2699 format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
2701 Returns:
2702 The input range set up to parse one line at a time into a record tuple.
2704 See_Also:
2706 It is similar to $(LREF byLine) and uses
2707 $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood.
2709 template byRecord(Fields...)
2711 auto byRecord(string format)
2713 return ByRecordImpl!(Fields)(this, format);
2718 @system unittest
2720 static import std.file;
2721 import std.typecons : tuple;
2723 // prepare test file
2724 auto testFile = std.file.deleteme();
2725 scope(failure) printf("Failed test at line %d\n", __LINE__);
2726 std.file.write(testFile, "1 2\n4 1\n5 100");
2727 scope(exit) std.file.remove(testFile);
2729 File f = File(testFile);
2730 scope(exit) f.close();
2732 auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
2733 uint i;
2734 foreach (e; f.byRecord!(int, int)("%s %s"))
2736 assert(e == expected[i++]);
2740 // Note: This was documented until 2013/08
2742 * Range that reads a chunk at a time.
2744 private struct ByChunkImpl
2746 private:
2747 File file_;
2748 ubyte[] chunk_;
2750 void prime()
2752 chunk_ = file_.rawRead(chunk_);
2753 if (chunk_.length == 0)
2754 file_.detach();
2757 public:
2758 this(File file, size_t size)
2760 this(file, new ubyte[](size));
2763 this(File file, ubyte[] buffer)
2765 import std.exception : enforce;
2766 enforce(buffer.length, "size must be larger than 0");
2767 file_ = file;
2768 chunk_ = buffer;
2769 prime();
2772 // `ByChunk`'s input range primitive operations.
2773 @property nothrow
2774 bool empty() const
2776 return !file_.isOpen;
2779 /// Ditto
2780 @property nothrow
2781 ubyte[] front()
2783 version (assert)
2785 import core.exception : RangeError;
2786 if (empty)
2787 throw new RangeError();
2789 return chunk_;
2792 /// Ditto
2793 void popFront()
2795 version (assert)
2797 import core.exception : RangeError;
2798 if (empty)
2799 throw new RangeError();
2801 prime();
2806 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2807 set up to read from the file handle a chunk at a time.
2809 The element type for the range will be `ubyte[]`. Range primitives
2810 may throw `StdioException` on I/O error.
2812 Example:
2813 ---------
2814 void main()
2816 // Read standard input 4KB at a time
2817 foreach (ubyte[] buffer; stdin.byChunk(4096))
2819 ... use buffer ...
2822 ---------
2824 The parameter may be a number (as shown in the example above) dictating the
2825 size of each chunk. Alternatively, `byChunk` accepts a
2826 user-provided buffer that it uses directly.
2828 Example:
2829 ---------
2830 void main()
2832 // Read standard input 4KB at a time
2833 foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
2835 ... use buffer ...
2838 ---------
2840 In either case, the content of the buffer is reused across calls. That means
2841 `front` will not persist after `popFront` is called, so if retention is
2842 needed, the caller must copy its contents (e.g. by calling `buffer.dup`).
2844 In the example above, `buffer.length` is 4096 for all iterations, except
2845 for the last one, in which case `buffer.length` may be less than 4096 (but
2846 always greater than zero).
2848 With the mentioned limitations, `byChunk` works with any algorithm
2849 compatible with input ranges.
2851 Example:
2853 // Efficient file copy, 1MB at a time.
2854 import std.algorithm, std.stdio;
2855 void main()
2857 stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
2861 $(REF joiner, std,algorithm,iteration) can be used to join chunks together into
2862 a single range lazily.
2863 Example:
2865 import std.algorithm, std.stdio;
2866 void main()
2868 //Range of ranges
2869 static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
2870 //Range of elements
2871 static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
2875 Returns: A call to `byChunk` returns a range initialized with the `File`
2876 object and the appropriate buffer.
2878 Throws: If the user-provided size is zero or the user-provided buffer
2879 is empty, throws an `Exception`. In case of an I/O error throws
2880 `StdioException`.
2882 auto byChunk(size_t chunkSize)
2884 return ByChunkImpl(this, chunkSize);
2886 /// Ditto
2887 auto byChunk(ubyte[] buffer)
2889 return ByChunkImpl(this, buffer);
2892 @system unittest
2894 static import std.file;
2896 scope(failure) printf("Failed test at line %d\n", __LINE__);
2898 auto deleteme = testFilename();
2899 std.file.write(deleteme, "asd\ndef\nasdf");
2901 auto witness = ["asd\n", "def\n", "asdf" ];
2902 auto f = File(deleteme);
2903 scope(exit)
2905 f.close();
2906 assert(!f.isOpen);
2907 std.file.remove(deleteme);
2910 uint i;
2911 foreach (chunk; f.byChunk(4))
2912 assert(chunk == cast(ubyte[]) witness[i++]);
2914 assert(i == witness.length);
2917 @system unittest
2919 static import std.file;
2921 scope(failure) printf("Failed test at line %d\n", __LINE__);
2923 auto deleteme = testFilename();
2924 std.file.write(deleteme, "asd\ndef\nasdf");
2926 auto witness = ["asd\n", "def\n", "asdf" ];
2927 auto f = File(deleteme);
2928 scope(exit)
2930 f.close();
2931 assert(!f.isOpen);
2932 std.file.remove(deleteme);
2935 uint i;
2936 foreach (chunk; f.byChunk(new ubyte[4]))
2937 assert(chunk == cast(ubyte[]) witness[i++]);
2939 assert(i == witness.length);
2942 // Note: This was documented until 2013/08
2944 `Range` that locks the file and allows fast writing to it.
2946 struct LockingTextWriter
2948 private:
2949 import std.range.primitives : ElementType, isInfinite, isInputRange;
2950 // Access the FILE* handle through the 'file_' member
2951 // to keep the object alive through refcounting
2952 File file_;
2954 // the unshared version of FILE* handle, extracted from the File object
2955 @property _iobuf* handle_() @trusted { return cast(_iobuf*) file_._p.handle; }
2957 // the file's orientation (byte- or wide-oriented)
2958 int orientation_;
2960 // Buffers for when we need to transcode.
2961 wchar highSurrogate = '\0'; // '\0' indicates empty
2962 void highSurrogateShouldBeEmpty() @safe
2964 import std.utf : UTFException;
2965 if (highSurrogate != '\0')
2966 throw new UTFException("unpaired surrogate UTF-16 value");
2968 char[4] rbuf8;
2969 size_t rbuf8Filled = 0;
2970 public:
2972 this(ref File f) @trusted
2974 import std.exception : enforce;
2976 enforce(f._p && f._p.handle, "Attempting to write to closed File");
2977 file_ = f;
2978 FILE* fps = f._p.handle;
2980 version (CRuntime_Microsoft)
2982 // Microsoft doesn't implement fwide. Instead, there's the
2983 // concept of ANSI/UNICODE mode. fputc doesn't work in UNICODE
2984 // mode; fputwc has to be used. So that essentially means
2985 // "wide-oriented" for us.
2986 immutable int mode = _setmode(f.fileno, _O_TEXT);
2987 // Set some arbitrary mode to obtain the previous one.
2988 if (mode != -1) // _setmode() succeeded
2990 _setmode(f.fileno, mode); // Restore previous mode.
2991 if (mode & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT))
2993 orientation_ = 1; // wide
2997 else
2999 import core.stdc.wchar_ : fwide;
3000 orientation_ = fwide(fps, 0);
3003 _FLOCK(fps);
3006 ~this() @trusted
3008 if (auto p = file_._p)
3010 if (p.handle) _FUNLOCK(p.handle);
3012 file_ = File.init;
3013 /* Destroy file_ before possibly throwing. Else it wouldn't be
3014 destroyed, and its reference count would be wrong. */
3015 highSurrogateShouldBeEmpty();
3018 this(this) @trusted
3020 if (auto p = file_._p)
3022 if (p.handle) _FLOCK(p.handle);
3026 /// Range primitive implementations.
3027 void put(A)(scope A writeme)
3028 if ((isSomeChar!(ElementType!A) ||
3029 is(ElementType!A : const(ubyte))) &&
3030 isInputRange!A &&
3031 !isInfinite!A)
3033 import std.exception : errnoEnforce;
3035 alias C = ElementEncodingType!A;
3036 static assert(!is(C == void));
3037 static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[]))
3039 if (orientation_ <= 0)
3041 //file.write(writeme); causes infinite recursion!!!
3042 //file.rawWrite(writeme);
3043 auto result = trustedFwrite(file_._p.handle, writeme);
3044 if (result != writeme.length) errnoEnforce(0);
3045 return;
3049 // put each element in turn.
3050 foreach (c; writeme)
3052 put(c);
3056 /// ditto
3057 void put(C)(scope C c) @safe
3058 if (isSomeChar!C || is(C : const(ubyte)))
3060 import std.utf : decodeFront, encode, stride;
3062 static if (c.sizeof == 1)
3064 highSurrogateShouldBeEmpty();
3065 if (orientation_ <= 0) trustedFPUTC(c, handle_);
3066 else if (c <= 0x7F) trustedFPUTWC(c, handle_);
3067 else if (c >= 0b1100_0000) // start byte of multibyte sequence
3069 rbuf8[0] = c;
3070 rbuf8Filled = 1;
3072 else // continuation byte of multibyte sequence
3074 rbuf8[rbuf8Filled] = c;
3075 ++rbuf8Filled;
3076 if (stride(rbuf8[]) == rbuf8Filled) // sequence is complete
3078 char[] str = rbuf8[0 .. rbuf8Filled];
3079 immutable dchar d = decodeFront(str);
3080 wchar_t[4 / wchar_t.sizeof] wbuf;
3081 immutable size = encode(wbuf, d);
3082 foreach (i; 0 .. size)
3083 trustedFPUTWC(wbuf[i], handle_);
3084 rbuf8Filled = 0;
3088 else static if (c.sizeof == 2)
3090 import std.utf : decode;
3092 if (c <= 0x7F)
3094 highSurrogateShouldBeEmpty();
3095 if (orientation_ <= 0) trustedFPUTC(c, handle_);
3096 else trustedFPUTWC(c, handle_);
3098 else if (0xD800 <= c && c <= 0xDBFF) // high surrogate
3100 highSurrogateShouldBeEmpty();
3101 highSurrogate = c;
3103 else // standalone or low surrogate
3105 dchar d = c;
3106 if (highSurrogate != '\0')
3108 immutable wchar[2] rbuf = [highSurrogate, c];
3109 size_t index = 0;
3110 d = decode(rbuf[], index);
3111 highSurrogate = 0;
3113 if (orientation_ <= 0)
3115 char[4] wbuf;
3116 immutable size = encode(wbuf, d);
3117 foreach (i; 0 .. size)
3118 trustedFPUTC(wbuf[i], handle_);
3120 else
3122 wchar_t[4 / wchar_t.sizeof] wbuf;
3123 immutable size = encode(wbuf, d);
3124 foreach (i; 0 .. size)
3125 trustedFPUTWC(wbuf[i], handle_);
3127 rbuf8Filled = 0;
3130 else // 32-bit characters
3132 import std.utf : encode;
3134 highSurrogateShouldBeEmpty();
3135 if (orientation_ <= 0)
3137 if (c <= 0x7F)
3139 trustedFPUTC(c, handle_);
3141 else
3143 char[4] buf = void;
3144 immutable len = encode(buf, c);
3145 foreach (i ; 0 .. len)
3146 trustedFPUTC(buf[i], handle_);
3149 else
3151 version (Windows)
3153 import std.utf : isValidDchar;
3155 assert(isValidDchar(c));
3156 if (c <= 0xFFFF)
3158 trustedFPUTWC(cast(wchar_t) c, handle_);
3160 else
3162 trustedFPUTWC(cast(wchar_t)
3163 ((((c - 0x10000) >> 10) & 0x3FF)
3164 + 0xD800), handle_);
3165 trustedFPUTWC(cast(wchar_t)
3166 (((c - 0x10000) & 0x3FF) + 0xDC00),
3167 handle_);
3170 else version (Posix)
3172 trustedFPUTWC(cast(wchar_t) c, handle_);
3174 else
3176 static assert(0);
3184 * Output range which locks the file when created, and unlocks the file when it goes
3185 * out of scope.
3187 * Returns: An $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3188 * which accepts string types, `ubyte[]`, individual character types, and
3189 * individual `ubyte`s.
3191 * Note: Writing either arrays of `char`s or `ubyte`s is faster than
3192 * writing each character individually from a range. For large amounts of data,
3193 * writing the contents in chunks using an intermediary array can result
3194 * in a speed increase.
3196 * Throws: $(REF UTFException, std, utf) if the data given is a `char` range
3197 * and it contains malformed UTF data.
3199 * See_Also: $(LREF byChunk) for an example.
3201 auto lockingTextWriter() @safe
3203 return LockingTextWriter(this);
3206 // An output range which optionally locks the file and puts it into
3207 // binary mode (similar to rawWrite). Because it needs to restore
3208 // the file mode on destruction, it is RefCounted on Windows.
3209 struct BinaryWriterImpl(bool locking)
3211 import std.traits : hasIndirections;
3212 private:
3213 // Access the FILE* handle through the 'file_' member
3214 // to keep the object alive through refcounting
3215 File file_;
3216 string name;
3218 version (Windows)
3220 fileno_t fd;
3221 int oldMode;
3224 public:
3225 // Don't use this, but `File.lockingBinaryWriter()` instead.
3226 // Must be public for RefCounted and emplace() in druntime.
3227 this(scope ref File f)
3229 import std.exception : enforce;
3230 file_ = f;
3231 enforce(f._p && f._p.handle);
3232 name = f._name;
3233 FILE* fps = f._p.handle;
3234 static if (locking)
3235 _FLOCK(fps);
3237 version (Windows)
3239 .fflush(fps); // before changing translation mode
3240 fd = .fileno(fps);
3241 oldMode = ._setmode(fd, _O_BINARY);
3245 ~this()
3247 if (!file_._p || !file_._p.handle)
3248 return;
3250 FILE* fps = file_._p.handle;
3252 version (Windows)
3254 .fflush(fps); // before restoring translation mode
3255 ._setmode(fd, oldMode);
3258 _FUNLOCK(fps);
3261 void rawWrite(T)(in T[] buffer)
3263 import std.conv : text;
3264 import std.exception : errnoEnforce;
3266 auto result = trustedFwrite(file_._p.handle, buffer);
3267 if (result == result.max) result = 0;
3268 errnoEnforce(result == buffer.length,
3269 text("Wrote ", result, " instead of ", buffer.length,
3270 " objects of type ", T.stringof, " to file `",
3271 name, "'"));
3274 version (Windows)
3276 @disable this(this);
3278 else
3280 this(this)
3282 if (auto p = file_._p)
3284 if (p.handle) _FLOCK(p.handle);
3289 void put(T)(auto ref scope const T value)
3290 if (!hasIndirections!T &&
3291 !isInputRange!T)
3293 rawWrite((&value)[0 .. 1]);
3296 void put(T)(scope const(T)[] array)
3297 if (!hasIndirections!T &&
3298 !isInputRange!T)
3300 rawWrite(array);
3304 /** Returns an output range that locks the file and allows fast writing to it.
3306 Example:
3307 Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
3308 in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
3310 import std.algorithm, std.complex, std.range, std.stdio;
3312 void main()
3314 enum size = 500;
3315 writef("P5\n%d %d %d\n", size, size, ubyte.max);
3317 iota(-1, 3, 2.0/size).map!(y =>
3318 iota(-1.5, 0.5, 2.0/size).map!(x =>
3319 cast(ubyte)(1+
3320 recurrence!((a, n) => x + y * complex(0, 1) + a[n-1]^^2)(complex(0))
3321 .take(ubyte.max)
3322 .countUntil!(z => z.re^^2 + z.im^^2 > 4))
3325 .copy(stdout.lockingBinaryWriter);
3329 auto lockingBinaryWriter()
3331 alias LockingBinaryWriterImpl = BinaryWriterImpl!true;
3333 version (Windows)
3335 import std.typecons : RefCounted;
3336 alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl;
3338 else
3339 alias LockingBinaryWriter = LockingBinaryWriterImpl;
3341 return LockingBinaryWriter(this);
3344 @system unittest
3346 import std.algorithm.mutation : reverse;
3347 import std.exception : collectException;
3348 static import std.file;
3349 import std.range : only, retro;
3350 import std.string : format;
3352 auto deleteme = testFilename();
3353 scope(exit) collectException(std.file.remove(deleteme));
3356 auto writer = File(deleteme, "wb").lockingBinaryWriter();
3357 auto input = File(deleteme, "rb");
3359 ubyte[1] byteIn = [42];
3360 writer.rawWrite(byteIn);
3361 destroy(writer);
3363 ubyte[1] byteOut = input.rawRead(new ubyte[1]);
3364 assert(byteIn[0] == byteOut[0]);
3367 auto output = File(deleteme, "wb");
3368 auto writer = output.lockingBinaryWriter();
3369 auto input = File(deleteme, "rb");
3371 T[] readExact(T)(T[] buf)
3373 auto result = input.rawRead(buf);
3374 assert(result.length == buf.length,
3375 "Read %d out of %d bytes"
3376 .format(result.length, buf.length));
3377 return result;
3380 // test raw values
3381 ubyte byteIn = 42;
3382 byteIn.only.copy(writer); output.flush();
3383 ubyte byteOut = readExact(new ubyte[1])[0];
3384 assert(byteIn == byteOut);
3386 // test arrays
3387 ubyte[] bytesIn = [1, 2, 3, 4, 5];
3388 bytesIn.copy(writer); output.flush();
3389 ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]);
3390 scope(failure) .writeln(bytesOut);
3391 assert(bytesIn == bytesOut);
3393 // test ranges of values
3394 bytesIn.retro.copy(writer); output.flush();
3395 bytesOut = readExact(bytesOut);
3396 bytesOut.reverse();
3397 assert(bytesIn == bytesOut);
3399 // test string
3400 "foobar".copy(writer); output.flush();
3401 char[] charsOut = readExact(new char[6]);
3402 assert(charsOut == "foobar");
3404 // test ranges of arrays
3405 only("foo", "bar").copy(writer); output.flush();
3406 charsOut = readExact(charsOut);
3407 assert(charsOut == "foobar");
3409 // test that we are writing arrays as is,
3410 // without UTF-8 transcoding
3411 "foo"d.copy(writer); output.flush();
3412 dchar[] dcharsOut = readExact(new dchar[3]);
3413 assert(dcharsOut == "foo");
3416 /** Returns the size of the file in bytes, ulong.max if file is not searchable or throws if the operation fails.
3417 Example:
3419 import std.stdio, std.file;
3421 void main()
3423 string deleteme = "delete.me";
3424 auto file_handle = File(deleteme, "w");
3425 file_handle.write("abc"); //create temporary file
3426 scope(exit) deleteme.remove; //remove temporary file at scope exit
3428 assert(file_handle.size() == 3); //check if file size is 3 bytes
3432 @property ulong size() @safe
3434 import std.exception : collectException;
3436 ulong pos = void;
3437 if (collectException(pos = tell)) return ulong.max;
3438 scope(exit) seek(pos);
3439 seek(0, SEEK_END);
3440 return tell;
3444 @system unittest
3446 @system struct SystemToString
3448 string toString()
3450 return "system";
3454 @trusted struct TrustedToString
3456 string toString()
3458 return "trusted";
3462 @safe struct SafeToString
3464 string toString()
3466 return "safe";
3470 @system void systemTests()
3472 //system code can write to files/stdout with anything!
3473 if (false)
3475 auto f = File();
3477 f.write("just a string");
3478 f.write("string with arg: ", 47);
3479 f.write(SystemToString());
3480 f.write(TrustedToString());
3481 f.write(SafeToString());
3483 write("just a string");
3484 write("string with arg: ", 47);
3485 write(SystemToString());
3486 write(TrustedToString());
3487 write(SafeToString());
3489 f.writeln("just a string");
3490 f.writeln("string with arg: ", 47);
3491 f.writeln(SystemToString());
3492 f.writeln(TrustedToString());
3493 f.writeln(SafeToString());
3495 writeln("just a string");
3496 writeln("string with arg: ", 47);
3497 writeln(SystemToString());
3498 writeln(TrustedToString());
3499 writeln(SafeToString());
3501 f.writef("string with arg: %s", 47);
3502 f.writef("%s", SystemToString());
3503 f.writef("%s", TrustedToString());
3504 f.writef("%s", SafeToString());
3506 writef("string with arg: %s", 47);
3507 writef("%s", SystemToString());
3508 writef("%s", TrustedToString());
3509 writef("%s", SafeToString());
3511 f.writefln("string with arg: %s", 47);
3512 f.writefln("%s", SystemToString());
3513 f.writefln("%s", TrustedToString());
3514 f.writefln("%s", SafeToString());
3516 writefln("string with arg: %s", 47);
3517 writefln("%s", SystemToString());
3518 writefln("%s", TrustedToString());
3519 writefln("%s", SafeToString());
3523 @safe void safeTests()
3525 auto f = File();
3527 //safe code can write to files only with @safe and @trusted code...
3528 if (false)
3530 f.write("just a string");
3531 f.write("string with arg: ", 47);
3532 f.write(TrustedToString());
3533 f.write(SafeToString());
3535 write("just a string");
3536 write("string with arg: ", 47);
3537 write(TrustedToString());
3538 write(SafeToString());
3540 f.writeln("just a string");
3541 f.writeln("string with arg: ", 47);
3542 f.writeln(TrustedToString());
3543 f.writeln(SafeToString());
3545 writeln("just a string");
3546 writeln("string with arg: ", 47);
3547 writeln(TrustedToString());
3548 writeln(SafeToString());
3550 f.writef("string with arg: %s", 47);
3551 f.writef("%s", TrustedToString());
3552 f.writef("%s", SafeToString());
3554 writef("string with arg: %s", 47);
3555 writef("%s", TrustedToString());
3556 writef("%s", SafeToString());
3558 f.writefln("string with arg: %s", 47);
3559 f.writefln("%s", TrustedToString());
3560 f.writefln("%s", SafeToString());
3562 writefln("string with arg: %s", 47);
3563 writefln("%s", TrustedToString());
3564 writefln("%s", SafeToString());
3567 static assert(!__traits(compiles, f.write(SystemToString().toString())));
3568 static assert(!__traits(compiles, f.writeln(SystemToString())));
3569 static assert(!__traits(compiles, f.writef("%s", SystemToString())));
3570 static assert(!__traits(compiles, f.writefln("%s", SystemToString())));
3572 static assert(!__traits(compiles, write(SystemToString().toString())));
3573 static assert(!__traits(compiles, writeln(SystemToString())));
3574 static assert(!__traits(compiles, writef("%s", SystemToString())));
3575 static assert(!__traits(compiles, writefln("%s", SystemToString())));
3578 systemTests();
3579 safeTests();
3582 @safe unittest
3584 import std.exception : collectException;
3585 static import std.file;
3587 auto deleteme = testFilename();
3588 scope(exit) collectException(std.file.remove(deleteme));
3589 std.file.write(deleteme, "1 2 3");
3590 auto f = File(deleteme);
3591 assert(f.size == 5);
3592 assert(f.tell == 0);
3595 @safe unittest
3597 static import std.file;
3598 import std.range : chain, only, repeat;
3599 import std.range.primitives : isOutputRange;
3601 auto deleteme = testFilename();
3602 scope(exit) std.file.remove(deleteme);
3605 auto writer = File(deleteme, "w").lockingTextWriter();
3606 static assert(isOutputRange!(typeof(writer), dchar));
3607 writer.put("日本語");
3608 writer.put("日本語"w);
3609 writer.put("日本語"d);
3610 writer.put('日');
3611 writer.put(chain(only('本'), only('語')));
3612 // https://issues.dlang.org/show_bug.cgi?id=11945
3613 writer.put(repeat('#', 12));
3614 // https://issues.dlang.org/show_bug.cgi?id=17229
3615 writer.put(cast(immutable(ubyte)[])"日本語");
3617 assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語");
3620 @safe unittest // wchar -> char
3622 static import std.file;
3623 import std.exception : assertThrown;
3624 import std.utf : UTFException;
3626 auto deleteme = testFilename();
3627 scope(exit) std.file.remove(deleteme);
3630 auto writer = File(deleteme, "w").lockingTextWriter();
3631 writer.put("\U0001F608"w);
3633 assert(std.file.readText!string(deleteme) == "\U0001F608");
3635 // Test invalid input: unpaired high surrogate
3637 immutable wchar surr = "\U0001F608"w[0];
3638 auto f = File(deleteme, "w");
3639 assertThrown!UTFException(() {
3640 auto writer = f.lockingTextWriter();
3641 writer.put('x');
3642 writer.put(surr);
3643 assertThrown!UTFException(writer.put(char('y')));
3644 assertThrown!UTFException(writer.put(wchar('y')));
3645 assertThrown!UTFException(writer.put(dchar('y')));
3646 assertThrown!UTFException(writer.put(surr));
3647 // First `surr` is still unpaired at this point. `writer` gets
3648 // destroyed now, and the destructor throws a UTFException for
3649 // the unpaired surrogate.
3650 } ());
3652 assert(std.file.readText!string(deleteme) == "x");
3654 // Test invalid input: unpaired low surrogate
3656 immutable wchar surr = "\U0001F608"w[1];
3657 auto writer = File(deleteme, "w").lockingTextWriter();
3658 assertThrown!UTFException(writer.put(surr));
3659 writer.put('y');
3660 assertThrown!UTFException(writer.put(surr));
3662 assert(std.file.readText!string(deleteme) == "y");
3665 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18801
3667 static import std.file;
3668 import std.string : stripLeft;
3670 auto deleteme = testFilename();
3671 scope(exit) std.file.remove(deleteme);
3674 auto writer = File(deleteme, "w,ccs=UTF-8").lockingTextWriter();
3675 writer.put("foo");
3677 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foo");
3680 auto writer = File(deleteme, "a,ccs=UTF-8").lockingTextWriter();
3681 writer.put("bar");
3683 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foobar");
3685 @safe unittest // char/wchar -> wchar_t
3687 import core.stdc.locale : LC_CTYPE, setlocale;
3688 import core.stdc.wchar_ : fwide;
3689 import core.stdc.string : strlen;
3690 import std.algorithm.searching : any, endsWith;
3691 import std.conv : text;
3692 import std.meta : AliasSeq;
3693 import std.string : fromStringz, stripLeft;
3694 static import std.file;
3695 auto deleteme = testFilename();
3696 scope(exit) std.file.remove(deleteme);
3697 const char* oldCt = () @trusted {
3698 const(char)* p = setlocale(LC_CTYPE, null);
3699 // Subsequent calls to `setlocale` might invalidate this return value,
3700 // so duplicate it.
3701 // See: https://github.com/dlang/phobos/pull/7660
3702 return p ? p[0 .. strlen(p) + 1].idup.ptr : null;
3703 }();
3704 const utf8 = ["en_US.UTF-8", "C.UTF-8", ".65001"].any!((loc) @trusted {
3705 return setlocale(LC_CTYPE, loc.ptr).fromStringz.endsWith(loc);
3707 scope(exit) () @trusted { setlocale(LC_CTYPE, oldCt); } ();
3708 alias strs = AliasSeq!("xä\U0001F607", "yö\U0001F608"w);
3710 auto f = File(deleteme, "w");
3711 version (CRuntime_Microsoft)
3713 () @trusted { _setmode(fileno(f.getFP()), _O_U8TEXT); } ();
3715 else
3717 assert(fwide(f.getFP(), 1) == 1);
3719 auto writer = f.lockingTextWriter();
3720 assert(writer.orientation_ == 1);
3721 static foreach (s; strs) writer.put(s);
3723 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") ==
3724 text(strs));
3726 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18789
3728 static import std.file;
3729 auto deleteme = testFilename();
3730 scope(exit) std.file.remove(deleteme);
3731 // converting to char
3733 auto f = File(deleteme, "w");
3734 f.writeln("\U0001F608"w); // UTFException
3736 // converting to wchar_t
3738 auto f = File(deleteme, "w,ccs=UTF-16LE");
3739 // from char
3740 f.writeln("ö"); // writes garbage
3741 f.writeln("\U0001F608"); // ditto
3742 // from wchar
3743 f.writeln("\U0001F608"w); // leads to ErrnoException
3747 @safe unittest
3749 import std.exception : collectException;
3750 auto e = collectException({ File f; f.writeln("Hello!"); }());
3751 assert(e && e.msg == "Attempting to write to closed File");
3754 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=21592
3756 import std.exception : collectException;
3757 import std.utf : UTFException;
3758 static import std.file;
3759 auto deleteme = testFilename();
3760 scope(exit) std.file.remove(deleteme);
3761 auto f = File(deleteme, "w");
3762 auto e = collectException!UTFException(f.writeln(wchar(0xD801)));
3763 assert(e.next is null);
3766 version (StdStressTest)
3768 // https://issues.dlang.org/show_bug.cgi?id=15768
3769 @system unittest
3771 import std.parallelism : parallel;
3772 import std.range : iota;
3774 auto deleteme = testFilename();
3775 stderr = File(deleteme, "w");
3777 foreach (t; 1_000_000.iota.parallel)
3779 stderr.write("aaa");
3784 /// Used to specify the lock type for `File.lock` and `File.tryLock`.
3785 enum LockType
3788 * Specifies a _read (shared) lock. A _read lock denies all processes
3789 * write access to the specified region of the file, including the
3790 * process that first locks the region. All processes can _read the
3791 * locked region. Multiple simultaneous _read locks are allowed, as
3792 * long as there are no exclusive locks.
3794 read,
3797 * Specifies a read/write (exclusive) lock. A read/write lock denies all
3798 * other processes both read and write access to the locked file region.
3799 * If a segment has an exclusive lock, it may not have any shared locks
3800 * or other exclusive locks.
3802 readWrite
3805 struct LockingTextReader
3807 private File _f;
3808 private char _front;
3809 private bool _hasChar;
3811 this(File f)
3813 import std.exception : enforce;
3814 enforce(f.isOpen, "LockingTextReader: File must be open");
3815 _f = f;
3816 _FLOCK(_f._p.handle);
3819 this(this)
3821 _FLOCK(_f._p.handle);
3824 ~this()
3826 if (_hasChar)
3827 ungetc(_front, cast(FILE*)_f._p.handle);
3829 // File locking has its own reference count
3830 if (_f.isOpen) _FUNLOCK(_f._p.handle);
3833 void opAssign(LockingTextReader r)
3835 import std.algorithm.mutation : swap;
3836 swap(this, r);
3839 @property bool empty()
3841 if (!_hasChar)
3843 if (!_f.isOpen || _f.eof)
3844 return true;
3845 immutable int c = _FGETC(cast(_iobuf*) _f._p.handle);
3846 if (c == EOF)
3848 .destroy(_f);
3849 return true;
3851 _front = cast(char) c;
3852 _hasChar = true;
3854 return false;
3857 @property char front()
3859 if (!_hasChar)
3861 version (assert)
3863 import core.exception : RangeError;
3864 if (empty)
3865 throw new RangeError();
3867 else
3869 empty;
3872 return _front;
3875 void popFront()
3877 if (!_hasChar)
3878 empty;
3879 _hasChar = false;
3883 @system unittest
3885 // @system due to readf
3886 static import std.file;
3887 import std.range.primitives : isInputRange;
3889 static assert(isInputRange!LockingTextReader);
3890 auto deleteme = testFilename();
3891 std.file.write(deleteme, "1 2 3");
3892 scope(exit) std.file.remove(deleteme);
3893 int x;
3894 auto f = File(deleteme);
3895 f.readf("%s ", &x);
3896 assert(x == 1);
3897 f.readf("%d ", &x);
3898 assert(x == 2);
3899 f.readf("%d ", &x);
3900 assert(x == 3);
3903 // https://issues.dlang.org/show_bug.cgi?id=13686
3904 @system unittest
3906 import std.algorithm.comparison : equal;
3907 static import std.file;
3908 import std.utf : byDchar;
3910 auto deleteme = testFilename();
3911 std.file.write(deleteme, "Тест");
3912 scope(exit) std.file.remove(deleteme);
3914 string s;
3915 File(deleteme).readf("%s", &s);
3916 assert(s == "Тест");
3918 auto ltr = LockingTextReader(File(deleteme)).byDchar;
3919 assert(equal(ltr, "Тест".byDchar));
3922 // https://issues.dlang.org/show_bug.cgi?id=12320
3923 @system unittest
3925 static import std.file;
3926 auto deleteme = testFilename();
3927 std.file.write(deleteme, "ab");
3928 scope(exit) std.file.remove(deleteme);
3929 auto ltr = LockingTextReader(File(deleteme));
3930 assert(ltr.front == 'a');
3931 ltr.popFront();
3932 assert(ltr.front == 'b');
3933 ltr.popFront();
3934 assert(ltr.empty);
3937 // https://issues.dlang.org/show_bug.cgi?id=14861
3938 @system unittest
3940 // @system due to readf
3941 static import std.file;
3942 auto deleteme = testFilename();
3943 File fw = File(deleteme, "w");
3944 for (int i; i != 5000; i++)
3945 fw.writeln(i, ";", "Иванов;Пётр;Петрович");
3946 fw.close();
3947 scope(exit) std.file.remove(deleteme);
3948 // Test read
3949 File fr = File(deleteme, "r");
3950 scope (exit) fr.close();
3951 int nom; string fam, nam, ot;
3952 // Error format read
3953 while (!fr.eof)
3954 fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot);
3958 * Indicates whether `T` is a file handle, i.e. the type
3959 * is implicitly convertable to $(LREF File) or a pointer to a
3960 * $(REF FILE, core,stdc,stdio).
3962 * Returns:
3963 * `true` if `T` is a file handle, `false` otherwise.
3965 template isFileHandle(T)
3967 enum isFileHandle = is(T : FILE*) ||
3968 is(T : File);
3972 @safe unittest
3974 static assert(isFileHandle!(FILE*));
3975 static assert(isFileHandle!(File));
3979 * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
3981 private @property File trustedStdout() @trusted
3983 return stdout;
3986 /***********************************
3987 Writes its arguments in text format to standard output (without a trailing newline).
3989 Params:
3990 args = the items to write to `stdout`
3992 Throws: In case of an I/O error, throws an `StdioException`.
3994 Example:
3995 Reads `stdin` and writes it to `stdout` with an argument
3996 counter.
3998 import std.stdio;
4000 void main()
4002 string line;
4004 for (size_t count = 0; (line = readln) !is null; count++)
4006 write("Input ", count, ": ", line, "\n");
4011 void write(T...)(T args)
4012 if (!is(T[0] : File))
4014 trustedStdout.write(args);
4017 @system unittest
4019 static import std.file;
4021 scope(failure) printf("Failed test at line %d\n", __LINE__);
4022 void[] buf;
4023 if (false) write(buf);
4024 // test write
4025 auto deleteme = testFilename();
4026 auto f = File(deleteme, "w");
4027 f.write("Hello, ", "world number ", 42, "!");
4028 f.close();
4029 scope(exit) { std.file.remove(deleteme); }
4030 assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!");
4033 /***********************************
4034 * Equivalent to `write(args, '\n')`. Calling `writeln` without
4035 * arguments is valid and just prints a newline to the standard
4036 * output.
4038 * Params:
4039 * args = the items to write to `stdout`
4041 * Throws:
4042 * In case of an I/O error, throws an $(LREF StdioException).
4043 * Example:
4044 * Reads `stdin` and writes it to `stdout` with an argument
4045 * counter.
4047 import std.stdio;
4049 void main()
4051 string line;
4053 for (size_t count = 0; (line = readln) !is null; count++)
4055 writeln("Input ", count, ": ", line);
4060 void writeln(T...)(T args)
4062 static if (T.length == 0)
4064 import std.exception : enforce;
4066 enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
4068 else static if (T.length == 1 &&
4069 is(T[0] : const(char)[]) &&
4070 (is(T[0] == U[], U) || __traits(isStaticArray, T[0])))
4072 // Specialization for strings - a very frequent case
4073 auto w = .trustedStdout.lockingTextWriter();
4075 static if (__traits(isStaticArray, T[0]))
4077 w.put(args[0][]);
4079 else
4081 w.put(args[0]);
4083 w.put('\n');
4085 else
4087 // Most general instance
4088 trustedStdout.write(args, '\n');
4092 @safe unittest
4094 // Just make sure the call compiles
4095 if (false) writeln();
4097 if (false) writeln("wyda");
4099 // https://issues.dlang.org/show_bug.cgi?id=8040
4100 if (false) writeln(null);
4101 if (false) writeln(">", null, "<");
4103 // https://issues.dlang.org/show_bug.cgi?id=14041
4104 if (false)
4106 char[8] a;
4107 writeln(a);
4108 immutable b = a;
4109 b.writeln;
4110 const c = a[];
4111 c.writeln;
4115 @system unittest
4117 static import std.file;
4119 scope(failure) printf("Failed test at line %d\n", __LINE__);
4121 // test writeln
4122 auto deleteme = testFilename();
4123 auto f = File(deleteme, "w");
4124 scope(exit) { std.file.remove(deleteme); }
4125 f.writeln("Hello, ", "world number ", 42, "!");
4126 f.close();
4127 version (Windows)
4128 assert(cast(char[]) std.file.read(deleteme) ==
4129 "Hello, world number 42!\r\n");
4130 else
4131 assert(cast(char[]) std.file.read(deleteme) ==
4132 "Hello, world number 42!\n");
4134 // test writeln on stdout
4135 auto saveStdout = stdout;
4136 scope(exit) stdout = saveStdout;
4137 stdout.open(deleteme, "w");
4138 writeln("Hello, ", "world number ", 42, "!");
4139 stdout.close();
4140 version (Windows)
4141 assert(cast(char[]) std.file.read(deleteme) ==
4142 "Hello, world number 42!\r\n");
4143 else
4144 assert(cast(char[]) std.file.read(deleteme) ==
4145 "Hello, world number 42!\n");
4147 stdout.open(deleteme, "w");
4148 writeln("Hello!"c);
4149 writeln("Hello!"w); // https://issues.dlang.org/show_bug.cgi?id=8386
4150 writeln("Hello!"d); // https://issues.dlang.org/show_bug.cgi?id=8386
4151 writeln("embedded\0null"c); // https://issues.dlang.org/show_bug.cgi?id=8730
4152 stdout.close();
4153 version (Windows)
4154 assert(cast(char[]) std.file.read(deleteme) ==
4155 "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
4156 else
4157 assert(cast(char[]) std.file.read(deleteme) ==
4158 "Hello!\nHello!\nHello!\nembedded\0null\n");
4161 @system unittest
4163 static import std.file;
4165 auto deleteme = testFilename();
4166 auto f = File(deleteme, "w");
4167 scope(exit) { std.file.remove(deleteme); }
4169 enum EI : int { A, B }
4170 enum ED : double { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4171 enum EC : char { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4172 enum ES : string { A = "aaa", B = "bbb" }
4174 f.writeln(EI.A); // false, but A on 2.058
4175 f.writeln(EI.B); // true, but B on 2.058
4177 f.writeln(ED.A); // A
4178 f.writeln(ED.B); // B
4180 f.writeln(EC.A); // A
4181 f.writeln(EC.B); // B
4183 f.writeln(ES.A); // A
4184 f.writeln(ES.B); // B
4186 f.close();
4187 version (Windows)
4188 assert(cast(char[]) std.file.read(deleteme) ==
4189 "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
4190 else
4191 assert(cast(char[]) std.file.read(deleteme) ==
4192 "A\nB\nA\nB\nA\nB\nA\nB\n");
4195 @system unittest
4197 static auto useInit(T)(T ltw)
4199 T val;
4200 val = ltw;
4201 val = T.init;
4202 return val;
4204 useInit(stdout.lockingTextWriter());
4207 @system unittest
4209 // https://issues.dlang.org/show_bug.cgi?id=21920
4210 void function(string) printer = &writeln!string;
4211 if (false) printer("Hello");
4215 /***********************************
4216 Writes formatted data to standard output (without a trailing newline).
4218 Params:
4219 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4220 When passed as a compile-time argument, the string will be statically checked
4221 against the argument types passed.
4222 args = Items to write.
4224 Note: In older versions of Phobos, it used to be possible to write:
4226 ------
4227 writef(stderr, "%s", "message");
4228 ------
4230 to print a message to `stderr`. This syntax is no longer supported, and has
4231 been superceded by:
4233 ------
4234 stderr.writef("%s", "message");
4235 ------
4238 void writef(alias fmt, A...)(A args)
4239 if (isSomeString!(typeof(fmt)))
4241 import std.format : checkFormatException;
4243 alias e = checkFormatException!(fmt, A);
4244 static assert(!e, e);
4245 return .writef(fmt, args);
4248 /// ditto
4249 void writef(Char, A...)(in Char[] fmt, A args)
4251 trustedStdout.writef(fmt, args);
4254 @system unittest
4256 static import std.file;
4258 scope(failure) printf("Failed test at line %d\n", __LINE__);
4260 // test writef
4261 auto deleteme = testFilename();
4262 auto f = File(deleteme, "w");
4263 scope(exit) { std.file.remove(deleteme); }
4264 f.writef!"Hello, %s world number %s!"("nice", 42);
4265 f.close();
4266 assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
4267 // test write on stdout
4268 auto saveStdout = stdout;
4269 scope(exit) stdout = saveStdout;
4270 stdout.open(deleteme, "w");
4271 writef!"Hello, %s world number %s!"("nice", 42);
4272 stdout.close();
4273 assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
4276 /***********************************
4277 * Equivalent to $(D writef(fmt, args, '\n')).
4279 void writefln(alias fmt, A...)(A args)
4280 if (isSomeString!(typeof(fmt)))
4282 import std.format : checkFormatException;
4284 alias e = checkFormatException!(fmt, A);
4285 static assert(!e, e);
4286 return .writefln(fmt, args);
4289 /// ditto
4290 void writefln(Char, A...)(in Char[] fmt, A args)
4292 trustedStdout.writefln(fmt, args);
4295 @system unittest
4297 static import std.file;
4299 scope(failure) printf("Failed test at line %d\n", __LINE__);
4301 // test File.writefln
4302 auto deleteme = testFilename();
4303 auto f = File(deleteme, "w");
4304 scope(exit) { std.file.remove(deleteme); }
4305 f.writefln!"Hello, %s world number %s!"("nice", 42);
4306 f.close();
4307 version (Windows)
4308 assert(cast(char[]) std.file.read(deleteme) ==
4309 "Hello, nice world number 42!\r\n");
4310 else
4311 assert(cast(char[]) std.file.read(deleteme) ==
4312 "Hello, nice world number 42!\n",
4313 cast(char[]) std.file.read(deleteme));
4315 // test writefln
4316 auto saveStdout = stdout;
4317 scope(exit) stdout = saveStdout;
4318 stdout.open(deleteme, "w");
4319 writefln!"Hello, %s world number %s!"("nice", 42);
4320 stdout.close();
4321 version (Windows)
4322 assert(cast(char[]) std.file.read(deleteme) ==
4323 "Hello, nice world number 42!\r\n");
4324 else
4325 assert(cast(char[]) std.file.read(deleteme) ==
4326 "Hello, nice world number 42!\n");
4330 * Reads formatted data from `stdin` using $(REF formattedRead, std,_format).
4331 * Params:
4332 * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4333 * When passed as a compile-time argument, the string will be statically checked
4334 * against the argument types passed.
4335 * args = Items to be read.
4336 * Returns:
4337 * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
4338 * this number will be less than the number of variables provided.
4339 * Example:
4340 ----
4341 // test.d
4342 void main()
4344 import std.stdio;
4345 foreach (_; 0 .. 3)
4347 int a;
4348 readf!" %d"(a);
4349 writeln(++a);
4352 ----
4353 $(CONSOLE
4354 % echo "1 2 3" | rdmd test.d
4360 uint readf(alias format, A...)(auto ref A args)
4361 if (isSomeString!(typeof(format)))
4363 import std.format : checkFormatException;
4365 alias e = checkFormatException!(format, A);
4366 static assert(!e, e);
4367 return .readf(format, args);
4370 /// ditto
4371 uint readf(A...)(scope const(char)[] format, auto ref A args)
4373 return stdin.readf(format, args);
4376 @system unittest
4378 float f;
4379 if (false) readf("%s", &f);
4381 char a;
4382 wchar b;
4383 dchar c;
4384 if (false) readf("%s %s %s", a, b, c);
4385 // backwards compatibility with pointers
4386 if (false) readf("%s %s %s", a, &b, c);
4387 if (false) readf("%s %s %s", &a, &b, &c);
4390 /**********************************
4391 * Read line from `stdin`.
4393 * This version manages its own read buffer, which means one memory allocation per call. If you are not
4394 * retaining a reference to the read data, consider the `readln(buf)` version, which may offer
4395 * better performance as it can reuse its read buffer.
4397 * Returns:
4398 * The line that was read, including the line terminator character.
4399 * Params:
4400 * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
4401 * terminator = Line terminator (by default, `'\n'`).
4402 * Note:
4403 * String terminators are not supported due to ambiguity with readln(buf) below.
4404 * Throws:
4405 * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4406 * Example:
4407 * Reads `stdin` and writes it to `stdout`.
4409 import std.stdio;
4411 void main()
4413 string line;
4414 while ((line = readln()) !is null)
4415 write(line);
4419 S readln(S = string)(dchar terminator = '\n')
4420 if (isSomeString!S)
4422 return stdin.readln!S(terminator);
4425 /**********************************
4426 * Read line from `stdin` and write it to buf[], including terminating character.
4428 * This can be faster than $(D line = readln()) because you can reuse
4429 * the buffer for each call. Note that reusing the buffer means that you
4430 * must copy the previous contents if you wish to retain them.
4432 * Returns:
4433 * `size_t` 0 for end of file, otherwise number of characters read
4434 * Params:
4435 * buf = Buffer used to store the resulting line data. buf is resized as necessary.
4436 * terminator = Line terminator (by default, `'\n'`). Use $(REF newline, std,ascii)
4437 * for portability (unless the file was opened in text mode).
4438 * Throws:
4439 * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4440 * Example:
4441 * Reads `stdin` and writes it to `stdout`.
4443 import std.stdio;
4445 void main()
4447 char[] buf;
4448 while (readln(buf))
4449 write(buf);
4453 size_t readln(C)(ref C[] buf, dchar terminator = '\n')
4454 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
4456 return stdin.readln(buf, terminator);
4459 /** ditto */
4460 size_t readln(C, R)(ref C[] buf, R terminator)
4461 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
4462 isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
4464 return stdin.readln(buf, terminator);
4467 @safe unittest
4469 import std.meta : AliasSeq;
4471 //we can't actually test readln, so at the very least,
4472 //we test compilability
4473 void foo()
4475 readln();
4476 readln('\t');
4477 static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
4479 readln!String();
4480 readln!String('\t');
4482 static foreach (String; AliasSeq!(char[], wchar[], dchar[]))
4484 String buf;
4485 readln(buf);
4486 readln(buf, '\t');
4487 readln(buf, "<br />");
4493 * Convenience function that forwards to `core.sys.posix.stdio.fopen`
4494 * (to `_wfopen` on Windows)
4495 * with appropriately-constructed C-style strings.
4497 private FILE* _fopen(R1, R2)(R1 name, R2 mode = "r")
4498 if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
4499 (isSomeFiniteCharInputRange!R2 || isSomeString!R2))
4501 import std.internal.cstring : tempCString;
4503 auto namez = name.tempCString!FSChar();
4504 auto modez = mode.tempCString!FSChar();
4506 static _fopenImpl(scope const(FSChar)* namez, scope const(FSChar)* modez) @trusted nothrow @nogc
4508 version (Windows)
4510 return _wfopen(namez, modez);
4512 else version (Posix)
4515 * The new opengroup large file support API is transparently
4516 * included in the normal C bindings. https://www.opengroup.org/platform/lfs.html#1.0
4517 * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
4518 * the normal functions work fine. If not, then large file support
4519 * probably isn't available. Do not use the old transitional API
4520 * (the native extern(C) fopen64, https://unix.org/version2/whatsnew/lfs20mar.html#3.0)
4522 import core.sys.posix.stdio : fopen;
4523 return fopen(namez, modez);
4525 else
4527 return fopen(namez, modez);
4530 return _fopenImpl(namez, modez);
4533 version (Posix)
4535 /***********************************
4536 * Convenience function that forwards to `core.sys.posix.stdio.popen`
4537 * with appropriately-constructed C-style strings.
4539 FILE* _popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
4540 if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
4541 (isSomeFiniteCharInputRange!R2 || isSomeString!R2))
4543 import std.internal.cstring : tempCString;
4545 auto namez = name.tempCString!FSChar();
4546 auto modez = mode.tempCString!FSChar();
4548 static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
4550 import core.sys.posix.stdio : popen;
4551 return popen(namez, modez);
4553 return popenImpl(namez, modez);
4558 * Convenience function that forwards to `core.stdc.stdio.fwrite`
4560 private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
4562 return fwrite(obj.ptr, T.sizeof, obj.length, f);
4566 * Convenience function that forwards to `core.stdc.stdio.fread`
4568 private auto trustedFread(T)(FILE* f, T[] obj) @trusted
4569 if (!imported!"std.traits".hasIndirections!T)
4571 return fread(obj.ptr, T.sizeof, obj.length, f);
4574 private auto trustedFread(T)(FILE* f, T[] obj) @system
4575 if (imported!"std.traits".hasIndirections!T)
4577 return fread(obj.ptr, T.sizeof, obj.length, f);
4581 * Iterates through the lines of a file by using `foreach`.
4583 * Example:
4585 ---------
4586 void main()
4588 foreach (string line; lines(stdin))
4590 ... use line ...
4593 ---------
4594 The line terminator (`'\n'` by default) is part of the string read (it
4595 could be missing in the last line of the file). Several types are
4596 supported for `line`, and the behavior of `lines`
4597 changes accordingly:
4599 $(OL $(LI If `line` has type `string`, $(D
4600 wstring), or `dstring`, a new string of the respective type
4601 is allocated every read.) $(LI If `line` has type $(D
4602 char[]), `wchar[]`, `dchar[]`, the line's content
4603 will be reused (overwritten) across reads.) $(LI If `line`
4604 has type `immutable(ubyte)[]`, the behavior is similar to
4605 case (1), except that no UTF checking is attempted upon input.) $(LI
4606 If `line` has type `ubyte[]`, the behavior is
4607 similar to case (2), except that no UTF checking is attempted upon
4608 input.))
4610 In all cases, a two-symbols versions is also accepted, in which case
4611 the first symbol (of integral type, e.g. `ulong` or $(D
4612 uint)) tracks the zero-based number of the current line.
4614 Example:
4615 ----
4616 foreach (ulong i, string line; lines(stdin))
4618 ... use line ...
4620 ----
4622 In case of an I/O error, an `StdioException` is thrown.
4624 See_Also:
4625 $(LREF byLine)
4628 struct lines
4630 private File f;
4631 private dchar terminator = '\n';
4634 Constructor.
4635 Params:
4636 f = File to read lines from.
4637 terminator = Line separator (`'\n'` by default).
4639 this(File f, dchar terminator = '\n') @safe
4641 this.f = f;
4642 this.terminator = terminator;
4645 int opApply(D)(scope D dg)
4647 import std.traits : Parameters;
4648 alias Parms = Parameters!(dg);
4649 static if (isSomeString!(Parms[$ - 1]))
4651 int result = 0;
4652 static if (is(Parms[$ - 1] : const(char)[]))
4653 alias C = char;
4654 else static if (is(Parms[$ - 1] : const(wchar)[]))
4655 alias C = wchar;
4656 else static if (is(Parms[$ - 1] : const(dchar)[]))
4657 alias C = dchar;
4658 C[] line;
4659 static if (Parms.length == 2)
4660 Parms[0] i = 0;
4661 for (;;)
4663 import std.conv : to;
4665 if (!f.readln(line, terminator)) break;
4666 auto copy = to!(Parms[$ - 1])(line);
4667 static if (Parms.length == 2)
4669 result = dg(i, copy);
4670 ++i;
4672 else
4674 result = dg(copy);
4676 if (result != 0) break;
4678 return result;
4680 else
4682 // raw read
4683 return opApplyRaw(dg);
4686 // no UTF checking
4687 int opApplyRaw(D)(scope D dg)
4689 import std.conv : to;
4690 import std.exception : assumeUnique;
4691 import std.traits : Parameters;
4693 alias Parms = Parameters!(dg);
4694 enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
4695 int result = 1;
4696 int c = void;
4697 _FLOCK(f._p.handle);
4698 scope(exit) _FUNLOCK(f._p.handle);
4699 ubyte[] buffer;
4700 static if (Parms.length == 2)
4701 Parms[0] line = 0;
4702 while ((c = _FGETC(cast(_iobuf*) f._p.handle)) != -1)
4704 buffer ~= to!(ubyte)(c);
4705 if (c == terminator)
4707 static if (duplicate)
4708 auto arg = assumeUnique(buffer);
4709 else
4710 alias arg = buffer;
4711 // unlock the file while calling the delegate
4712 _FUNLOCK(f._p.handle);
4713 scope(exit) _FLOCK(f._p.handle);
4714 static if (Parms.length == 1)
4716 result = dg(arg);
4718 else
4720 result = dg(line, arg);
4721 ++line;
4723 if (result) break;
4724 static if (!duplicate)
4725 buffer.length = 0;
4728 // can only reach when _FGETC returned -1
4729 if (!f.eof) throw new StdioException("Error in reading file"); // error occured
4730 return result;
4734 @safe unittest
4737 As pointed out in <https://github.com/dlang/phobos/issues/10605>,
4738 it's a pity that `byLine()` & co. aren't @safe to use yet.
4740 This is a first attempt at working towards that goal.
4741 For now, this test doesn't do much; as there isn't much to do safely yet.
4743 auto deleteMe = testFilename();
4744 scope(exit) { imported!"std.file".remove(deleteMe); }
4746 // Setup
4748 auto f = File(deleteMe, "w");
4749 scope(exit) { f.close(); }
4750 foreach (i; 1 .. 11)
4751 f.writeln(i);
4754 // Actual tests
4756 auto f = File(deleteMe, "r");
4757 scope(exit) { f.close(); }
4759 auto myLines = lines(f);
4760 foreach (string line; myLines)
4761 continue;
4766 auto f = File(deleteMe, "r");
4767 scope(exit) { f.close(); }
4769 auto myByLineCopy = f.byLineCopy;
4770 foreach (line; myByLineCopy)
4771 continue;
4775 auto f = File(deleteMe, "r");
4776 scope(exit) { f.close(); }
4778 auto myByLine = f.byLine;
4779 foreach (line; myByLine)
4780 continue;
4784 @system unittest
4786 static import std.file;
4787 import std.meta : AliasSeq;
4789 scope(failure) printf("Failed test at line %d\n", __LINE__);
4791 auto deleteme = testFilename();
4792 scope(exit) { std.file.remove(deleteme); }
4794 alias TestedWith =
4795 AliasSeq!(string, wstring, dstring,
4796 char[], wchar[], dchar[]);
4797 foreach (T; TestedWith)
4799 // test looping with an empty file
4800 std.file.write(deleteme, "");
4801 auto f = File(deleteme, "r");
4802 foreach (T line; lines(f))
4804 assert(false);
4806 f.close();
4808 // test looping with a file with three lines
4809 std.file.write(deleteme, "Line one\nline two\nline three\n");
4810 f.open(deleteme, "r");
4811 uint i = 0;
4812 foreach (T line; lines(f))
4814 if (i == 0) assert(line == "Line one\n");
4815 else if (i == 1) assert(line == "line two\n");
4816 else if (i == 2) assert(line == "line three\n");
4817 else assert(false);
4818 ++i;
4820 f.close();
4822 // test looping with a file with three lines, last without a newline
4823 std.file.write(deleteme, "Line one\nline two\nline three");
4824 f.open(deleteme, "r");
4825 i = 0;
4826 foreach (T line; lines(f))
4828 if (i == 0) assert(line == "Line one\n");
4829 else if (i == 1) assert(line == "line two\n");
4830 else if (i == 2) assert(line == "line three");
4831 else assert(false);
4832 ++i;
4834 f.close();
4837 // test with ubyte[] inputs
4838 alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]);
4839 foreach (T; TestedWith2)
4841 // test looping with an empty file
4842 std.file.write(deleteme, "");
4843 auto f = File(deleteme, "r");
4844 foreach (T line; lines(f))
4846 assert(false);
4848 f.close();
4850 // test looping with a file with three lines
4851 std.file.write(deleteme, "Line one\nline two\nline three\n");
4852 f.open(deleteme, "r");
4853 uint i = 0;
4854 foreach (T line; lines(f))
4856 if (i == 0) assert(cast(char[]) line == "Line one\n");
4857 else if (i == 1) assert(cast(char[]) line == "line two\n",
4858 T.stringof ~ " " ~ cast(char[]) line);
4859 else if (i == 2) assert(cast(char[]) line == "line three\n");
4860 else assert(false);
4861 ++i;
4863 f.close();
4865 // test looping with a file with three lines, last without a newline
4866 std.file.write(deleteme, "Line one\nline two\nline three");
4867 f.open(deleteme, "r");
4868 i = 0;
4869 foreach (T line; lines(f))
4871 if (i == 0) assert(cast(char[]) line == "Line one\n");
4872 else if (i == 1) assert(cast(char[]) line == "line two\n");
4873 else if (i == 2) assert(cast(char[]) line == "line three");
4874 else assert(false);
4875 ++i;
4877 f.close();
4881 static foreach (T; AliasSeq!(ubyte[]))
4883 // test looping with a file with three lines, last without a newline
4884 // using a counter too this time
4885 std.file.write(deleteme, "Line one\nline two\nline three");
4886 auto f = File(deleteme, "r");
4887 uint i = 0;
4888 foreach (ulong j, T line; lines(f))
4890 if (i == 0) assert(cast(char[]) line == "Line one\n");
4891 else if (i == 1) assert(cast(char[]) line == "line two\n");
4892 else if (i == 2) assert(cast(char[]) line == "line three");
4893 else assert(false);
4894 ++i;
4896 f.close();
4901 Iterates through a file a chunk at a time by using `foreach`.
4903 Example:
4905 ---------
4906 void main()
4908 foreach (ubyte[] buffer; chunks(stdin, 4096))
4910 ... use buffer ...
4913 ---------
4915 The content of `buffer` is reused across calls. In the
4916 example above, `buffer.length` is 4096 for all iterations,
4917 except for the last one, in which case `buffer.length` may
4918 be less than 4096 (but always greater than zero).
4920 In case of an I/O error, an `StdioException` is thrown.
4922 auto chunks(File f, size_t size)
4924 return ChunksImpl(f, size);
4926 private struct ChunksImpl
4928 private File f;
4929 private size_t size;
4930 // private string fileName; // Currently, no use
4932 this(File f, size_t size)
4935 assert(size, "size must be larger than 0");
4939 this.f = f;
4940 this.size = size;
4943 int opApply(D)(scope D dg)
4945 import core.stdc.stdlib : alloca;
4946 import std.exception : enforce;
4948 enforce(f.isOpen, "Attempting to read from an unopened file");
4949 enum maxStackSize = 1024 * 16;
4950 ubyte[] buffer = void;
4951 if (size < maxStackSize)
4952 buffer = (cast(ubyte*) alloca(size))[0 .. size];
4953 else
4954 buffer = new ubyte[size];
4955 size_t r = void;
4956 int result = 1;
4957 uint tally = 0;
4958 while ((r = trustedFread(f._p.handle, buffer)) > 0)
4960 assert(r <= size);
4961 if (r != size)
4963 // error occured
4964 if (!f.eof) throw new StdioException(null);
4965 buffer.length = r;
4967 static if (is(typeof(dg(tally, buffer))))
4969 if ((result = dg(tally, buffer)) != 0) break;
4971 else
4973 if ((result = dg(buffer)) != 0) break;
4975 ++tally;
4977 return result;
4981 @system unittest
4983 static import std.file;
4985 scope(failure) printf("Failed test at line %d\n", __LINE__);
4987 auto deleteme = testFilename();
4988 scope(exit) { std.file.remove(deleteme); }
4990 // test looping with an empty file
4991 std.file.write(deleteme, "");
4992 auto f = File(deleteme, "r");
4993 foreach (ubyte[] line; chunks(f, 4))
4995 assert(false);
4997 f.close();
4999 // test looping with a file with three lines
5000 std.file.write(deleteme, "Line one\nline two\nline three\n");
5001 f = File(deleteme, "r");
5002 uint i = 0;
5003 foreach (ubyte[] line; chunks(f, 3))
5005 if (i == 0) assert(cast(char[]) line == "Lin");
5006 else if (i == 1) assert(cast(char[]) line == "e o");
5007 else if (i == 2) assert(cast(char[]) line == "ne\n");
5008 else break;
5009 ++i;
5011 f.close();
5014 // Issue 21730 - null ptr dereferenced in ChunksImpl.opApply (SIGSEGV)
5015 @system unittest
5017 import std.exception : assertThrown;
5018 static import std.file;
5020 auto deleteme = testFilename();
5021 scope(exit) { if (std.file.exists(deleteme)) std.file.remove(deleteme); }
5023 auto err1 = File(deleteme, "w+x");
5024 err1.close;
5025 std.file.remove(deleteme);
5026 assertThrown(() {foreach (ubyte[] buf; chunks(err1, 4096)) {}}());
5030 Writes an array or range to a file.
5031 Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
5032 Similar to $(REF write, std,file), strings are written as-is,
5033 rather than encoded according to the `File`'s $(HTTP
5034 en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
5035 orientation).
5037 void toFile(T)(T data, string fileName)
5038 if (is(typeof(copy(data, stdout.lockingBinaryWriter))))
5040 copy(data, File(fileName, "wb").lockingBinaryWriter);
5043 @system unittest
5045 static import std.file;
5047 auto deleteme = testFilename();
5048 scope(exit) { std.file.remove(deleteme); }
5050 "Test".toFile(deleteme);
5051 assert(std.file.readText(deleteme) == "Test");
5054 /*********************
5055 * Thrown if I/O errors happen.
5057 class StdioException : Exception
5059 static import core.stdc.errno;
5060 /// Operating system error code.
5061 uint errno;
5064 Initialize with a message and an error code.
5066 this(string message, uint e = core.stdc.errno.errno) @trusted
5068 import std.exception : errnoString;
5069 errno = e;
5070 auto sysmsg = errnoString(errno);
5071 // If e is 0, we don't use the system error message. (The message
5072 // is "Success", which is rather pointless for an exception.)
5073 super(e == 0 ? message
5074 : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
5077 /** Convenience functions that throw an `StdioException`. */
5078 static void opCall(string msg) @safe
5080 throw new StdioException(msg);
5083 /// ditto
5084 static void opCall() @safe
5086 throw new StdioException(null, core.stdc.errno.errno);
5090 enum StdFileHandle: string
5092 stdin = "core.stdc.stdio.stdin",
5093 stdout = "core.stdc.stdio.stdout",
5094 stderr = "core.stdc.stdio.stderr",
5097 // Undocumented but public because the std* handles are aliasing it.
5098 @property ref File makeGlobal(StdFileHandle _iob)()
5100 __gshared File.Impl impl;
5101 __gshared File result;
5103 // Use an inline spinlock to make sure the initializer is only run once.
5104 // We assume there will be at most uint.max / 2 threads trying to initialize
5105 // `handle` at once and steal the high bit to indicate that the globals have
5106 // been initialized.
5107 static shared uint spinlock;
5108 import core.atomic : atomicLoad, atomicOp, MemoryOrder;
5109 if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
5111 for (;;)
5113 if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2)
5114 break;
5115 if (atomicOp!"+="(spinlock, 1) == 1)
5117 with (StdFileHandle)
5118 assert(_iob == stdin || _iob == stdout || _iob == stderr);
5119 impl.handle = cast() mixin(_iob);
5120 result._p = &impl;
5121 atomicOp!"+="(spinlock, uint.max / 2);
5122 break;
5124 atomicOp!"-="(spinlock, 1);
5127 return result;
5130 /** The standard input stream.
5132 Returns:
5133 stdin as a $(LREF File).
5135 Note:
5136 The returned $(LREF File) wraps $(REF stdin,core,stdc,stdio), and
5137 is therefore thread global. Reassigning `stdin` to a different
5138 `File` must be done in a single-threaded or locked context in
5139 order to avoid race conditions.
5141 All reading from `stdin` automatically locks the file globally,
5142 and will cause all other threads calling `read` to wait until
5143 the lock is released.
5145 alias stdin = makeGlobal!(StdFileHandle.stdin);
5148 @safe unittest
5150 // Read stdin, sort lines, write to stdout
5151 import std.algorithm.mutation : copy;
5152 import std.algorithm.sorting : sort;
5153 import std.array : array;
5154 import std.typecons : Yes;
5156 void main()
5158 stdin // read from stdin
5159 .byLineCopy(Yes.keepTerminator) // copying each line
5160 .array() // convert to array of lines
5161 .sort() // sort the lines
5162 .copy( // copy output of .sort to an OutputRange
5163 stdout.lockingTextWriter()); // the OutputRange
5168 The standard output stream.
5170 Returns:
5171 stdout as a $(LREF File).
5173 Note:
5174 The returned $(LREF File) wraps $(REF stdout,core,stdc,stdio), and
5175 is therefore thread global. Reassigning `stdout` to a different
5176 `File` must be done in a single-threaded or locked context in
5177 order to avoid race conditions.
5179 All writing to `stdout` automatically locks the file globally,
5180 and will cause all other threads calling `write` to wait until
5181 the lock is released.
5183 alias stdout = makeGlobal!(StdFileHandle.stdout);
5186 @safe unittest
5188 void main()
5190 stdout.writeln("Write a message to stdout.");
5195 @safe unittest
5197 void main()
5199 import std.algorithm.iteration : filter, map, sum;
5200 import std.format : format;
5201 import std.range : iota, tee;
5203 int len;
5204 const r = 6.iota
5205 .filter!(a => a % 2) // 1 3 5
5206 .map!(a => a * 2) // 2 6 10
5207 .tee!(_ => stdout.writefln("len: %d", len++))
5208 .sum;
5210 assert(r == 18);
5215 @safe unittest
5217 void main()
5219 import std.algorithm.mutation : copy;
5220 import std.algorithm.iteration : map;
5221 import std.format : format;
5222 import std.range : iota;
5224 10.iota
5225 .map!(e => "N: %d".format(e))
5226 .copy(stdout.lockingTextWriter()); // the OutputRange
5231 The standard error stream.
5233 Returns:
5234 stderr as a $(LREF File).
5236 Note:
5237 The returned $(LREF File) wraps $(REF stderr,core,stdc,stdio), and
5238 is therefore thread global. Reassigning `stderr` to a different
5239 `File` must be done in a single-threaded or locked context in
5240 order to avoid race conditions.
5242 All writing to `stderr` automatically locks the file globally,
5243 and will cause all other threads calling `write` to wait until
5244 the lock is released.
5246 alias stderr = makeGlobal!(StdFileHandle.stderr);
5249 @safe unittest
5251 void main()
5253 stderr.writeln("Write a message to stderr.");
5257 @system unittest
5259 static import std.file;
5260 import std.typecons : tuple;
5262 scope(failure) printf("Failed test at line %d\n", __LINE__);
5263 auto deleteme = testFilename();
5265 std.file.write(deleteme, "1 2\n4 1\n5 100");
5266 scope(exit) std.file.remove(deleteme);
5268 File f = File(deleteme);
5269 scope(exit) f.close();
5270 auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
5271 uint i;
5272 foreach (e; f.byRecord!(int, int)("%s %s"))
5274 //writeln(e);
5275 assert(e == t[i++]);
5277 assert(i == 3);
5281 @safe unittest
5283 // Retain backwards compatibility
5284 // https://issues.dlang.org/show_bug.cgi?id=17472
5285 static assert(is(typeof(stdin) == File));
5286 static assert(is(typeof(stdout) == File));
5287 static assert(is(typeof(stderr) == File));
5290 // roll our own appender, but with "safe" arrays
5291 private struct ReadlnAppender
5293 char[] buf;
5294 size_t pos;
5295 bool safeAppend = false;
5297 void initialize(char[] b) @safe
5299 buf = b;
5300 pos = 0;
5302 @property char[] data() @trusted
5304 if (safeAppend)
5305 assumeSafeAppend(buf.ptr[0 .. pos]);
5306 return buf.ptr[0 .. pos];
5309 bool reserveWithoutAllocating(size_t n)
5311 if (buf.length >= pos + n) // buf is already large enough
5312 return true;
5314 immutable curCap = buf.capacity;
5315 if (curCap >= pos + n)
5317 buf.length = curCap;
5318 /* Any extra capacity we end up not using can safely be claimed
5319 by someone else. */
5320 safeAppend = true;
5321 return true;
5324 return false;
5326 void reserve(size_t n) @trusted
5328 import core.stdc.string : memcpy;
5329 if (!reserveWithoutAllocating(n))
5331 size_t ncap = buf.length * 2 + 128 + n;
5332 char[] nbuf = new char[ncap];
5333 memcpy(nbuf.ptr, buf.ptr, pos);
5334 buf = nbuf;
5335 // Allocated a new buffer. No one else knows about it.
5336 safeAppend = true;
5339 void putchar(char c) @trusted
5341 reserve(1);
5342 buf.ptr[pos++] = c;
5344 void putdchar(dchar dc) @trusted
5346 import std.utf : encode, UseReplacementDchar;
5348 char[4] ubuf;
5349 immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc);
5350 reserve(size);
5351 foreach (c; ubuf)
5352 buf.ptr[pos++] = c;
5354 void putonly(const char[] b) @trusted
5356 import core.stdc.string : memcpy;
5357 assert(pos == 0); // assume this is the only put call
5358 if (reserveWithoutAllocating(b.length))
5359 memcpy(buf.ptr + pos, b.ptr, b.length);
5360 else
5361 buf = b.dup;
5362 pos = b.length;
5366 private struct LockedFile
5368 private @system _iobuf* fp;
5370 this(FILE* fps) @trusted
5372 _FLOCK(fps);
5373 // Since fps is now locked, we can cast away shared
5374 fp = cast(_iobuf*) fps;
5377 @disable this();
5378 @disable this(this);
5379 @disable void opAssign(LockedFile);
5381 // these use unlocked fgetc calls
5382 @trusted fgetc() { return _FGETC(fp); }
5383 @trusted fgetwc() { return _FGETWC(fp); }
5385 ~this() @trusted
5387 _FUNLOCK(cast(FILE*) fp);
5391 @safe unittest
5393 void f() @safe
5395 FILE* fps;
5396 auto lf = LockedFile(fps);
5397 static assert(!__traits(compiles, lf = LockedFile(fps)));
5398 version (ShouldFail)
5400 lf.fps = null; // error with -preview=systemVariables
5405 // Private implementation of readln
5406 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation) @safe
5408 version (CRuntime_Microsoft)
5410 auto lf = LockedFile(fps);
5412 ReadlnAppender app;
5413 app.initialize(buf);
5415 int c;
5416 while ((c = lf.fgetc()) != -1)
5418 app.putchar(cast(char) c);
5419 if (c == terminator)
5421 buf = app.data;
5422 return buf.length;
5427 if (ferror(fps))
5428 StdioException();
5429 buf = app.data;
5430 return buf.length;
5432 else static if (__traits(compiles, core.sys.posix.stdio.getdelim))
5434 if (orientation == File.Orientation.wide)
5436 import core.stdc.wchar_ : fwide;
5438 auto lf = LockedFile(fps);
5439 /* Stream is in wide characters.
5440 * Read them and convert to chars.
5442 version (Windows)
5444 buf.length = 0;
5445 for (int c = void; (c = lf.fgetwc()) != -1; )
5447 if ((c & ~0x7F) == 0)
5448 { buf ~= c;
5449 if (c == terminator)
5450 break;
5452 else
5454 if (c >= 0xD800 && c <= 0xDBFF)
5456 int c2 = void;
5457 if ((c2 = lf.fgetwc()) != -1 ||
5458 c2 < 0xDC00 && c2 > 0xDFFF)
5460 StdioException("unpaired UTF-16 surrogate");
5462 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5464 import std.utf : encode;
5465 encode(buf, c);
5468 if (ferror(fps))
5469 StdioException();
5470 return buf.length;
5472 else version (Posix)
5474 buf.length = 0;
5475 for (int c; (c = lf.fgetwc()) != -1; )
5477 import std.utf : encode;
5479 if ((c & ~0x7F) == 0)
5480 buf ~= cast(char) c;
5481 else
5482 encode(buf, cast(dchar) c);
5483 if (c == terminator)
5484 break;
5486 if (ferror(fps))
5487 StdioException();
5488 return buf.length;
5490 else
5492 static assert(0);
5495 return () @trusted {
5496 import core.stdc.stdlib : free;
5498 static char *lineptr = null;
5499 static size_t n = 0;
5500 scope(exit)
5502 if (n > 128 * 1024)
5504 // Bound memory used by readln
5505 free(lineptr);
5506 lineptr = null;
5507 n = 0;
5511 const s = core.sys.posix.stdio.getdelim(&lineptr, &n, terminator, fps);
5512 if (s < 0)
5514 if (ferror(fps))
5515 StdioException();
5516 buf.length = 0; // end of file
5517 return 0;
5520 const line = lineptr[0 .. s];
5521 if (s <= buf.length)
5523 buf = buf[0 .. s];
5524 buf[] = line;
5526 else
5528 buf = line.dup;
5530 return s;
5531 }();
5533 else // version (NO_GETDELIM)
5535 import core.stdc.wchar_ : fwide;
5537 auto lf = LockedFile(fps);
5538 if (orientation == File.Orientation.wide)
5540 /* Stream is in wide characters.
5541 * Read them and convert to chars.
5543 version (Windows)
5545 buf.length = 0;
5546 for (int c; (c = lf.fgetwc()) != -1; )
5548 if ((c & ~0x7F) == 0)
5549 { buf ~= c;
5550 if (c == terminator)
5551 break;
5553 else
5555 if (c >= 0xD800 && c <= 0xDBFF)
5557 int c2 = void;
5558 if ((c2 = lf.fgetwc()) != -1 ||
5559 c2 < 0xDC00 && c2 > 0xDFFF)
5561 StdioException("unpaired UTF-16 surrogate");
5563 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5565 import std.utf : encode;
5566 encode(buf, c);
5569 if (ferror(fps))
5570 StdioException();
5571 return buf.length;
5573 else version (Posix)
5575 import std.utf : encode;
5576 buf.length = 0;
5577 for (int c; (c = lf.fgetwc()) != -1; )
5579 if ((c & ~0x7F) == 0)
5580 buf ~= cast(char) c;
5581 else
5582 encode(buf, cast(dchar) c);
5583 if (c == terminator)
5584 break;
5586 if (ferror(fps))
5587 StdioException();
5588 return buf.length;
5590 else
5592 static assert(0);
5596 // Narrow stream
5597 // First, fill the existing buffer
5598 for (size_t bufPos = 0; bufPos < buf.length; )
5600 immutable c = lf.fgetc();
5601 if (c == -1)
5603 buf.length = bufPos;
5604 goto endGame;
5606 buf[bufPos++] = cast(char) c;
5607 if (c == terminator)
5609 // No need to test for errors in file
5610 buf.length = bufPos;
5611 return bufPos;
5614 // Then, append to it
5615 for (int c; (c = lf.fgetc()) != -1; )
5617 buf ~= cast(char) c;
5618 if (c == terminator)
5620 // No need to test for errors in file
5621 return buf.length;
5625 endGame:
5626 if (ferror(fps))
5627 StdioException();
5628 return buf.length;
5632 @system unittest
5634 static import std.file;
5635 auto deleteme = testFilename();
5636 scope(exit) std.file.remove(deleteme);
5638 std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n");
5639 File f = File(deleteme, "rb");
5641 char[] ln = new char[2];
5642 f.readln(ln);
5644 assert(ln == "abcd\n");
5645 char[] t = ln[0 .. 2];
5646 t ~= 't';
5647 assert(t == "abt");
5648 // https://issues.dlang.org/show_bug.cgi?id=13856: ln stomped to "abtd"
5649 assert(ln == "abcd\n");
5651 // it can also stomp the array length
5652 ln = new char[4];
5653 f.readln(ln);
5654 assert(ln == "0123456789abcde\n");
5656 char[100] buf;
5657 ln = buf[];
5658 f.readln(ln);
5659 assert(ln == "1234\n");
5660 assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough
5663 /** Experimental network access via the File interface
5665 Opens a TCP connection to the given host and port, then returns
5666 a File struct with read and write access through the same interface
5667 as any other file (meaning writef and the byLine ranges work!).
5669 Authors:
5670 Adam D. Ruppe
5672 Bugs:
5673 Only works on Linux
5675 version (linux)
5677 File openNetwork(string host, ushort port)
5679 import core.stdc.string : memcpy;
5680 import core.sys.posix.arpa.inet : htons;
5681 import core.sys.posix.netdb : gethostbyname;
5682 import core.sys.posix.netinet.in_ : sockaddr_in;
5683 static import core.sys.posix.unistd;
5684 static import sock = core.sys.posix.sys.socket;
5685 import std.conv : to;
5686 import std.exception : enforce;
5687 import std.internal.cstring : tempCString;
5689 auto h = enforce( gethostbyname(host.tempCString()),
5690 new StdioException("gethostbyname"));
5692 int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
5693 enforce(s != -1, new StdioException("socket"));
5695 scope(failure)
5697 // want to make sure it doesn't dangle if something throws. Upon
5698 // normal exit, the File struct's reference counting takes care of
5699 // closing, so we don't need to worry about success
5700 core.sys.posix.unistd.close(s);
5703 sockaddr_in addr;
5705 addr.sin_family = sock.AF_INET;
5706 addr.sin_port = htons(port);
5707 memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
5709 enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
5710 new StdioException("Connect failed"));
5712 File f;
5713 f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
5714 return f;
5718 version (StdUnittest) private string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
5720 import std.conv : text;
5721 import std.file : deleteme;
5722 import std.path : baseName;
5724 // filename intentionally contains non-ASCII (Russian) characters for
5725 // https://issues.dlang.org/show_bug.cgi?id=7648
5726 return text(deleteme, "-детка.", baseName(file), ".", line);