d: Merge upstream dmd 47871363d, druntime, c52e28b7, phobos 99e9c1b77.
[official-gcc.git] / libphobos / src / std / stdio.d
blob1bde73d628d15d91de1640024a1e69efcd59fe26
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 $(B core.stdc.stdio). $(B core.stdc.stdio)
38 is $(D_PARAM public)ally imported when importing $(B std.stdio).
40 Source: $(PHOBOSSRC std/stdio.d)
41 Copyright: Copyright The D Language Foundation 2007-.
42 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
43 Authors: $(HTTP digitalmars.com, Walter Bright),
44 $(HTTP erdani.org, Andrei Alexandrescu),
45 Alex Rønne Petersen
47 module std.stdio;
49 import core.stdc.stddef : wchar_t;
50 public import core.stdc.stdio;
51 import std.algorithm.mutation : copy;
52 import std.meta : allSatisfy;
53 import std.range : ElementEncodingType, empty, front, isBidirectionalRange,
54 isInputRange, isSomeFiniteCharInputRange, put;
55 import std.traits : isSomeChar, isSomeString, Unqual, isPointer;
56 import std.typecons : Flag, No, Yes;
58 /++
59 If flag `KeepTerminator` is set to `KeepTerminator.yes`, then the delimiter
60 is included in the strings returned.
62 alias KeepTerminator = Flag!"keepTerminator";
64 version (CRuntime_Microsoft)
66 version = MICROSOFT_STDIO;
68 else version (CRuntime_DigitalMars)
70 // Specific to the way Digital Mars C does stdio
71 version = DIGITAL_MARS_STDIO;
73 else version (CRuntime_Glibc)
75 // Specific to the way Gnu C does stdio
76 version = GCC_IO;
78 else version (CRuntime_Bionic)
80 version = GENERIC_IO;
82 else version (CRuntime_Musl)
84 version = GENERIC_IO;
86 else version (CRuntime_UClibc)
88 // uClibc supports GCC IO
89 version = GCC_IO;
91 else version (OSX)
93 version = GENERIC_IO;
94 version = Darwin;
96 else version (iOS)
98 version = GENERIC_IO;
99 version = Darwin;
101 else version (TVOS)
103 version = GENERIC_IO;
104 version = Darwin;
106 else version (WatchOS)
108 version = GENERIC_IO;
109 version = Darwin;
111 else version (FreeBSD)
113 version = GENERIC_IO;
115 else version (NetBSD)
117 version = GENERIC_IO;
119 else version (OpenBSD)
121 version = GENERIC_IO;
123 else version (DragonFlyBSD)
125 version = GENERIC_IO;
127 else version (Solaris)
129 version = GENERIC_IO;
132 // Character type used for operating system filesystem APIs
133 version (Windows)
135 private alias FSChar = wchar;
137 else
139 private alias FSChar = char;
143 version (Windows)
145 // core.stdc.stdio.fopen expects file names to be
146 // encoded in CP_ACP on Windows instead of UTF-8.
147 /+ Waiting for druntime pull 299
149 extern (C) nothrow @nogc FILE* _wfopen(in wchar* filename, in wchar* mode);
150 extern (C) nothrow @nogc FILE* _wfreopen(in wchar* filename, in wchar* mode, FILE* fp);
152 import core.sys.windows.basetsd : HANDLE;
155 version (Posix)
157 static import core.sys.posix.stdio; // getdelim, flockfile
160 version (DIGITAL_MARS_STDIO)
162 private alias _FPUTC = _fputc_nlock;
163 private alias _FPUTWC = _fputwc_nlock;
164 private alias _FGETC = _fgetc_nlock;
165 private alias _FGETWC = _fgetwc_nlock;
166 private alias _FLOCK = __fp_lock;
167 private alias _FUNLOCK = __fp_unlock;
169 // Alias for MICROSOFT_STDIO compatibility.
170 // @@@DEPRECATED_2.107@@@
171 // Rename this back to _setmode once the deprecation phase has ended.
172 private alias __setmode = setmode;
174 // @@@DEPRECATED_2.107@@@
175 deprecated("internal alias FPUTC was unintentionally available from "
176 ~ "std.stdio and will be removed afer 2.107")
177 alias FPUTC = _fputc_nlock;
178 // @@@DEPRECATED_2.107@@@
179 deprecated("internal alias FPUTWC was unintentionally available from "
180 ~ "std.stdio and will be removed afer 2.107")
181 alias FPUTWC = _fputwc_nlock;
182 // @@@DEPRECATED_2.107@@@
183 deprecated("internal alias FGETC was unintentionally available from "
184 ~ "std.stdio and will be removed afer 2.107")
185 alias FGETC = _fgetc_nlock;
186 // @@@DEPRECATED_2.107@@@
187 deprecated("internal alias FGETWC was unintentionally available from "
188 ~ "std.stdio and will be removed afer 2.107")
189 alias FGETWC = _fgetwc_nlock;
190 // @@@DEPRECATED_2.107@@@
191 deprecated("internal alias FLOCK was unintentionally available from "
192 ~ "std.stdio and will be removed afer 2.107")
193 alias FLOCK = __fp_lock;
194 // @@@DEPRECATED_2.107@@@
195 deprecated("internal alias FUNLOCK was unintentionally available from "
196 ~ "std.stdio and will be removed afer 2.107")
197 alias FUNLOCK = __fp_unlock;
198 // @@@DEPRECATED_2.107@@@
199 deprecated("internal alias _setmode was unintentionally available from "
200 ~ "std.stdio and will be removed afer 2.107")
201 alias _setmode = setmode;
202 // @@@DEPRECATED_2.107@@@
203 deprecated("internal function _fileno was unintentionally available from "
204 ~ "std.stdio and will be removed afer 2.107")
205 int _fileno(FILE* f) { return f._file; }
207 else version (MICROSOFT_STDIO)
209 private alias _FPUTC = _fputc_nolock;
210 private alias _FPUTWC = _fputwc_nolock;
211 private alias _FGETC = _fgetc_nolock;
212 private alias _FGETWC = _fgetwc_nolock;
213 private alias _FLOCK = _lock_file;
214 private alias _FUNLOCK = _unlock_file;
216 // @@@DEPRECATED_2.107@@@
217 // Remove this once the deprecation phase for DIGITAL_MARS_STDIO has ended.
218 private alias __setmode = _setmode;
220 // @@@DEPRECATED_2.107@@@
221 deprecated("internal alias FPUTC was unintentionally available from "
222 ~ "std.stdio and will be removed afer 2.107")
223 alias FPUTC = _fputc_nolock;
224 // @@@DEPRECATED_2.107@@@
225 deprecated("internal alias FPUTWC was unintentionally available from "
226 ~ "std.stdio and will be removed afer 2.107")
227 alias FPUTWC = _fputwc_nolock;
228 // @@@DEPRECATED_2.107@@@
229 deprecated("internal alias FGETC was unintentionally available from "
230 ~ "std.stdio and will be removed afer 2.107")
231 alias FGETC = _fgetc_nolock;
232 // @@@DEPRECATED_2.107@@@
233 deprecated("internal alias FGETWC was unintentionally available from "
234 ~ "std.stdio and will be removed afer 2.107")
235 alias FGETWC = _fgetwc_nolock;
236 // @@@DEPRECATED_2.107@@@
237 deprecated("internal alias FLOCK was unintentionally available from "
238 ~ "std.stdio and will be removed afer 2.107")
239 alias FLOCK = _lock_file;
240 // @@@DEPRECATED_2.107@@@
241 deprecated("internal alias FUNLOCK was unintentionally available from "
242 ~ "std.stdio and will be removed afer 2.107")
243 alias FUNLOCK = _unlock_file;
245 else version (GCC_IO)
247 private alias _FPUTC = fputc_unlocked;
248 private alias _FPUTWC = fputwc_unlocked;
249 private alias _FGETC = fgetc_unlocked;
250 private alias _FGETWC = fgetwc_unlocked;
251 private alias _FLOCK = core.sys.posix.stdio.flockfile;
252 private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
254 // @@@DEPRECATED_2.107@@@
255 deprecated("internal alias FPUTC was unintentionally available from "
256 ~ "std.stdio and will be removed afer 2.107")
257 alias FPUTC = fputc_unlocked;
258 // @@@DEPRECATED_2.107@@@
259 deprecated("internal alias FPUTWC was unintentionally available from "
260 ~ "std.stdio and will be removed afer 2.107")
261 alias FPUTWC = fputwc_unlocked;
262 // @@@DEPRECATED_2.107@@@
263 deprecated("internal alias FGETC was unintentionally available from "
264 ~ "std.stdio and will be removed afer 2.107")
265 alias FGETC = fgetc_unlocked;
266 // @@@DEPRECATED_2.107@@@
267 deprecated("internal alias FGETWC was unintentionally available from "
268 ~ "std.stdio and will be removed afer 2.107")
269 alias FGETWC = fgetwc_unlocked;
270 // @@@DEPRECATED_2.107@@@
271 deprecated("internal alias FLOCK was unintentionally available from "
272 ~ "std.stdio and will be removed afer 2.107")
273 alias FLOCK = core.sys.posix.stdio.flockfile;
274 // @@@DEPRECATED_2.107@@@
275 deprecated("internal alias FUNLOCK was unintentionally available from "
276 ~ "std.stdio and will be removed afer 2.107")
277 alias FUNLOCK = core.sys.posix.stdio.funlockfile;
279 else version (GENERIC_IO)
281 nothrow:
282 @nogc:
284 extern (C) private
286 static import core.stdc.wchar_;
288 pragma(mangle, fputc.mangleof) int _FPUTC(int c, _iobuf* fp);
289 pragma(mangle, core.stdc.wchar_.fputwc.mangleof) int _FPUTWC(wchar_t c, _iobuf* fp);
290 pragma(mangle, fgetc.mangleof) int _FGETC(_iobuf* fp);
291 pragma(mangle, core.stdc.wchar_.fgetwc.mangleof) int _FGETWC(_iobuf* fp);
294 version (Posix)
296 private alias _FLOCK = core.sys.posix.stdio.flockfile;
297 private alias _FUNLOCK = core.sys.posix.stdio.funlockfile;
299 else
301 static assert(0, "don't know how to lock files on GENERIC_IO");
304 // @@@DEPRECATED_2.107@@@
305 deprecated("internal function fputc_unlocked was unintentionally available "
306 ~ "from std.stdio and will be removed afer 2.107")
307 extern (C) pragma(mangle, fputc.mangleof) int fputc_unlocked(int c, _iobuf* fp);
308 // @@@DEPRECATED_2.107@@@
309 deprecated("internal function fputwc_unlocked was unintentionally available "
310 ~ "from std.stdio and will be removed afer 2.107")
311 extern (C) pragma(mangle, core.stdc.wchar_.fputwc.mangleof) int fputwc_unlocked(wchar_t c, _iobuf* fp);
312 // @@@DEPRECATED_2.107@@@
313 deprecated("internal function fgetc_unlocked was unintentionally available "
314 ~ "from std.stdio and will be removed afer 2.107")
315 extern (C) pragma(mangle, fgetc.mangleof) int fgetc_unlocked(_iobuf* fp);
316 // @@@DEPRECATED_2.107@@@
317 deprecated("internal function fgetwc_unlocked was unintentionally available "
318 ~ "from std.stdio and will be removed afer 2.107")
319 extern (C) pragma(mangle, core.stdc.wchar_.fgetwc.mangleof) int fgetwc_unlocked(_iobuf* fp);
321 // @@@DEPRECATED_2.107@@@
322 deprecated("internal alias FPUTC was unintentionally available from "
323 ~ "std.stdio and will be removed afer 2.107")
324 alias FPUTC = fputc_unlocked;
325 // @@@DEPRECATED_2.107@@@
326 deprecated("internal alias FPUTWC was unintentionally available from "
327 ~ "std.stdio and will be removed afer 2.107")
328 alias FPUTWC = fputwc_unlocked;
329 // @@@DEPRECATED_2.107@@@
330 deprecated("internal alias FGETC was unintentionally available from "
331 ~ "std.stdio and will be removed afer 2.107")
332 alias FGETC = fgetc_unlocked;
333 // @@@DEPRECATED_2.107@@@
334 deprecated("internal alias FGETWC was unintentionally available from "
335 ~ "std.stdio and will be removed afer 2.107")
336 alias FGETWC = fgetwc_unlocked;
338 version (Posix)
340 // @@@DEPRECATED_2.107@@@
341 deprecated("internal alias FLOCK was unintentionally available from "
342 ~ "std.stdio and will be removed afer 2.107")
343 alias FLOCK = core.sys.posix.stdio.flockfile;
344 // @@@DEPRECATED_2.107@@@
345 deprecated("internal alias FUNLOCK was unintentionally available from "
346 ~ "std.stdio and will be removed afer 2.107")
347 alias FUNLOCK = core.sys.posix.stdio.funlockfile;
350 else
352 static assert(0, "unsupported C I/O system");
355 private extern (C) @nogc nothrow
357 pragma(mangle, _FPUTC.mangleof) int trustedFPUTC(int ch, _iobuf* h) @trusted;
359 version (DIGITAL_MARS_STDIO)
360 pragma(mangle, _FPUTWC.mangleof) int trustedFPUTWC(int ch, _iobuf* h) @trusted;
361 else
362 pragma(mangle, _FPUTWC.mangleof) int trustedFPUTWC(wchar_t ch, _iobuf* h) @trusted;
365 static if (__traits(compiles, core.sys.posix.stdio.getdelim))
367 extern(C) nothrow @nogc
369 // @@@DEPRECATED_2.104@@@
370 deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getdelim instead.")
371 ptrdiff_t getdelim(char**, size_t*, int, FILE*);
373 // @@@DEPRECATED_2.104@@@
374 // getline() always comes together with getdelim()
375 deprecated("To be removed after 2.104. Use core.sys.posix.stdio.getline instead.")
376 ptrdiff_t getline(char**, size_t*, FILE*);
380 //------------------------------------------------------------------------------
381 private struct ByRecordImpl(Fields...)
383 private:
384 import std.typecons : Tuple;
386 File file;
387 char[] line;
388 Tuple!(Fields) current;
389 string format;
391 public:
392 this(File f, string format)
394 assert(f.isOpen);
395 file = f;
396 this.format = format;
397 popFront(); // prime the range
400 /// Range primitive implementations.
401 @property bool empty()
403 return !file.isOpen;
406 /// Ditto
407 @property ref Tuple!(Fields) front()
409 return current;
412 /// Ditto
413 void popFront()
415 import std.conv : text;
416 import std.exception : enforce;
417 import std.format.read : formattedRead;
418 import std.string : chomp;
420 enforce(file.isOpen, "ByRecord: File must be open");
421 file.readln(line);
422 if (!line.length)
424 file.detach();
426 else
428 line = chomp(line);
429 formattedRead(line, format, &current);
430 enforce(line.empty, text("Leftover characters in record: `",
431 line, "'"));
436 template byRecord(Fields...)
438 auto byRecord(File f, string format)
440 return typeof(return)(f, format);
445 Encapsulates a `FILE*`. Generally D does not attempt to provide
446 thin wrappers over equivalent functions in the C standard library, but
447 manipulating `FILE*` values directly is unsafe and error-prone in
448 many ways. The `File` type ensures safe manipulation, automatic
449 file closing, and a lot of convenience.
451 The underlying `FILE*` handle is maintained in a reference-counted
452 manner, such that as soon as the last `File` variable bound to a
453 given `FILE*` goes out of scope, the underlying `FILE*` is
454 automatically closed.
456 Example:
457 ----
458 // test.d
459 import std.stdio;
461 void main(string[] args)
463 auto f = File("test.txt", "w"); // open for writing
464 f.write("Hello");
465 if (args.length > 1)
467 auto g = f; // now g and f write to the same file
468 // internal reference count is 2
469 g.write(", ", args[1]);
470 // g exits scope, reference count decreases to 1
472 f.writeln("!");
473 // f exits scope, reference count falls to zero,
474 // underlying `FILE*` is closed.
476 ----
477 $(CONSOLE
478 % rdmd test.d Jimmy
479 % cat test.txt
480 Hello, Jimmy!
481 % __
484 struct File
486 import core.atomic : atomicOp, atomicStore, atomicLoad;
487 import std.range.primitives : ElementEncodingType;
488 import std.traits : isScalarType, isArray;
489 enum Orientation { unknown, narrow, wide }
491 private struct Impl
493 FILE * handle = null; // Is null iff this Impl is closed by another File
494 shared uint refs = uint.max / 2;
495 bool isPopened; // true iff the stream has been created by popen()
496 Orientation orientation;
498 private Impl* _p;
499 private string _name;
501 package this(FILE* handle, string name, uint refs = 1, bool isPopened = false) @trusted
503 import core.stdc.stdlib : malloc;
504 import std.exception : enforce;
506 assert(!_p);
507 _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
508 initImpl(handle, name, refs, isPopened);
511 private void initImpl(FILE* handle, string name, uint refs = 1, bool isPopened = false)
513 assert(_p);
514 _p.handle = handle;
515 atomicStore(_p.refs, refs);
516 _p.isPopened = isPopened;
517 _p.orientation = Orientation.unknown;
518 _name = name;
522 Constructor taking the name of the file to open and the open mode.
524 Copying one `File` object to another results in the two `File`
525 objects referring to the same underlying file.
527 The destructor automatically closes the file as soon as no `File`
528 object refers to it anymore.
530 Params:
531 name = range or string representing the file _name
532 stdioOpenmode = range or string represting the open mode
533 (with the same semantics as in the C standard library
534 $(HTTP cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen)
535 function)
537 Throws: `ErrnoException` if the file could not be opened.
539 this(string name, scope const(char)[] stdioOpenmode = "rb") @safe
541 import std.conv : text;
542 import std.exception : errnoEnforce;
544 this(errnoEnforce(_fopen(name, stdioOpenmode),
545 text("Cannot open file `", name, "' in mode `",
546 stdioOpenmode, "'")),
547 name);
549 // MSVCRT workaround (issue 14422)
550 version (MICROSOFT_STDIO)
552 setAppendWin(stdioOpenmode);
556 /// ditto
557 this(R1, R2)(R1 name)
558 if (isSomeFiniteCharInputRange!R1)
560 import std.conv : to;
561 this(name.to!string, "rb");
564 /// ditto
565 this(R1, R2)(R1 name, R2 mode)
566 if (isSomeFiniteCharInputRange!R1 &&
567 isSomeFiniteCharInputRange!R2)
569 import std.conv : to;
570 this(name.to!string, mode.to!string);
573 @safe unittest
575 static import std.file;
576 import std.utf : byChar;
577 auto deleteme = testFilename();
578 auto f = File(deleteme.byChar, "w".byChar);
579 f.close();
580 std.file.remove(deleteme);
583 ~this() @safe
585 detach();
588 this(this) @safe nothrow
590 if (!_p) return;
591 assert(atomicLoad(_p.refs));
592 atomicOp!"+="(_p.refs, 1);
596 Assigns a file to another. The target of the assignment gets detached
597 from whatever file it was attached to, and attaches itself to the new
598 file.
600 ref File opAssign(File rhs) @safe return
602 import std.algorithm.mutation : swap;
604 swap(this, rhs);
605 return this;
608 // https://issues.dlang.org/show_bug.cgi?id=20129
609 @safe unittest
611 File[int] aa;
612 aa.require(0, File.init);
616 Detaches from the current file (throwing on failure), and then attempts to
617 _open file `name` with mode `stdioOpenmode`. The mode has the
618 same semantics as in the C standard library $(HTTP
619 cplusplus.com/reference/clibrary/cstdio/fopen.html, fopen) function.
621 Throws: `ErrnoException` in case of error.
623 void open(string name, scope const(char)[] stdioOpenmode = "rb") @trusted
625 resetFile(name, stdioOpenmode, false);
628 // https://issues.dlang.org/show_bug.cgi?id=20585
629 @system unittest
631 File f;
633 f.open("doesn't exist");
634 catch (Exception _e)
638 assert(!f.isOpen);
640 f.close(); // to check not crash here
643 private void resetFile(string name, scope const(char)[] stdioOpenmode, bool isPopened) @trusted
645 import core.stdc.stdlib : malloc;
646 import std.exception : enforce;
647 import std.conv : text;
648 import std.exception : errnoEnforce;
650 if (_p !is null)
652 detach();
655 FILE* handle;
656 version (Posix)
658 if (isPopened)
660 errnoEnforce(handle = _popen(name, stdioOpenmode),
661 "Cannot run command `"~name~"'");
663 else
665 errnoEnforce(handle = _fopen(name, stdioOpenmode),
666 text("Cannot open file `", name, "' in mode `",
667 stdioOpenmode, "'"));
670 else
672 assert(isPopened == false);
673 errnoEnforce(handle = _fopen(name, stdioOpenmode),
674 text("Cannot open file `", name, "' in mode `",
675 stdioOpenmode, "'"));
677 _p = cast(Impl*) enforce(malloc(Impl.sizeof), "Out of memory");
678 initImpl(handle, name, 1, isPopened);
679 version (MICROSOFT_STDIO)
681 setAppendWin(stdioOpenmode);
685 private void closeHandles() @trusted
687 assert(_p);
688 import std.exception : errnoEnforce;
690 version (Posix)
692 import core.sys.posix.stdio : pclose;
693 import std.format : format;
695 if (_p.isPopened)
697 auto res = pclose(_p.handle);
698 errnoEnforce(res != -1,
699 "Could not close pipe `"~_name~"'");
700 _p.handle = null;
701 return;
704 if (_p.handle)
706 auto handle = _p.handle;
707 _p.handle = null;
708 // fclose disassociates the FILE* even in case of error (issue 19751)
709 errnoEnforce(.fclose(handle) == 0,
710 "Could not close file `"~_name~"'");
714 version (MICROSOFT_STDIO)
716 private void setAppendWin(scope const(char)[] stdioOpenmode) @safe
718 bool append, update;
719 foreach (c; stdioOpenmode)
720 if (c == 'a')
721 append = true;
722 else
723 if (c == '+')
724 update = true;
725 if (append && !update)
726 seek(size);
731 Reuses the `File` object to either open a different file, or change
732 the file mode. If `name` is `null`, the mode of the currently open
733 file is changed; otherwise, a new file is opened, reusing the C
734 `FILE*`. The function has the same semantics as in the C standard
735 library $(HTTP cplusplus.com/reference/cstdio/freopen/, freopen)
736 function.
738 Note: Calling `reopen` with a `null` `name` is not implemented
739 in all C runtimes.
741 Throws: `ErrnoException` in case of error.
743 void reopen(string name, scope const(char)[] stdioOpenmode = "rb") @trusted
745 import std.conv : text;
746 import std.exception : enforce, errnoEnforce;
747 import std.internal.cstring : tempCString;
749 enforce(isOpen, "Attempting to reopen() an unopened file");
751 auto namez = (name == null ? _name : name).tempCString!FSChar();
752 auto modez = stdioOpenmode.tempCString!FSChar();
754 FILE* fd = _p.handle;
755 version (Windows)
756 fd = _wfreopen(namez, modez, fd);
757 else
758 fd = freopen(namez, modez, fd);
760 errnoEnforce(fd, name
761 ? text("Cannot reopen file `", name, "' in mode `", stdioOpenmode, "'")
762 : text("Cannot reopen file in mode `", stdioOpenmode, "'"));
764 if (name !is null)
765 _name = name;
768 @system unittest // Test changing filename
770 import std.exception : assertThrown, assertNotThrown;
771 static import std.file;
773 auto deleteme = testFilename();
774 std.file.write(deleteme, "foo");
775 scope(exit) std.file.remove(deleteme);
776 auto f = File(deleteme);
777 assert(f.readln() == "foo");
779 auto deleteme2 = testFilename();
780 std.file.write(deleteme2, "bar");
781 scope(exit) std.file.remove(deleteme2);
782 f.reopen(deleteme2);
783 assert(f.name == deleteme2);
784 assert(f.readln() == "bar");
785 f.close();
788 version (CRuntime_DigitalMars) {} else // Not implemented
789 version (CRuntime_Microsoft) {} else // Not implemented
790 @system unittest // Test changing mode
792 import std.exception : assertThrown, assertNotThrown;
793 static import std.file;
795 auto deleteme = testFilename();
796 std.file.write(deleteme, "foo");
797 scope(exit) std.file.remove(deleteme);
798 auto f = File(deleteme, "r+");
799 assert(f.readln() == "foo");
800 f.reopen(null, "w");
801 f.write("bar");
802 f.seek(0);
803 f.reopen(null, "a");
804 f.write("baz");
805 assert(f.name == deleteme);
806 f.close();
807 assert(std.file.readText(deleteme) == "barbaz");
811 Detaches from the current file (throwing on failure), and then runs a command
812 by calling the C standard library function $(HTTP
813 opengroup.org/onlinepubs/007908799/xsh/_popen.html, _popen).
815 Throws: `ErrnoException` in case of error.
817 version (Posix) void popen(string command, scope const(char)[] stdioOpenmode = "r") @safe
819 resetFile(command, stdioOpenmode ,true);
823 First calls `detach` (throwing on failure), then attempts to
824 associate the given file descriptor with the `File`, and sets the file's name to `null`.
826 The mode must be compatible with the mode of the file descriptor.
828 Throws: `ErrnoException` in case of error.
829 Params:
830 fd = File descriptor to associate with this `File`.
831 stdioOpenmode = Mode to associate with this File. The mode has the same semantics
832 semantics as in the C standard library
833 $(HTTP cplusplus.com/reference/cstdio/fopen/, fdopen) function, and must be compatible with `fd`.
835 void fdopen(int fd, scope const(char)[] stdioOpenmode = "rb") @safe
837 fdopen(fd, stdioOpenmode, null);
840 package void fdopen(int fd, scope const(char)[] stdioOpenmode, string name) @trusted
842 import std.exception : errnoEnforce;
843 import std.internal.cstring : tempCString;
845 auto modez = stdioOpenmode.tempCString();
846 detach();
848 version (DIGITAL_MARS_STDIO)
850 // This is a re-implementation of DMC's fdopen, but without the
851 // mucking with the file descriptor. POSIX standard requires the
852 // new fdopen'd file to retain the given file descriptor's
853 // position.
854 auto fp = fopen("NUL", modez);
855 errnoEnforce(fp, "Cannot open placeholder NUL stream");
856 _FLOCK(fp);
857 auto iob = cast(_iobuf*) fp;
858 .close(iob._file);
859 iob._file = fd;
860 iob._flag &= ~_IOTRAN;
861 _FUNLOCK(fp);
863 else
865 version (Windows) // MSVCRT
866 auto fp = _fdopen(fd, modez);
867 else version (Posix)
869 import core.sys.posix.stdio : fdopen;
870 auto fp = fdopen(fd, modez);
872 errnoEnforce(fp);
874 this = File(fp, name);
877 // Declare a dummy HANDLE to allow generating documentation
878 // for Windows-only methods.
879 version (StdDdoc) { version (Windows) {} else alias HANDLE = int; }
882 First calls `detach` (throwing on failure), and then attempts to
883 associate the given Windows `HANDLE` with the `File`. The mode must
884 be compatible with the access attributes of the handle. Windows only.
886 Throws: `ErrnoException` in case of error.
888 version (StdDdoc)
889 void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode);
891 version (Windows)
892 void windowsHandleOpen(HANDLE handle, scope const(char)[] stdioOpenmode)
894 import core.stdc.stdint : intptr_t;
895 import std.exception : errnoEnforce;
896 import std.format : format;
898 // Create file descriptors from the handles
899 version (DIGITAL_MARS_STDIO)
900 auto fd = _handleToFD(handle, FHND_DEVICE);
901 else // MSVCRT
903 int mode;
904 modeLoop:
905 foreach (c; stdioOpenmode)
906 switch (c)
908 case 'r': mode |= _O_RDONLY; break;
909 case '+': mode &=~_O_RDONLY; break;
910 case 'a': mode |= _O_APPEND; break;
911 case 'b': mode |= _O_BINARY; break;
912 case 't': mode |= _O_TEXT; break;
913 case ',': break modeLoop;
914 default: break;
917 auto fd = _open_osfhandle(cast(intptr_t) handle, mode);
920 errnoEnforce(fd >= 0, "Cannot open Windows HANDLE");
921 fdopen(fd, stdioOpenmode, "HANDLE(%s)".format(handle));
925 /** Returns `true` if the file is opened. */
926 @property bool isOpen() const @safe pure nothrow
928 return _p !is null && _p.handle;
932 Returns `true` if the file is at end (see $(HTTP
933 cplusplus.com/reference/clibrary/cstdio/feof.html, feof)).
935 Throws: `Exception` if the file is not opened.
937 @property bool eof() const @trusted pure
939 import std.exception : enforce;
941 enforce(_p && _p.handle, "Calling eof() against an unopened file.");
942 return .feof(cast(FILE*) _p.handle) != 0;
946 Returns the name last used to initialize this `File`, if any.
948 Some functions that create or initialize the `File` set the name field to `null`.
949 Examples include $(LREF tmpfile), $(LREF wrapFile), and $(LREF fdopen). See the
950 documentation of those functions for details.
952 Returns: The name last used to initialize this this file, or `null` otherwise.
954 @property string name() const @safe pure nothrow return
956 return _name;
960 If the file is closed or not yet opened, returns `true`. Otherwise, returns
961 $(HTTP cplusplus.com/reference/clibrary/cstdio/ferror.html, ferror) for
962 the file handle.
964 @property bool error() const @trusted pure nothrow
966 return !isOpen || .ferror(cast(FILE*) _p.handle);
969 @safe unittest
971 // https://issues.dlang.org/show_bug.cgi?id=12349
972 static import std.file;
973 auto deleteme = testFilename();
974 auto f = File(deleteme, "w");
975 scope(exit) std.file.remove(deleteme);
977 f.close();
978 assert(f.error);
982 Detaches from the underlying file. If the sole owner, calls `close`.
984 Throws: `ErrnoException` on failure if closing the file.
986 void detach() @trusted
988 import core.stdc.stdlib : free;
990 if (!_p) return;
991 scope(exit) _p = null;
993 if (atomicOp!"-="(_p.refs, 1) == 0)
995 scope(exit) free(_p);
996 closeHandles();
1000 @safe unittest
1002 static import std.file;
1004 auto deleteme = testFilename();
1005 scope(exit) std.file.remove(deleteme);
1006 auto f = File(deleteme, "w");
1008 auto f2 = f;
1009 f2.detach();
1011 assert(f._p.refs == 1);
1012 f.close();
1016 If the file was closed or not yet opened, succeeds vacuously. Otherwise
1017 closes the file (by calling $(HTTP
1018 cplusplus.com/reference/clibrary/cstdio/fclose.html, fclose)),
1019 throwing on error. Even if an exception is thrown, afterwards the $(D
1020 File) object is empty. This is different from `detach` in that it
1021 always closes the file; consequently, all other `File` objects
1022 referring to the same handle will see a closed file henceforth.
1024 Throws: `ErrnoException` on error.
1026 void close() @trusted
1028 import core.stdc.stdlib : free;
1029 import std.exception : errnoEnforce;
1031 if (!_p) return; // succeed vacuously
1032 scope(exit)
1034 if (atomicOp!"-="(_p.refs, 1) == 0)
1035 free(_p);
1036 _p = null; // start a new life
1038 if (!_p.handle) return; // Impl is closed by another File
1040 scope(exit) _p.handle = null; // nullify the handle anyway
1041 closeHandles();
1045 If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns
1046 $(HTTP cplusplus.com/reference/clibrary/cstdio/_clearerr.html,
1047 _clearerr) for the file handle.
1049 void clearerr() @safe pure nothrow
1051 _p is null || _p.handle is null ||
1052 .clearerr(_p.handle);
1056 Flushes the C `FILE` buffers.
1058 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_fflush.html, _fflush)
1059 for the file handle.
1061 Throws: `Exception` if the file is not opened or if the call to `fflush` fails.
1063 void flush() @trusted
1065 import std.exception : enforce, errnoEnforce;
1067 enforce(isOpen, "Attempting to flush() in an unopened file");
1068 errnoEnforce(.fflush(_p.handle) == 0);
1071 @safe unittest
1073 // https://issues.dlang.org/show_bug.cgi?id=12349
1074 import std.exception : assertThrown;
1075 static import std.file;
1077 auto deleteme = testFilename();
1078 auto f = File(deleteme, "w");
1079 scope(exit) std.file.remove(deleteme);
1081 f.close();
1082 assertThrown(f.flush());
1086 Forces any data buffered by the OS to be written to disk.
1087 Call $(LREF flush) before calling this function to flush the C `FILE` buffers first.
1089 This function calls
1090 $(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx,
1091 `FlushFileBuffers`) on Windows,
1092 $(HTTP developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html,
1093 `F_FULLFSYNC fcntl`) on Darwin and
1094 $(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html,
1095 `fsync`) on POSIX for the file handle.
1097 Throws: `Exception` if the file is not opened or if the OS call fails.
1099 void sync() @trusted
1101 import std.exception : enforce;
1103 enforce(isOpen, "Attempting to sync() an unopened file");
1105 version (Windows)
1107 import core.sys.windows.winbase : FlushFileBuffers;
1108 wenforce(FlushFileBuffers(windowsHandle), "FlushFileBuffers failed");
1110 else version (Darwin)
1112 import core.sys.darwin.fcntl : fcntl, F_FULLFSYNC;
1113 import std.exception : errnoEnforce;
1114 errnoEnforce(fcntl(fileno, F_FULLFSYNC, 0) != -1, "fcntl failed");
1116 else
1118 import core.sys.posix.unistd : fsync;
1119 import std.exception : errnoEnforce;
1120 errnoEnforce(fsync(fileno) == 0, "fsync failed");
1125 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fread.html, fread) for the
1126 file handle. The number of items to read and the size of
1127 each item is inferred from the size and type of the input array, respectively.
1129 Returns: The slice of `buffer` containing the data that was actually read.
1130 This will be shorter than `buffer` if EOF was reached before the buffer
1131 could be filled.
1133 Throws: `Exception` if `buffer` is empty.
1134 `ErrnoException` if the file is not opened or the call to `fread` fails.
1136 `rawRead` always reads in binary mode on Windows.
1138 T[] rawRead(T)(T[] buffer)
1140 import std.exception : enforce, errnoEnforce;
1142 if (!buffer.length)
1143 throw new Exception("rawRead must take a non-empty buffer");
1144 enforce(isOpen, "Attempting to read from an unopened file");
1145 version (Windows)
1147 immutable fd = .fileno(_p.handle);
1148 immutable mode = .__setmode(fd, _O_BINARY);
1149 scope(exit) .__setmode(fd, mode);
1150 version (DIGITAL_MARS_STDIO)
1152 import core.atomic : atomicOp;
1154 // https://issues.dlang.org/show_bug.cgi?id=4243
1155 immutable info = __fhnd_info[fd];
1156 atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
1157 scope(exit) __fhnd_info[fd] = info;
1160 immutable freadResult = trustedFread(_p.handle, buffer);
1161 assert(freadResult <= buffer.length); // fread return guarantee
1162 if (freadResult != buffer.length) // error or eof
1164 errnoEnforce(!error);
1165 return buffer[0 .. freadResult];
1167 return buffer;
1171 @system unittest
1173 static import std.file;
1175 auto testFile = std.file.deleteme();
1176 std.file.write(testFile, "\r\n\n\r\n");
1177 scope(exit) std.file.remove(testFile);
1179 auto f = File(testFile, "r");
1180 auto buf = f.rawRead(new char[5]);
1181 f.close();
1182 assert(buf == "\r\n\n\r\n");
1185 // https://issues.dlang.org/show_bug.cgi?id=21729
1186 @system unittest
1188 import std.exception : assertThrown;
1190 File f;
1191 ubyte[1] u;
1192 assertThrown(f.rawRead(u));
1195 // https://issues.dlang.org/show_bug.cgi?id=21728
1196 @system unittest
1198 static if (__traits(compiles, { import std.process : pipe; })) // not available for iOS
1200 import std.process : pipe;
1201 import std.exception : assertThrown;
1203 auto p = pipe();
1204 p.readEnd.close;
1205 ubyte[1] u;
1206 assertThrown(p.readEnd.rawRead(u));
1211 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fwrite.html, fwrite) for the file
1212 handle. The number of items to write and the size of each
1213 item is inferred from the size and type of the input array, respectively. An
1214 error is thrown if the buffer could not be written in its entirety.
1216 `rawWrite` always writes in binary mode on Windows.
1218 Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fails.
1220 void rawWrite(T)(in T[] buffer)
1222 import std.conv : text;
1223 import std.exception : errnoEnforce;
1225 version (Windows)
1227 immutable fd = .fileno(_p.handle);
1228 immutable oldMode = .__setmode(fd, _O_BINARY);
1230 if (oldMode != _O_BINARY)
1232 // need to flush the data that was written with the original mode
1233 .__setmode(fd, oldMode);
1234 flush(); // before changing translation mode .__setmode(fd, _O_BINARY);
1235 .__setmode(fd, _O_BINARY);
1238 version (DIGITAL_MARS_STDIO)
1240 import core.atomic : atomicOp;
1242 // https://issues.dlang.org/show_bug.cgi?id=4243
1243 immutable info = __fhnd_info[fd];
1244 atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
1245 scope (exit) __fhnd_info[fd] = info;
1248 scope (exit)
1250 if (oldMode != _O_BINARY)
1252 flush();
1253 .__setmode(fd, oldMode);
1258 auto result = trustedFwrite(_p.handle, buffer);
1259 if (result == result.max) result = 0;
1260 errnoEnforce(result == buffer.length,
1261 text("Wrote ", result, " instead of ", buffer.length,
1262 " objects of type ", T.stringof, " to file `",
1263 _name, "'"));
1267 @system unittest
1269 static import std.file;
1271 auto testFile = std.file.deleteme();
1272 auto f = File(testFile, "w");
1273 scope(exit) std.file.remove(testFile);
1275 f.rawWrite("\r\n\n\r\n");
1276 f.close();
1277 assert(std.file.read(testFile) == "\r\n\n\r\n");
1281 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/fseek.html, fseek)
1282 for the file handle to move its position indicator.
1284 Params:
1285 offset = Binary files: Number of bytes to offset from origin.$(BR)
1286 Text files: Either zero, or a value returned by $(LREF tell).
1287 origin = Binary files: Position used as reference for the offset, must be
1288 one of $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio),
1289 $(REF_ALTTEXT SEEK_CUR, SEEK_CUR, core,stdc,stdio) or
1290 $(REF_ALTTEXT SEEK_END, SEEK_END, core,stdc,stdio).$(BR)
1291 Text files: Shall necessarily be
1292 $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio).
1294 Throws: `Exception` if the file is not opened.
1295 `ErrnoException` if the call to `fseek` fails.
1297 void seek(long offset, int origin = SEEK_SET) @trusted
1299 import std.conv : to, text;
1300 import std.exception : enforce, errnoEnforce;
1302 // Some libc sanitize the whence input (e.g. glibc), but some don't,
1303 // e.g. Microsoft runtime crashes on an invalid origin,
1304 // and Musl additionally accept SEEK_DATA & SEEK_HOLE (Linux extension).
1305 // To provide a consistent behavior cross platform, we use the glibc check
1306 // See also https://issues.dlang.org/show_bug.cgi?id=19797
1307 enforce(origin == SEEK_SET || origin == SEEK_CUR || origin == SEEK_END,
1308 "Invalid `origin` argument passed to `seek`, must be one of: SEEK_SET, SEEK_CUR, SEEK_END");
1310 enforce(isOpen, "Attempting to seek() in an unopened file");
1311 version (Windows)
1313 version (CRuntime_Microsoft)
1315 alias fseekFun = _fseeki64;
1316 alias off_t = long;
1318 else
1320 alias fseekFun = fseek;
1321 alias off_t = int;
1324 else version (Posix)
1326 import core.sys.posix.stdio : fseeko, off_t;
1327 alias fseekFun = fseeko;
1329 errnoEnforce(fseekFun(_p.handle, to!off_t(offset), origin) == 0,
1330 "Could not seek in file `"~_name~"'");
1333 @system unittest
1335 import std.conv : text;
1336 static import std.file;
1337 import std.exception;
1339 auto deleteme = testFilename();
1340 auto f = File(deleteme, "w+");
1341 scope(exit) { f.close(); std.file.remove(deleteme); }
1342 f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1343 f.seek(7);
1344 assert(f.readln() == "hijklmnopqrstuvwxyz");
1346 version (CRuntime_DigitalMars)
1347 auto bigOffset = int.max - 100;
1348 else
1349 version (CRuntime_Bionic)
1350 auto bigOffset = int.max - 100;
1351 else
1352 auto bigOffset = cast(ulong) int.max + 100;
1353 f.seek(bigOffset);
1354 assert(f.tell == bigOffset, text(f.tell));
1355 // Uncomment the tests below only if you want to wait for
1356 // a long time
1357 // f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1358 // f.seek(-3, SEEK_END);
1359 // assert(f.readln() == "xyz");
1361 assertThrown(f.seek(0, ushort.max));
1365 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/ftell.html, ftell) for the
1366 managed file handle.
1368 Throws: `Exception` if the file is not opened.
1369 `ErrnoException` if the call to `ftell` fails.
1371 @property ulong tell() const @trusted
1373 import std.exception : enforce, errnoEnforce;
1375 enforce(isOpen, "Attempting to tell() in an unopened file");
1376 version (Windows)
1378 version (CRuntime_Microsoft)
1379 immutable result = _ftelli64(cast(FILE*) _p.handle);
1380 else
1381 immutable result = ftell(cast(FILE*) _p.handle);
1383 else version (Posix)
1385 import core.sys.posix.stdio : ftello;
1386 immutable result = ftello(cast(FILE*) _p.handle);
1388 errnoEnforce(result != -1,
1389 "Query ftell() failed for file `"~_name~"'");
1390 return result;
1394 @system unittest
1396 import std.conv : text;
1397 static import std.file;
1399 auto testFile = std.file.deleteme();
1400 std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz");
1401 scope(exit) { std.file.remove(testFile); }
1403 auto f = File(testFile);
1404 auto a = new ubyte[4];
1405 f.rawRead(a);
1406 assert(f.tell == 4, text(f.tell));
1410 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_rewind.html, _rewind)
1411 for the file handle.
1413 Throws: `Exception` if the file is not opened.
1415 void rewind() @safe
1417 import std.exception : enforce;
1419 enforce(isOpen, "Attempting to rewind() an unopened file");
1420 .rewind(_p.handle);
1424 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html, _setvbuf) for
1425 the file handle.
1427 Throws: `Exception` if the file is not opened.
1428 `ErrnoException` if the call to `setvbuf` fails.
1430 void setvbuf(size_t size, int mode = _IOFBF) @trusted
1432 import std.exception : enforce, errnoEnforce;
1434 enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1435 errnoEnforce(.setvbuf(_p.handle, null, mode, size) == 0,
1436 "Could not set buffering for file `"~_name~"'");
1440 Calls $(HTTP cplusplus.com/reference/clibrary/cstdio/_setvbuf.html,
1441 _setvbuf) for the file handle.
1443 Throws: `Exception` if the file is not opened.
1444 `ErrnoException` if the call to `setvbuf` fails.
1446 void setvbuf(void[] buf, int mode = _IOFBF) @trusted
1448 import std.exception : enforce, errnoEnforce;
1450 enforce(isOpen, "Attempting to call setvbuf() on an unopened file");
1451 errnoEnforce(.setvbuf(_p.handle,
1452 cast(char*) buf.ptr, mode, buf.length) == 0,
1453 "Could not set buffering for file `"~_name~"'");
1457 version (Windows)
1459 import core.sys.windows.winbase : OVERLAPPED;
1460 import core.sys.windows.winnt : BOOL, ULARGE_INTEGER;
1461 import std.windows.syserror : wenforce;
1463 private BOOL lockImpl(alias F, Flags...)(ulong start, ulong length,
1464 Flags flags)
1466 if (!start && !length)
1467 length = ulong.max;
1468 ULARGE_INTEGER liStart = void, liLength = void;
1469 liStart.QuadPart = start;
1470 liLength.QuadPart = length;
1471 OVERLAPPED overlapped;
1472 overlapped.Offset = liStart.LowPart;
1473 overlapped.OffsetHigh = liStart.HighPart;
1474 overlapped.hEvent = null;
1475 return F(windowsHandle, flags, 0, liLength.LowPart,
1476 liLength.HighPart, &overlapped);
1479 version (Posix)
1481 private int lockImpl(int operation, short l_type,
1482 ulong start, ulong length)
1484 import core.sys.posix.fcntl : fcntl, flock, off_t;
1485 import core.sys.posix.unistd : getpid;
1486 import std.conv : to;
1488 flock fl = void;
1489 fl.l_type = l_type;
1490 fl.l_whence = SEEK_SET;
1491 fl.l_start = to!off_t(start);
1492 fl.l_len = to!off_t(length);
1493 fl.l_pid = getpid();
1494 return fcntl(fileno, operation, &fl);
1499 Locks the specified file segment. If the file segment is already locked
1500 by another process, waits until the existing lock is released.
1501 If both `start` and `length` are zero, the entire file is locked.
1503 Locks created using `lock` and `tryLock` have the following properties:
1504 $(UL
1505 $(LI All locks are automatically released when the process terminates.)
1506 $(LI Locks are not inherited by child processes.)
1507 $(LI Closing a file will release all locks associated with the file. On POSIX,
1508 even locks acquired via a different `File` will be released as well.)
1509 $(LI Not all NFS implementations correctly implement file locking.)
1512 void lock(LockType lockType = LockType.readWrite,
1513 ulong start = 0, ulong length = 0)
1515 import std.exception : enforce;
1517 enforce(isOpen, "Attempting to call lock() on an unopened file");
1518 version (Posix)
1520 import core.sys.posix.fcntl : F_RDLCK, F_SETLKW, F_WRLCK;
1521 import std.exception : errnoEnforce;
1522 immutable short type = lockType == LockType.readWrite
1523 ? F_WRLCK : F_RDLCK;
1524 errnoEnforce(lockImpl(F_SETLKW, type, start, length) != -1,
1525 "Could not set lock for file `"~_name~"'");
1527 else
1528 version (Windows)
1530 import core.sys.windows.winbase : LockFileEx, LOCKFILE_EXCLUSIVE_LOCK;
1531 immutable type = lockType == LockType.readWrite ?
1532 LOCKFILE_EXCLUSIVE_LOCK : 0;
1533 wenforce(lockImpl!LockFileEx(start, length, type),
1534 "Could not set lock for file `"~_name~"'");
1536 else
1537 static assert(false);
1541 Attempts to lock the specified file segment.
1542 If both `start` and `length` are zero, the entire file is locked.
1543 Returns: `true` if the lock was successful, and `false` if the
1544 specified file segment was already locked.
1546 bool tryLock(LockType lockType = LockType.readWrite,
1547 ulong start = 0, ulong length = 0)
1549 import std.exception : enforce;
1551 enforce(isOpen, "Attempting to call tryLock() on an unopened file");
1552 version (Posix)
1554 import core.stdc.errno : EACCES, EAGAIN, errno;
1555 import core.sys.posix.fcntl : F_RDLCK, F_SETLK, F_WRLCK;
1556 import std.exception : errnoEnforce;
1557 immutable short type = lockType == LockType.readWrite
1558 ? F_WRLCK : F_RDLCK;
1559 immutable res = lockImpl(F_SETLK, type, start, length);
1560 if (res == -1 && (errno == EACCES || errno == EAGAIN))
1561 return false;
1562 errnoEnforce(res != -1, "Could not set lock for file `"~_name~"'");
1563 return true;
1565 else
1566 version (Windows)
1568 import core.sys.windows.winbase : GetLastError, LockFileEx, LOCKFILE_EXCLUSIVE_LOCK,
1569 LOCKFILE_FAIL_IMMEDIATELY;
1570 import core.sys.windows.winerror : ERROR_IO_PENDING, ERROR_LOCK_VIOLATION;
1571 immutable type = lockType == LockType.readWrite
1572 ? LOCKFILE_EXCLUSIVE_LOCK : 0;
1573 immutable res = lockImpl!LockFileEx(start, length,
1574 type | LOCKFILE_FAIL_IMMEDIATELY);
1575 if (!res && (GetLastError() == ERROR_IO_PENDING
1576 || GetLastError() == ERROR_LOCK_VIOLATION))
1577 return false;
1578 wenforce(res, "Could not set lock for file `"~_name~"'");
1579 return true;
1581 else
1582 static assert(false);
1586 Removes the lock over the specified file segment.
1588 void unlock(ulong start = 0, ulong length = 0)
1590 import std.exception : enforce;
1592 enforce(isOpen, "Attempting to call unlock() on an unopened file");
1593 version (Posix)
1595 import core.sys.posix.fcntl : F_SETLK, F_UNLCK;
1596 import std.exception : errnoEnforce;
1597 errnoEnforce(lockImpl(F_SETLK, F_UNLCK, start, length) != -1,
1598 "Could not remove lock for file `"~_name~"'");
1600 else
1601 version (Windows)
1603 import core.sys.windows.winbase : UnlockFileEx;
1604 wenforce(lockImpl!UnlockFileEx(start, length),
1605 "Could not remove lock for file `"~_name~"'");
1607 else
1608 static assert(false);
1611 version (Windows)
1612 @system unittest
1614 static import std.file;
1615 auto deleteme = testFilename();
1616 scope(exit) std.file.remove(deleteme);
1617 auto f = File(deleteme, "wb");
1618 assert(f.tryLock());
1619 auto g = File(deleteme, "wb");
1620 assert(!g.tryLock());
1621 assert(!g.tryLock(LockType.read));
1622 f.unlock();
1623 f.lock(LockType.read);
1624 assert(!g.tryLock());
1625 assert(g.tryLock(LockType.read));
1626 f.unlock();
1627 g.unlock();
1630 version (Posix)
1631 @system unittest
1633 static if (__traits(compiles, { import std.process : spawnProcess; }))
1635 static import std.file;
1636 auto deleteme = testFilename();
1637 scope(exit) std.file.remove(deleteme);
1639 // Since locks are per-process, we cannot test lock failures within
1640 // the same process. fork() is used to create a second process.
1641 static void runForked(void delegate() code)
1643 import core.sys.posix.sys.wait : waitpid;
1644 import core.sys.posix.unistd : fork, _exit;
1645 int child, status;
1646 if ((child = fork()) == 0)
1648 code();
1649 _exit(0);
1651 else
1653 assert(waitpid(child, &status, 0) != -1);
1654 assert(status == 0, "Fork crashed");
1658 auto f = File(deleteme, "w+b");
1660 runForked
1662 auto g = File(deleteme, "a+b");
1663 assert(g.tryLock());
1664 g.unlock();
1665 assert(g.tryLock(LockType.read));
1668 assert(f.tryLock());
1669 runForked
1671 auto g = File(deleteme, "a+b");
1672 assert(!g.tryLock());
1673 assert(!g.tryLock(LockType.read));
1675 f.unlock();
1677 f.lock(LockType.read);
1678 runForked
1680 auto g = File(deleteme, "a+b");
1681 assert(!g.tryLock());
1682 assert(g.tryLock(LockType.read));
1683 g.unlock();
1685 f.unlock();
1686 } // static if
1687 } // unittest
1691 Writes its arguments in text format to the file.
1693 Throws: `Exception` if the file is not opened.
1694 `ErrnoException` on an error writing to the file.
1696 void write(S...)(S args)
1698 import std.traits : isBoolean, isIntegral, isAggregateType;
1699 import std.utf : UTFException;
1700 auto w = lockingTextWriter();
1701 foreach (arg; args)
1705 alias A = typeof(arg);
1706 static if (isAggregateType!A || is(A == enum))
1708 import std.format.write : formattedWrite;
1710 formattedWrite(w, "%s", arg);
1712 else static if (isSomeString!A)
1714 put(w, arg);
1716 else static if (isIntegral!A)
1718 import std.conv : toTextRange;
1720 toTextRange(arg, w);
1722 else static if (isBoolean!A)
1724 put(w, arg ? "true" : "false");
1726 else static if (isSomeChar!A)
1728 put(w, arg);
1730 else
1732 import std.format.write : formattedWrite;
1734 // Most general case
1735 formattedWrite(w, "%s", arg);
1738 catch (UTFException e)
1740 /* Reset the writer so that it doesn't throw another
1741 UTFException on destruction. */
1742 w.highSurrogate = '\0';
1743 throw e;
1749 Writes its arguments in text format to the file, followed by a newline.
1751 Throws: `Exception` if the file is not opened.
1752 `ErrnoException` on an error writing to the file.
1754 void writeln(S...)(S args)
1756 write(args, '\n');
1760 Writes its arguments in text format to the file, according to the
1761 format string fmt.
1763 Params:
1764 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
1765 When passed as a compile-time argument, the string will be statically checked
1766 against the argument types passed.
1767 args = Items to write.
1769 Throws: `Exception` if the file is not opened.
1770 `ErrnoException` on an error writing to the file.
1772 void writef(alias fmt, A...)(A args)
1773 if (isSomeString!(typeof(fmt)))
1775 import std.format : checkFormatException;
1777 alias e = checkFormatException!(fmt, A);
1778 static assert(!e, e);
1779 return this.writef(fmt, args);
1782 /// ditto
1783 void writef(Char, A...)(in Char[] fmt, A args)
1785 import std.format.write : formattedWrite;
1787 formattedWrite(lockingTextWriter(), fmt, args);
1790 /// Equivalent to `file.writef(fmt, args, '\n')`.
1791 void writefln(alias fmt, A...)(A args)
1792 if (isSomeString!(typeof(fmt)))
1794 import std.format : checkFormatException;
1796 alias e = checkFormatException!(fmt, A);
1797 static assert(!e, e);
1798 return this.writefln(fmt, args);
1801 /// ditto
1802 void writefln(Char, A...)(in Char[] fmt, A args)
1804 import std.format.write : formattedWrite;
1806 auto w = lockingTextWriter();
1807 formattedWrite(w, fmt, args);
1808 w.put('\n');
1812 Read line from the file handle and return it as a specified type.
1814 This version manages its own read buffer, which means one memory allocation per call. If you are not
1815 retaining a reference to the read data, consider the `File.readln(buf)` version, which may offer
1816 better performance as it can reuse its read buffer.
1818 Params:
1819 S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
1820 terminator = Line terminator (by default, `'\n'`).
1822 Note:
1823 String terminators are not supported due to ambiguity with readln(buf) below.
1825 Returns:
1826 The line that was read, including the line terminator character.
1828 Throws:
1829 `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
1831 Example:
1833 // Reads `stdin` and writes it to `stdout`.
1834 import std.stdio;
1836 void main()
1838 string line;
1839 while ((line = stdin.readln()) !is null)
1840 write(line);
1844 S readln(S = string)(dchar terminator = '\n')
1845 if (isSomeString!S)
1847 Unqual!(ElementEncodingType!S)[] buf;
1848 readln(buf, terminator);
1849 return cast(S) buf;
1852 @system unittest
1854 import std.algorithm.comparison : equal;
1855 static import std.file;
1856 import std.meta : AliasSeq;
1858 auto deleteme = testFilename();
1859 std.file.write(deleteme, "hello\nworld\n");
1860 scope(exit) std.file.remove(deleteme);
1861 static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
1863 auto witness = [ "hello\n", "world\n" ];
1864 auto f = File(deleteme);
1865 uint i = 0;
1866 String buf;
1867 while ((buf = f.readln!String()).length)
1869 assert(i < witness.length);
1870 assert(equal(buf, witness[i++]));
1872 assert(i == witness.length);
1876 @system unittest
1878 static import std.file;
1879 import std.typecons : Tuple;
1881 auto deleteme = testFilename();
1882 std.file.write(deleteme, "cześć \U0002000D");
1883 scope(exit) std.file.remove(deleteme);
1884 uint[] lengths = [12,8,7];
1885 static foreach (uint i, C; Tuple!(char, wchar, dchar).Types)
1887 immutable(C)[] witness = "cześć \U0002000D";
1888 auto buf = File(deleteme).readln!(immutable(C)[])();
1889 assert(buf.length == lengths[i]);
1890 assert(buf == witness);
1895 Read line from the file handle and write it to `buf[]`, including
1896 terminating character.
1898 This can be faster than $(D line = File.readln()) because you can reuse
1899 the buffer for each call. Note that reusing the buffer means that you
1900 must copy the previous contents if you wish to retain them.
1902 Params:
1903 buf = Buffer used to store the resulting line data. buf is
1904 enlarged if necessary, then set to the slice exactly containing the line.
1905 terminator = Line terminator (by default, `'\n'`). Use
1906 $(REF newline, std,ascii) for portability (unless the file was opened in
1907 text mode).
1909 Returns:
1910 0 for end of file, otherwise number of characters read.
1911 The return value will always be equal to `buf.length`.
1913 Throws: `StdioException` on I/O error, or `UnicodeException` on Unicode
1914 conversion error.
1916 Example:
1918 // Read lines from `stdin` into a string
1919 // Ignore lines starting with '#'
1920 // Write the string to `stdout`
1921 import std.stdio;
1923 void main()
1925 string output;
1926 char[] buf;
1928 while (stdin.readln(buf))
1930 if (buf[0] == '#')
1931 continue;
1933 output ~= buf;
1936 write(output);
1940 This method can be more efficient than the one in the previous example
1941 because `stdin.readln(buf)` reuses (if possible) memory allocated
1942 for `buf`, whereas $(D line = stdin.readln()) makes a new memory allocation
1943 for every line.
1945 For even better performance you can help `readln` by passing in a
1946 large buffer to avoid memory reallocations. This can be done by reusing the
1947 largest buffer returned by `readln`:
1949 Example:
1951 // Read lines from `stdin` and count words
1952 import std.array, std.stdio;
1954 void main()
1956 char[] buf;
1957 size_t words = 0;
1959 while (!stdin.eof)
1961 char[] line = buf;
1962 stdin.readln(line);
1963 if (line.length > buf.length)
1964 buf = line;
1966 words += line.split.length;
1969 writeln(words);
1972 This is actually what $(LREF byLine) does internally, so its usage
1973 is recommended if you want to process a complete file.
1975 size_t readln(C)(ref C[] buf, dchar terminator = '\n')
1976 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
1978 import std.exception : enforce;
1980 static if (is(C == char))
1982 enforce(_p && _p.handle, "Attempt to read from an unopened file.");
1983 if (_p.orientation == Orientation.unknown)
1985 import core.stdc.wchar_ : fwide;
1986 auto w = fwide(_p.handle, 0);
1987 if (w < 0) _p.orientation = Orientation.narrow;
1988 else if (w > 0) _p.orientation = Orientation.wide;
1990 return readlnImpl(_p.handle, buf, terminator, _p.orientation);
1992 else
1994 string s = readln(terminator);
1995 if (!s.length)
1997 buf = buf[0 .. 0];
1998 return 0;
2001 import std.utf : codeLength;
2002 buf.length = codeLength!C(s);
2003 size_t idx;
2004 foreach (C c; s)
2005 buf[idx++] = c;
2007 return buf.length;
2011 @system unittest
2013 // @system due to readln
2014 static import std.file;
2015 auto deleteme = testFilename();
2016 std.file.write(deleteme, "123\n456789");
2017 scope(exit) std.file.remove(deleteme);
2019 auto file = File(deleteme);
2020 char[] buffer = new char[10];
2021 char[] line = buffer;
2022 file.readln(line);
2023 auto beyond = line.length;
2024 buffer[beyond] = 'a';
2025 file.readln(line); // should not write buffer beyond line
2026 assert(buffer[beyond] == 'a');
2029 // https://issues.dlang.org/show_bug.cgi?id=15293
2030 @system unittest
2032 // @system due to readln
2033 static import std.file;
2034 auto deleteme = testFilename();
2035 std.file.write(deleteme, "a\n\naa");
2036 scope(exit) std.file.remove(deleteme);
2038 auto file = File(deleteme);
2039 char[] buffer;
2040 char[] line;
2042 file.readln(buffer, '\n');
2044 line = buffer;
2045 file.readln(line, '\n');
2047 line = buffer;
2048 file.readln(line, '\n');
2050 assert(line[0 .. 1].capacity == 0);
2053 /** ditto */
2054 size_t readln(C, R)(ref C[] buf, R terminator)
2055 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
2056 isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
2058 import std.algorithm.mutation : swap;
2059 import std.algorithm.searching : endsWith;
2060 import std.range.primitives : back;
2062 auto last = terminator.back;
2063 C[] buf2;
2064 swap(buf, buf2);
2065 for (;;)
2067 if (!readln(buf2, last) || endsWith(buf2, terminator))
2069 if (buf.empty)
2071 buf = buf2;
2073 else
2075 buf ~= buf2;
2077 break;
2079 buf ~= buf2;
2081 return buf.length;
2084 @system unittest
2086 static import std.file;
2087 import std.typecons : Tuple;
2089 auto deleteme = testFilename();
2090 std.file.write(deleteme, "hello\n\rworld\nhow\n\rare ya");
2091 scope(exit) std.file.remove(deleteme);
2092 foreach (C; Tuple!(char, wchar, dchar).Types)
2094 immutable(C)[][] witness = [ "hello\n\r", "world\nhow\n\r", "are ya" ];
2095 auto f = File(deleteme);
2096 uint i = 0;
2097 C[] buf;
2098 while (f.readln(buf, "\n\r"))
2100 assert(i < witness.length);
2101 assert(buf == witness[i++]);
2103 assert(buf.length == 0);
2108 * Reads formatted _data from the file using $(REF formattedRead, std,_format).
2109 * Params:
2110 * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
2111 * When passed as a compile-time argument, the string will be statically checked
2112 * against the argument types passed.
2113 * data = Items to be read.
2114 * Returns:
2115 * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
2116 * this number will be less than the number of variables provided.
2117 * Example:
2118 ----
2119 // test.d
2120 void main()
2122 import std.stdio;
2123 auto f = File("input");
2124 foreach (_; 0 .. 3)
2126 int a;
2127 f.readf!" %d"(a);
2128 writeln(++a);
2131 ----
2132 $(CONSOLE
2133 % echo "1 2 3" > input
2134 % rdmd test.d
2140 uint readf(alias format, Data...)(auto ref Data data)
2141 if (isSomeString!(typeof(format)))
2143 import std.format : checkFormatException;
2145 alias e = checkFormatException!(format, Data);
2146 static assert(!e, e);
2147 return this.readf(format, data);
2150 /// ditto
2151 uint readf(Data...)(scope const(char)[] format, auto ref Data data)
2153 import std.format.read : formattedRead;
2155 assert(isOpen);
2156 auto input = LockingTextReader(this);
2157 return formattedRead(input, format, data);
2161 @system unittest
2163 static import std.file;
2165 auto deleteme = std.file.deleteme();
2166 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2167 scope(exit) std.file.remove(deleteme);
2168 string s;
2169 auto f = File(deleteme);
2170 f.readf!"%s\n"(s);
2171 assert(s == "hello", "["~s~"]");
2172 f.readf("%s\n", s);
2173 assert(s == "world", "["~s~"]");
2175 bool b1, b2;
2176 f.readf("%s\n%s\n", b1, b2);
2177 assert(b1 == true && b2 == false);
2180 // backwards compatibility with pointers
2181 @system unittest
2183 // @system due to readf
2184 static import std.file;
2186 auto deleteme = testFilename();
2187 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2188 scope(exit) std.file.remove(deleteme);
2189 string s;
2190 auto f = File(deleteme);
2191 f.readf("%s\n", &s);
2192 assert(s == "hello", "["~s~"]");
2193 f.readf("%s\n", &s);
2194 assert(s == "world", "["~s~"]");
2196 // https://issues.dlang.org/show_bug.cgi?id=11698
2197 bool b1, b2;
2198 f.readf("%s\n%s\n", &b1, &b2);
2199 assert(b1 == true && b2 == false);
2202 // backwards compatibility (mixed)
2203 @system unittest
2205 // @system due to readf
2206 static import std.file;
2208 auto deleteme = testFilename();
2209 std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
2210 scope(exit) std.file.remove(deleteme);
2211 string s1, s2;
2212 auto f = File(deleteme);
2213 f.readf("%s\n%s\n", s1, &s2);
2214 assert(s1 == "hello");
2215 assert(s2 == "world");
2217 // https://issues.dlang.org/show_bug.cgi?id=11698
2218 bool b1, b2;
2219 f.readf("%s\n%s\n", &b1, b2);
2220 assert(b1 == true && b2 == false);
2223 // Nice error of std.stdio.readf with newlines
2224 // https://issues.dlang.org/show_bug.cgi?id=12260
2225 @system unittest
2227 static import std.file;
2229 auto deleteme = testFilename();
2230 std.file.write(deleteme, "1\n2");
2231 scope(exit) std.file.remove(deleteme);
2232 int input;
2233 auto f = File(deleteme);
2234 f.readf("%s", &input);
2236 import std.conv : ConvException;
2237 import std.exception : collectException;
2238 assert(collectException!ConvException(f.readf("%s", &input)).msg ==
2239 "Unexpected '\\n' when converting from type LockingTextReader to type int");
2243 Returns a temporary file by calling
2244 $(HTTP cplusplus.com/reference/clibrary/cstdio/_tmpfile.html, _tmpfile).
2245 Note that the created file has no $(LREF name).*/
2246 static File tmpfile() @safe
2248 import std.exception : errnoEnforce;
2250 return File(errnoEnforce(.tmpfile(),
2251 "Could not create temporary file with tmpfile()"),
2252 null);
2256 Unsafe function that wraps an existing `FILE*`. The resulting $(D
2257 File) never takes the initiative in closing the file.
2258 Note that the created file has no $(LREF name)*/
2259 /*private*/ static File wrapFile(FILE* f) @safe
2261 import std.exception : enforce;
2263 return File(enforce(f, "Could not wrap null FILE*"),
2264 null, /*uint.max / 2*/ 9999);
2268 Returns the `FILE*` corresponding to this object.
2270 FILE* getFP() @safe pure
2272 import std.exception : enforce;
2274 enforce(_p && _p.handle,
2275 "Attempting to call getFP() on an unopened file");
2276 return _p.handle;
2279 @system unittest
2281 static import core.stdc.stdio;
2282 assert(stdout.getFP() == core.stdc.stdio.stdout);
2286 Returns the file number corresponding to this object.
2288 @property int fileno() const @trusted
2290 import std.exception : enforce;
2292 enforce(isOpen, "Attempting to call fileno() on an unopened file");
2293 return .fileno(cast(FILE*) _p.handle);
2297 Returns the underlying operating system `HANDLE` (Windows only).
2299 version (StdDdoc)
2300 @property HANDLE windowsHandle();
2302 version (Windows)
2303 @property HANDLE windowsHandle()
2305 version (DIGITAL_MARS_STDIO)
2306 return _fdToHandle(fileno);
2307 else
2308 return cast(HANDLE)_get_osfhandle(fileno);
2312 // Note: This was documented until 2013/08
2314 Range that reads one line at a time. Returned by $(LREF byLine).
2316 Allows to directly use range operations on lines of a file.
2318 private struct ByLineImpl(Char, Terminator)
2320 private:
2321 import std.typecons : RefCounted, RefCountedAutoInitialize;
2323 /* Ref-counting stops the source range's Impl
2324 * from getting out of sync after the range is copied, e.g.
2325 * when accessing range.front, then using std.range.take,
2326 * then accessing range.front again. */
2327 alias PImpl = RefCounted!(Impl, RefCountedAutoInitialize.no);
2328 PImpl impl;
2330 static if (isScalarType!Terminator)
2331 enum defTerm = '\n';
2332 else
2333 enum defTerm = cast(Terminator)"\n";
2335 public:
2336 this(File f, KeepTerminator kt = No.keepTerminator,
2337 Terminator terminator = defTerm)
2339 impl = PImpl(f, kt, terminator);
2342 @property bool empty()
2344 return impl.refCountedPayload.empty;
2347 @property Char[] front()
2349 return impl.refCountedPayload.front;
2352 void popFront()
2354 impl.refCountedPayload.popFront();
2357 private:
2358 struct Impl
2360 private:
2361 File file;
2362 Char[] line;
2363 Char[] buffer;
2364 Terminator terminator;
2365 KeepTerminator keepTerminator;
2366 bool haveLine;
2368 public:
2369 this(File f, KeepTerminator kt, Terminator terminator)
2371 file = f;
2372 this.terminator = terminator;
2373 keepTerminator = kt;
2376 // Range primitive implementations.
2377 @property bool empty()
2379 needLine();
2380 return line is null;
2383 @property Char[] front()
2385 needLine();
2386 return line;
2389 void popFront()
2391 needLine();
2392 haveLine = false;
2395 private:
2396 void needLine()
2398 if (haveLine)
2399 return;
2400 import std.algorithm.searching : endsWith;
2401 assert(file.isOpen);
2402 line = buffer;
2403 file.readln(line, terminator);
2404 if (line.length > buffer.length)
2406 buffer = line;
2408 if (line.empty)
2410 file.detach();
2411 line = null;
2413 else if (keepTerminator == No.keepTerminator
2414 && endsWith(line, terminator))
2416 static if (isScalarType!Terminator)
2417 enum tlen = 1;
2418 else static if (isArray!Terminator)
2420 static assert(
2421 is(immutable ElementEncodingType!Terminator == immutable Char));
2422 const tlen = terminator.length;
2424 else
2425 static assert(false);
2426 line = line[0 .. line.length - tlen];
2428 haveLine = true;
2434 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2435 set up to read from the file handle one line at a time.
2437 The element type for the range will be `Char[]`. Range primitives
2438 may throw `StdioException` on I/O error.
2440 Note:
2441 Each `front` will not persist after $(D
2442 popFront) is called, so the caller must copy its contents (e.g. by
2443 calling `to!string`) when retention is needed. If the caller needs
2444 to retain a copy of every line, use the $(LREF byLineCopy) function
2445 instead.
2447 Params:
2448 Char = Character type for each line, defaulting to `char`.
2449 keepTerminator = Use `Yes.keepTerminator` to include the
2450 terminator at the end of each line.
2451 terminator = Line separator (`'\n'` by default). Use
2452 $(REF newline, std,ascii) for portability (unless the file was opened in
2453 text mode).
2455 Example:
2456 ----
2457 import std.algorithm, std.stdio, std.string;
2458 // Count words in a file using ranges.
2459 void main()
2461 auto file = File("file.txt"); // Open for reading
2462 const wordCount = file.byLine() // Read lines
2463 .map!split // Split into words
2464 .map!(a => a.length) // Count words per line
2465 .sum(); // Total word count
2466 writeln(wordCount);
2468 ----
2470 Example:
2471 ----
2472 import std.range, std.stdio;
2473 // Read lines using foreach.
2474 void main()
2476 auto file = File("file.txt"); // Open for reading
2477 auto range = file.byLine();
2478 // Print first three lines
2479 foreach (line; range.take(3))
2480 writeln(line);
2481 // Print remaining lines beginning with '#'
2482 foreach (line; range)
2484 if (!line.empty && line[0] == '#')
2485 writeln(line);
2488 ----
2489 Notice that neither example accesses the line data returned by
2490 `front` after the corresponding `popFront` call is made (because
2491 the contents may well have changed).
2493 auto byLine(Terminator = char, Char = char)
2494 (KeepTerminator keepTerminator = No.keepTerminator,
2495 Terminator terminator = '\n')
2496 if (isScalarType!Terminator)
2498 return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
2501 /// ditto
2502 auto byLine(Terminator, Char = char)
2503 (KeepTerminator keepTerminator, Terminator terminator)
2504 if (is(immutable ElementEncodingType!Terminator == immutable Char))
2506 return ByLineImpl!(Char, Terminator)(this, keepTerminator, terminator);
2509 @system unittest
2511 static import std.file;
2512 auto deleteme = testFilename();
2513 std.file.write(deleteme, "hi");
2514 scope(success) std.file.remove(deleteme);
2516 import std.meta : AliasSeq;
2517 static foreach (T; AliasSeq!(char, wchar, dchar))
2519 auto blc = File(deleteme).byLine!(T, T);
2520 assert(blc.front == "hi");
2521 // check front is cached
2522 assert(blc.front is blc.front);
2526 // https://issues.dlang.org/show_bug.cgi?id=19980
2527 @system unittest
2529 static import std.file;
2530 auto deleteme = testFilename();
2531 std.file.write(deleteme, "Line 1\nLine 2\nLine 3\n");
2532 scope(success) std.file.remove(deleteme);
2534 auto f = File(deleteme);
2535 f.byLine();
2536 f.byLine();
2537 assert(f.byLine().front == "Line 1");
2540 private struct ByLineCopy(Char, Terminator)
2542 private:
2543 import std.typecons : RefCounted, RefCountedAutoInitialize;
2545 /* Ref-counting stops the source range's ByLineCopyImpl
2546 * from getting out of sync after the range is copied, e.g.
2547 * when accessing range.front, then using std.range.take,
2548 * then accessing range.front again. */
2549 alias Impl = RefCounted!(ByLineCopyImpl!(Char, Terminator),
2550 RefCountedAutoInitialize.no);
2551 Impl impl;
2553 public:
2554 this(File f, KeepTerminator kt, Terminator terminator)
2556 impl = Impl(f, kt, terminator);
2559 @property bool empty()
2561 return impl.refCountedPayload.empty;
2564 @property Char[] front()
2566 return impl.refCountedPayload.front;
2569 void popFront()
2571 impl.refCountedPayload.popFront();
2575 private struct ByLineCopyImpl(Char, Terminator)
2577 ByLineImpl!(Unqual!Char, Terminator).Impl impl;
2578 bool gotFront;
2579 Char[] line;
2581 public:
2582 this(File f, KeepTerminator kt, Terminator terminator)
2584 impl = ByLineImpl!(Unqual!Char, Terminator).Impl(f, kt, terminator);
2587 @property bool empty()
2589 return impl.empty;
2592 @property front()
2594 if (!gotFront)
2596 line = impl.front.dup;
2597 gotFront = true;
2599 return line;
2602 void popFront()
2604 impl.popFront();
2605 gotFront = false;
2610 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2611 set up to read from the file handle one line
2612 at a time. Each line will be newly allocated. `front` will cache
2613 its value to allow repeated calls without unnecessary allocations.
2615 Note: Due to caching byLineCopy can be more memory-efficient than
2616 `File.byLine.map!idup`.
2618 The element type for the range will be `Char[]`. Range
2619 primitives may throw `StdioException` on I/O error.
2621 Params:
2622 Char = Character type for each line, defaulting to $(D immutable char).
2623 keepTerminator = Use `Yes.keepTerminator` to include the
2624 terminator at the end of each line.
2625 terminator = Line separator (`'\n'` by default). Use
2626 $(REF newline, std,ascii) for portability (unless the file was opened in
2627 text mode).
2629 Example:
2630 ----
2631 import std.algorithm, std.array, std.stdio;
2632 // Print sorted lines of a file.
2633 void main()
2635 auto sortedLines = File("file.txt") // Open for reading
2636 .byLineCopy() // Read persistent lines
2637 .array() // into an array
2638 .sort(); // then sort them
2639 foreach (line; sortedLines)
2640 writeln(line);
2642 ----
2643 See_Also:
2644 $(REF readText, std,file)
2646 auto byLineCopy(Terminator = char, Char = immutable char)
2647 (KeepTerminator keepTerminator = No.keepTerminator,
2648 Terminator terminator = '\n')
2649 if (isScalarType!Terminator)
2651 return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2654 /// ditto
2655 auto byLineCopy(Terminator, Char = immutable char)
2656 (KeepTerminator keepTerminator, Terminator terminator)
2657 if (is(immutable ElementEncodingType!Terminator == immutable Char))
2659 return ByLineCopy!(Char, Terminator)(this, keepTerminator, terminator);
2662 @safe unittest
2664 static assert(is(typeof(File("").byLine.front) == char[]));
2665 static assert(is(typeof(File("").byLineCopy.front) == string));
2666 static assert(
2667 is(typeof(File("").byLineCopy!(char, char).front) == char[]));
2670 @system unittest
2672 import std.algorithm.comparison : equal;
2673 static import std.file;
2675 scope(failure) printf("Failed test at line %d\n", __LINE__);
2676 auto deleteme = testFilename();
2677 std.file.write(deleteme, "");
2678 scope(success) std.file.remove(deleteme);
2680 // Test empty file
2681 auto f = File(deleteme);
2682 foreach (line; f.byLine())
2684 assert(false);
2686 f.detach();
2687 assert(!f.isOpen);
2689 void test(Terminator)(string txt, in string[] witness,
2690 KeepTerminator kt, Terminator term, bool popFirstLine = false)
2692 import std.algorithm.sorting : sort;
2693 import std.array : array;
2694 import std.conv : text;
2695 import std.range.primitives : walkLength;
2697 uint i;
2698 std.file.write(deleteme, txt);
2699 auto f = File(deleteme);
2700 scope(exit)
2702 f.close();
2703 assert(!f.isOpen);
2705 auto lines = f.byLine(kt, term);
2706 if (popFirstLine)
2708 lines.popFront();
2709 i = 1;
2711 assert(lines.empty || lines.front is lines.front);
2712 foreach (line; lines)
2714 assert(line == witness[i++]);
2716 assert(i == witness.length, text(i, " != ", witness.length));
2718 // https://issues.dlang.org/show_bug.cgi?id=11830
2719 auto walkedLength = File(deleteme).byLine(kt, term).walkLength;
2720 assert(walkedLength == witness.length, text(walkedLength, " != ", witness.length));
2722 // test persistent lines
2723 assert(File(deleteme).byLineCopy(kt, term).array.sort() == witness.dup.sort());
2726 KeepTerminator kt = No.keepTerminator;
2727 test("", null, kt, '\n');
2728 test("\n", [ "" ], kt, '\n');
2729 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n');
2730 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt, '\n', true);
2731 test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt, '\n');
2732 test("foo", [ "foo" ], kt, '\n', true);
2733 test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
2734 kt, "\r\n");
2735 test("sue\r", ["sue"], kt, '\r');
2737 kt = Yes.keepTerminator;
2738 test("", null, kt, '\n');
2739 test("\n", [ "\n" ], kt, '\n');
2740 test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt, '\n');
2741 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n');
2742 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt, '\n', true);
2743 test("foo", [ "foo" ], kt, '\n');
2744 test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
2745 kt, "\r\n");
2746 test("sue\r", ["sue\r"], kt, '\r');
2749 @system unittest
2751 import std.algorithm.comparison : equal;
2752 import std.range : drop, take;
2754 version (Win64)
2756 static import std.file;
2758 /* the C function tmpfile doesn't seem to work, even when called from C */
2759 auto deleteme = testFilename();
2760 auto file = File(deleteme, "w+");
2761 scope(success) std.file.remove(deleteme);
2763 else version (CRuntime_Bionic)
2765 static import std.file;
2767 /* the C function tmpfile doesn't work when called from a shared
2768 library apk:
2769 https://code.google.com/p/android/issues/detail?id=66815 */
2770 auto deleteme = testFilename();
2771 auto file = File(deleteme, "w+");
2772 scope(success) std.file.remove(deleteme);
2774 else
2775 auto file = File.tmpfile();
2776 file.write("1\n2\n3\n");
2778 // https://issues.dlang.org/show_bug.cgi?id=9599
2779 file.rewind();
2780 File.ByLineImpl!(char, char) fbl = file.byLine();
2781 auto fbl2 = fbl;
2782 assert(fbl.front == "1");
2783 assert(fbl.front is fbl2.front);
2784 assert(fbl.take(1).equal(["1"]));
2785 assert(fbl.equal(["2", "3"]));
2786 assert(fbl.empty);
2787 assert(file.isOpen); // we still have a valid reference
2789 file.rewind();
2790 fbl = file.byLine();
2791 assert(!fbl.drop(2).empty);
2792 assert(fbl.equal(["3"]));
2793 assert(fbl.empty);
2794 assert(file.isOpen);
2796 file.detach();
2797 assert(!file.isOpen);
2800 @system unittest
2802 static import std.file;
2803 auto deleteme = testFilename();
2804 std.file.write(deleteme, "hi");
2805 scope(success) std.file.remove(deleteme);
2807 auto blc = File(deleteme).byLineCopy;
2808 assert(!blc.empty);
2809 // check front is cached
2810 assert(blc.front is blc.front);
2814 Creates an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2815 set up to parse one line at a time from the file into a tuple.
2817 Range primitives may throw `StdioException` on I/O error.
2819 Params:
2820 format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
2822 Returns:
2823 The input range set up to parse one line at a time into a record tuple.
2825 See_Also:
2827 It is similar to $(LREF byLine) and uses
2828 $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood.
2830 template byRecord(Fields...)
2832 auto byRecord(string format)
2834 return ByRecordImpl!(Fields)(this, format);
2839 @system unittest
2841 static import std.file;
2842 import std.typecons : tuple;
2844 // prepare test file
2845 auto testFile = std.file.deleteme();
2846 scope(failure) printf("Failed test at line %d\n", __LINE__);
2847 std.file.write(testFile, "1 2\n4 1\n5 100");
2848 scope(exit) std.file.remove(testFile);
2850 File f = File(testFile);
2851 scope(exit) f.close();
2853 auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
2854 uint i;
2855 foreach (e; f.byRecord!(int, int)("%s %s"))
2857 assert(e == expected[i++]);
2861 // Note: This was documented until 2013/08
2863 * Range that reads a chunk at a time.
2865 private struct ByChunkImpl
2867 private:
2868 File file_;
2869 ubyte[] chunk_;
2871 void prime()
2873 chunk_ = file_.rawRead(chunk_);
2874 if (chunk_.length == 0)
2875 file_.detach();
2878 public:
2879 this(File file, size_t size)
2881 this(file, new ubyte[](size));
2884 this(File file, ubyte[] buffer)
2886 import std.exception : enforce;
2887 enforce(buffer.length, "size must be larger than 0");
2888 file_ = file;
2889 chunk_ = buffer;
2890 prime();
2893 // `ByChunk`'s input range primitive operations.
2894 @property nothrow
2895 bool empty() const
2897 return !file_.isOpen;
2900 /// Ditto
2901 @property nothrow
2902 ubyte[] front()
2904 version (assert)
2906 import core.exception : RangeError;
2907 if (empty)
2908 throw new RangeError();
2910 return chunk_;
2913 /// Ditto
2914 void popFront()
2916 version (assert)
2918 import core.exception : RangeError;
2919 if (empty)
2920 throw new RangeError();
2922 prime();
2927 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2928 set up to read from the file handle a chunk at a time.
2930 The element type for the range will be `ubyte[]`. Range primitives
2931 may throw `StdioException` on I/O error.
2933 Example:
2934 ---------
2935 void main()
2937 // Read standard input 4KB at a time
2938 foreach (ubyte[] buffer; stdin.byChunk(4096))
2940 ... use buffer ...
2943 ---------
2945 The parameter may be a number (as shown in the example above) dictating the
2946 size of each chunk. Alternatively, `byChunk` accepts a
2947 user-provided buffer that it uses directly.
2949 Example:
2950 ---------
2951 void main()
2953 // Read standard input 4KB at a time
2954 foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
2956 ... use buffer ...
2959 ---------
2961 In either case, the content of the buffer is reused across calls. That means
2962 `front` will not persist after `popFront` is called, so if retention is
2963 needed, the caller must copy its contents (e.g. by calling `buffer.dup`).
2965 In the example above, `buffer.length` is 4096 for all iterations, except
2966 for the last one, in which case `buffer.length` may be less than 4096 (but
2967 always greater than zero).
2969 With the mentioned limitations, `byChunk` works with any algorithm
2970 compatible with input ranges.
2972 Example:
2974 // Efficient file copy, 1MB at a time.
2975 import std.algorithm, std.stdio;
2976 void main()
2978 stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
2982 $(REF joiner, std,algorithm,iteration) can be used to join chunks together into
2983 a single range lazily.
2984 Example:
2986 import std.algorithm, std.stdio;
2987 void main()
2989 //Range of ranges
2990 static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
2991 //Range of elements
2992 static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
2996 Returns: A call to `byChunk` returns a range initialized with the `File`
2997 object and the appropriate buffer.
2999 Throws: If the user-provided size is zero or the user-provided buffer
3000 is empty, throws an `Exception`. In case of an I/O error throws
3001 `StdioException`.
3003 auto byChunk(size_t chunkSize)
3005 return ByChunkImpl(this, chunkSize);
3007 /// Ditto
3008 auto byChunk(ubyte[] buffer)
3010 return ByChunkImpl(this, buffer);
3013 @system unittest
3015 static import std.file;
3017 scope(failure) printf("Failed test at line %d\n", __LINE__);
3019 auto deleteme = testFilename();
3020 std.file.write(deleteme, "asd\ndef\nasdf");
3022 auto witness = ["asd\n", "def\n", "asdf" ];
3023 auto f = File(deleteme);
3024 scope(exit)
3026 f.close();
3027 assert(!f.isOpen);
3028 std.file.remove(deleteme);
3031 uint i;
3032 foreach (chunk; f.byChunk(4))
3033 assert(chunk == cast(ubyte[]) witness[i++]);
3035 assert(i == witness.length);
3038 @system unittest
3040 static import std.file;
3042 scope(failure) printf("Failed test at line %d\n", __LINE__);
3044 auto deleteme = testFilename();
3045 std.file.write(deleteme, "asd\ndef\nasdf");
3047 auto witness = ["asd\n", "def\n", "asdf" ];
3048 auto f = File(deleteme);
3049 scope(exit)
3051 f.close();
3052 assert(!f.isOpen);
3053 std.file.remove(deleteme);
3056 uint i;
3057 foreach (chunk; f.byChunk(new ubyte[4]))
3058 assert(chunk == cast(ubyte[]) witness[i++]);
3060 assert(i == witness.length);
3063 // Note: This was documented until 2013/08
3065 `Range` that locks the file and allows fast writing to it.
3067 struct LockingTextWriter
3069 private:
3070 import std.range.primitives : ElementType, isInfinite, isInputRange;
3071 // Access the FILE* handle through the 'file_' member
3072 // to keep the object alive through refcounting
3073 File file_;
3075 // the unshared version of FILE* handle, extracted from the File object
3076 @property _iobuf* handle_() @trusted { return cast(_iobuf*) file_._p.handle; }
3078 // the file's orientation (byte- or wide-oriented)
3079 int orientation_;
3081 // Buffers for when we need to transcode.
3082 wchar highSurrogate = '\0'; // '\0' indicates empty
3083 void highSurrogateShouldBeEmpty() @safe
3085 import std.utf : UTFException;
3086 if (highSurrogate != '\0')
3087 throw new UTFException("unpaired surrogate UTF-16 value");
3089 char[4] rbuf8;
3090 size_t rbuf8Filled = 0;
3091 public:
3093 this(ref File f) @trusted
3095 import std.exception : enforce;
3097 enforce(f._p && f._p.handle, "Attempting to write to closed File");
3098 file_ = f;
3099 FILE* fps = f._p.handle;
3101 version (MICROSOFT_STDIO)
3103 // Microsoft doesn't implement fwide. Instead, there's the
3104 // concept of ANSI/UNICODE mode. fputc doesn't work in UNICODE
3105 // mode; fputwc has to be used. So that essentially means
3106 // "wide-oriented" for us.
3107 immutable int mode = __setmode(f.fileno, _O_TEXT);
3108 // Set some arbitrary mode to obtain the previous one.
3109 __setmode(f.fileno, mode); // Restore previous mode.
3110 if (mode & (_O_WTEXT | _O_U16TEXT | _O_U8TEXT))
3112 orientation_ = 1; // wide
3115 else
3117 import core.stdc.wchar_ : fwide;
3118 orientation_ = fwide(fps, 0);
3121 _FLOCK(fps);
3124 ~this() @trusted
3126 if (auto p = file_._p)
3128 if (p.handle) _FUNLOCK(p.handle);
3130 file_ = File.init;
3131 /* Destroy file_ before possibly throwing. Else it wouldn't be
3132 destroyed, and its reference count would be wrong. */
3133 highSurrogateShouldBeEmpty();
3136 this(this) @trusted
3138 if (auto p = file_._p)
3140 if (p.handle) _FLOCK(p.handle);
3144 /// Range primitive implementations.
3145 void put(A)(scope A writeme)
3146 if ((isSomeChar!(ElementType!A) ||
3147 is(ElementType!A : const(ubyte))) &&
3148 isInputRange!A &&
3149 !isInfinite!A)
3151 import std.exception : errnoEnforce;
3153 alias C = ElementEncodingType!A;
3154 static assert(!is(C == void));
3155 static if (isSomeString!A && C.sizeof == 1 || is(A : const(ubyte)[]))
3157 if (orientation_ <= 0)
3159 //file.write(writeme); causes infinite recursion!!!
3160 //file.rawWrite(writeme);
3161 auto result = trustedFwrite(file_._p.handle, writeme);
3162 if (result != writeme.length) errnoEnforce(0);
3163 return;
3167 // put each element in turn.
3168 foreach (c; writeme)
3170 put(c);
3174 /// ditto
3175 void put(C)(scope C c) @safe if (isSomeChar!C || is(C : const(ubyte)))
3177 import std.utf : decodeFront, encode, stride;
3179 static if (c.sizeof == 1)
3181 highSurrogateShouldBeEmpty();
3182 if (orientation_ <= 0) trustedFPUTC(c, handle_);
3183 else if (c <= 0x7F) trustedFPUTWC(c, handle_);
3184 else if (c >= 0b1100_0000) // start byte of multibyte sequence
3186 rbuf8[0] = c;
3187 rbuf8Filled = 1;
3189 else // continuation byte of multibyte sequence
3191 rbuf8[rbuf8Filled] = c;
3192 ++rbuf8Filled;
3193 if (stride(rbuf8[]) == rbuf8Filled) // sequence is complete
3195 char[] str = rbuf8[0 .. rbuf8Filled];
3196 immutable dchar d = decodeFront(str);
3197 wchar_t[4 / wchar_t.sizeof] wbuf;
3198 immutable size = encode(wbuf, d);
3199 foreach (i; 0 .. size)
3200 trustedFPUTWC(wbuf[i], handle_);
3201 rbuf8Filled = 0;
3205 else static if (c.sizeof == 2)
3207 import std.utf : decode;
3209 if (c <= 0x7F)
3211 highSurrogateShouldBeEmpty();
3212 if (orientation_ <= 0) trustedFPUTC(c, handle_);
3213 else trustedFPUTWC(c, handle_);
3215 else if (0xD800 <= c && c <= 0xDBFF) // high surrogate
3217 highSurrogateShouldBeEmpty();
3218 highSurrogate = c;
3220 else // standalone or low surrogate
3222 dchar d = c;
3223 if (highSurrogate != '\0')
3225 immutable wchar[2] rbuf = [highSurrogate, c];
3226 size_t index = 0;
3227 d = decode(rbuf[], index);
3228 highSurrogate = 0;
3230 if (orientation_ <= 0)
3232 char[4] wbuf;
3233 immutable size = encode(wbuf, d);
3234 foreach (i; 0 .. size)
3235 trustedFPUTC(wbuf[i], handle_);
3237 else
3239 wchar_t[4 / wchar_t.sizeof] wbuf;
3240 immutable size = encode(wbuf, d);
3241 foreach (i; 0 .. size)
3242 trustedFPUTWC(wbuf[i], handle_);
3244 rbuf8Filled = 0;
3247 else // 32-bit characters
3249 import std.utf : encode;
3251 highSurrogateShouldBeEmpty();
3252 if (orientation_ <= 0)
3254 if (c <= 0x7F)
3256 trustedFPUTC(c, handle_);
3258 else
3260 char[4] buf = void;
3261 immutable len = encode(buf, c);
3262 foreach (i ; 0 .. len)
3263 trustedFPUTC(buf[i], handle_);
3266 else
3268 version (Windows)
3270 import std.utf : isValidDchar;
3272 assert(isValidDchar(c));
3273 if (c <= 0xFFFF)
3275 trustedFPUTWC(cast(wchar_t) c, handle_);
3277 else
3279 trustedFPUTWC(cast(wchar_t)
3280 ((((c - 0x10000) >> 10) & 0x3FF)
3281 + 0xD800), handle_);
3282 trustedFPUTWC(cast(wchar_t)
3283 (((c - 0x10000) & 0x3FF) + 0xDC00),
3284 handle_);
3287 else version (Posix)
3289 trustedFPUTWC(cast(wchar_t) c, handle_);
3291 else
3293 static assert(0);
3301 * Output range which locks the file when created, and unlocks the file when it goes
3302 * out of scope.
3304 * Returns: An $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3305 * which accepts string types, `ubyte[]`, individual character types, and
3306 * individual `ubyte`s.
3308 * Note: Writing either arrays of `char`s or `ubyte`s is faster than
3309 * writing each character individually from a range. For large amounts of data,
3310 * writing the contents in chunks using an intermediary array can result
3311 * in a speed increase.
3313 * Throws: $(REF UTFException, std, utf) if the data given is a `char` range
3314 * and it contains malformed UTF data.
3316 * See_Also: $(LREF byChunk) for an example.
3318 auto lockingTextWriter() @safe
3320 return LockingTextWriter(this);
3323 // An output range which optionally locks the file and puts it into
3324 // binary mode (similar to rawWrite). Because it needs to restore
3325 // the file mode on destruction, it is RefCounted on Windows.
3326 struct BinaryWriterImpl(bool locking)
3328 import std.traits : hasIndirections;
3329 private:
3330 // Access the FILE* handle through the 'file_' member
3331 // to keep the object alive through refcounting
3332 File file_;
3333 string name;
3335 version (Windows)
3337 int fd, oldMode;
3338 version (DIGITAL_MARS_STDIO)
3339 ubyte oldInfo;
3342 public:
3343 // Don't use this, but `File.lockingBinaryWriter()` instead.
3344 // Must be public for RefCounted and emplace() in druntime.
3345 this(scope ref File f)
3347 import std.exception : enforce;
3348 file_ = f;
3349 enforce(f._p && f._p.handle);
3350 name = f._name;
3351 FILE* fps = f._p.handle;
3352 static if (locking)
3353 _FLOCK(fps);
3355 version (Windows)
3357 .fflush(fps); // before changing translation mode
3358 fd = .fileno(fps);
3359 oldMode = .__setmode(fd, _O_BINARY);
3360 version (DIGITAL_MARS_STDIO)
3362 import core.atomic : atomicOp;
3364 // https://issues.dlang.org/show_bug.cgi?id=4243
3365 oldInfo = __fhnd_info[fd];
3366 atomicOp!"&="(__fhnd_info[fd], ~FHND_TEXT);
3371 ~this()
3373 if (!file_._p || !file_._p.handle)
3374 return;
3376 FILE* fps = file_._p.handle;
3378 version (Windows)
3380 .fflush(fps); // before restoring translation mode
3381 version (DIGITAL_MARS_STDIO)
3383 // https://issues.dlang.org/show_bug.cgi?id=4243
3384 __fhnd_info[fd] = oldInfo;
3386 .__setmode(fd, oldMode);
3389 _FUNLOCK(fps);
3392 void rawWrite(T)(in T[] buffer)
3394 import std.conv : text;
3395 import std.exception : errnoEnforce;
3397 auto result = trustedFwrite(file_._p.handle, buffer);
3398 if (result == result.max) result = 0;
3399 errnoEnforce(result == buffer.length,
3400 text("Wrote ", result, " instead of ", buffer.length,
3401 " objects of type ", T.stringof, " to file `",
3402 name, "'"));
3405 version (Windows)
3407 @disable this(this);
3409 else
3411 this(this)
3413 if (auto p = file_._p)
3415 if (p.handle) _FLOCK(p.handle);
3420 void put(T)(auto ref scope const T value)
3421 if (!hasIndirections!T &&
3422 !isInputRange!T)
3424 rawWrite((&value)[0 .. 1]);
3427 void put(T)(scope const(T)[] array)
3428 if (!hasIndirections!T &&
3429 !isInputRange!T)
3431 rawWrite(array);
3435 /** Returns an output range that locks the file and allows fast writing to it.
3437 Example:
3438 Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
3439 in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
3441 import std.algorithm, std.complex, std.range, std.stdio;
3443 void main()
3445 enum size = 500;
3446 writef("P5\n%d %d %d\n", size, size, ubyte.max);
3448 iota(-1, 3, 2.0/size).map!(y =>
3449 iota(-1.5, 0.5, 2.0/size).map!(x =>
3450 cast(ubyte)(1+
3451 recurrence!((a, n) => x + y * complex(0, 1) + a[n-1]^^2)(complex(0))
3452 .take(ubyte.max)
3453 .countUntil!(z => z.re^^2 + z.im^^2 > 4))
3456 .copy(stdout.lockingBinaryWriter);
3460 auto lockingBinaryWriter()
3462 alias LockingBinaryWriterImpl = BinaryWriterImpl!true;
3464 version (Windows)
3466 import std.typecons : RefCounted;
3467 alias LockingBinaryWriter = RefCounted!LockingBinaryWriterImpl;
3469 else
3470 alias LockingBinaryWriter = LockingBinaryWriterImpl;
3472 return LockingBinaryWriter(this);
3475 @system unittest
3477 import std.algorithm.mutation : reverse;
3478 import std.exception : collectException;
3479 static import std.file;
3480 import std.range : only, retro;
3481 import std.string : format;
3483 auto deleteme = testFilename();
3484 scope(exit) collectException(std.file.remove(deleteme));
3487 auto writer = File(deleteme, "wb").lockingBinaryWriter();
3488 auto input = File(deleteme, "rb");
3490 ubyte[1] byteIn = [42];
3491 writer.rawWrite(byteIn);
3492 destroy(writer);
3494 ubyte[1] byteOut = input.rawRead(new ubyte[1]);
3495 assert(byteIn[0] == byteOut[0]);
3498 auto output = File(deleteme, "wb");
3499 auto writer = output.lockingBinaryWriter();
3500 auto input = File(deleteme, "rb");
3502 T[] readExact(T)(T[] buf)
3504 auto result = input.rawRead(buf);
3505 assert(result.length == buf.length,
3506 "Read %d out of %d bytes"
3507 .format(result.length, buf.length));
3508 return result;
3511 // test raw values
3512 ubyte byteIn = 42;
3513 byteIn.only.copy(writer); output.flush();
3514 ubyte byteOut = readExact(new ubyte[1])[0];
3515 assert(byteIn == byteOut);
3517 // test arrays
3518 ubyte[] bytesIn = [1, 2, 3, 4, 5];
3519 bytesIn.copy(writer); output.flush();
3520 ubyte[] bytesOut = readExact(new ubyte[bytesIn.length]);
3521 scope(failure) .writeln(bytesOut);
3522 assert(bytesIn == bytesOut);
3524 // test ranges of values
3525 bytesIn.retro.copy(writer); output.flush();
3526 bytesOut = readExact(bytesOut);
3527 bytesOut.reverse();
3528 assert(bytesIn == bytesOut);
3530 // test string
3531 "foobar".copy(writer); output.flush();
3532 char[] charsOut = readExact(new char[6]);
3533 assert(charsOut == "foobar");
3535 // test ranges of arrays
3536 only("foo", "bar").copy(writer); output.flush();
3537 charsOut = readExact(charsOut);
3538 assert(charsOut == "foobar");
3540 // test that we are writing arrays as is,
3541 // without UTF-8 transcoding
3542 "foo"d.copy(writer); output.flush();
3543 dchar[] dcharsOut = readExact(new dchar[3]);
3544 assert(dcharsOut == "foo");
3547 /** Returns the size of the file in bytes, ulong.max if file is not searchable or throws if the operation fails.
3548 Example:
3550 import std.stdio, std.file;
3552 void main()
3554 string deleteme = "delete.me";
3555 auto file_handle = File(deleteme, "w");
3556 file_handle.write("abc"); //create temporary file
3557 scope(exit) deleteme.remove; //remove temporary file at scope exit
3559 assert(file_handle.size() == 3); //check if file size is 3 bytes
3563 @property ulong size() @safe
3565 import std.exception : collectException;
3567 ulong pos = void;
3568 if (collectException(pos = tell)) return ulong.max;
3569 scope(exit) seek(pos);
3570 seek(0, SEEK_END);
3571 return tell;
3575 @system unittest
3577 @system struct SystemToString
3579 string toString()
3581 return "system";
3585 @trusted struct TrustedToString
3587 string toString()
3589 return "trusted";
3593 @safe struct SafeToString
3595 string toString()
3597 return "safe";
3601 @system void systemTests()
3603 //system code can write to files/stdout with anything!
3604 if (false)
3606 auto f = File();
3608 f.write("just a string");
3609 f.write("string with arg: ", 47);
3610 f.write(SystemToString());
3611 f.write(TrustedToString());
3612 f.write(SafeToString());
3614 write("just a string");
3615 write("string with arg: ", 47);
3616 write(SystemToString());
3617 write(TrustedToString());
3618 write(SafeToString());
3620 f.writeln("just a string");
3621 f.writeln("string with arg: ", 47);
3622 f.writeln(SystemToString());
3623 f.writeln(TrustedToString());
3624 f.writeln(SafeToString());
3626 writeln("just a string");
3627 writeln("string with arg: ", 47);
3628 writeln(SystemToString());
3629 writeln(TrustedToString());
3630 writeln(SafeToString());
3632 f.writef("string with arg: %s", 47);
3633 f.writef("%s", SystemToString());
3634 f.writef("%s", TrustedToString());
3635 f.writef("%s", SafeToString());
3637 writef("string with arg: %s", 47);
3638 writef("%s", SystemToString());
3639 writef("%s", TrustedToString());
3640 writef("%s", SafeToString());
3642 f.writefln("string with arg: %s", 47);
3643 f.writefln("%s", SystemToString());
3644 f.writefln("%s", TrustedToString());
3645 f.writefln("%s", SafeToString());
3647 writefln("string with arg: %s", 47);
3648 writefln("%s", SystemToString());
3649 writefln("%s", TrustedToString());
3650 writefln("%s", SafeToString());
3654 @safe void safeTests()
3656 auto f = File();
3658 //safe code can write to files only with @safe and @trusted code...
3659 if (false)
3661 f.write("just a string");
3662 f.write("string with arg: ", 47);
3663 f.write(TrustedToString());
3664 f.write(SafeToString());
3666 write("just a string");
3667 write("string with arg: ", 47);
3668 write(TrustedToString());
3669 write(SafeToString());
3671 f.writeln("just a string");
3672 f.writeln("string with arg: ", 47);
3673 f.writeln(TrustedToString());
3674 f.writeln(SafeToString());
3676 writeln("just a string");
3677 writeln("string with arg: ", 47);
3678 writeln(TrustedToString());
3679 writeln(SafeToString());
3681 f.writef("string with arg: %s", 47);
3682 f.writef("%s", TrustedToString());
3683 f.writef("%s", SafeToString());
3685 writef("string with arg: %s", 47);
3686 writef("%s", TrustedToString());
3687 writef("%s", SafeToString());
3689 f.writefln("string with arg: %s", 47);
3690 f.writefln("%s", TrustedToString());
3691 f.writefln("%s", SafeToString());
3693 writefln("string with arg: %s", 47);
3694 writefln("%s", TrustedToString());
3695 writefln("%s", SafeToString());
3698 static assert(!__traits(compiles, f.write(SystemToString().toString())));
3699 static assert(!__traits(compiles, f.writeln(SystemToString())));
3700 static assert(!__traits(compiles, f.writef("%s", SystemToString())));
3701 static assert(!__traits(compiles, f.writefln("%s", SystemToString())));
3703 static assert(!__traits(compiles, write(SystemToString().toString())));
3704 static assert(!__traits(compiles, writeln(SystemToString())));
3705 static assert(!__traits(compiles, writef("%s", SystemToString())));
3706 static assert(!__traits(compiles, writefln("%s", SystemToString())));
3709 systemTests();
3710 safeTests();
3713 @safe unittest
3715 import std.exception : collectException;
3716 static import std.file;
3718 auto deleteme = testFilename();
3719 scope(exit) collectException(std.file.remove(deleteme));
3720 std.file.write(deleteme, "1 2 3");
3721 auto f = File(deleteme);
3722 assert(f.size == 5);
3723 assert(f.tell == 0);
3726 @system unittest
3728 // @system due to readln
3729 static import std.file;
3730 import std.range : chain, only, repeat;
3731 import std.range.primitives : isOutputRange;
3733 auto deleteme = testFilename();
3734 scope(exit) std.file.remove(deleteme);
3737 auto writer = File(deleteme, "w").lockingTextWriter();
3738 static assert(isOutputRange!(typeof(writer), dchar));
3739 writer.put("日本語");
3740 writer.put("日本語"w);
3741 writer.put("日本語"d);
3742 writer.put('日');
3743 writer.put(chain(only('本'), only('語')));
3744 // https://issues.dlang.org/show_bug.cgi?id=11945
3745 writer.put(repeat('#', 12));
3746 // https://issues.dlang.org/show_bug.cgi?id=17229
3747 writer.put(cast(immutable(ubyte)[])"日本語");
3749 assert(File(deleteme).readln() == "日本語日本語日本語日本語############日本語");
3752 @safe unittest // wchar -> char
3754 static import std.file;
3755 import std.exception : assertThrown;
3756 import std.utf : UTFException;
3758 auto deleteme = testFilename();
3759 scope(exit) std.file.remove(deleteme);
3762 auto writer = File(deleteme, "w").lockingTextWriter();
3763 writer.put("\U0001F608"w);
3765 assert(std.file.readText!string(deleteme) == "\U0001F608");
3767 // Test invalid input: unpaired high surrogate
3769 immutable wchar surr = "\U0001F608"w[0];
3770 auto f = File(deleteme, "w");
3771 assertThrown!UTFException(() {
3772 auto writer = f.lockingTextWriter();
3773 writer.put('x');
3774 writer.put(surr);
3775 assertThrown!UTFException(writer.put(char('y')));
3776 assertThrown!UTFException(writer.put(wchar('y')));
3777 assertThrown!UTFException(writer.put(dchar('y')));
3778 assertThrown!UTFException(writer.put(surr));
3779 // First `surr` is still unpaired at this point. `writer` gets
3780 // destroyed now, and the destructor throws a UTFException for
3781 // the unpaired surrogate.
3782 } ());
3784 assert(std.file.readText!string(deleteme) == "x");
3786 // Test invalid input: unpaired low surrogate
3788 immutable wchar surr = "\U0001F608"w[1];
3789 auto writer = File(deleteme, "w").lockingTextWriter();
3790 assertThrown!UTFException(writer.put(surr));
3791 writer.put('y');
3792 assertThrown!UTFException(writer.put(surr));
3794 assert(std.file.readText!string(deleteme) == "y");
3797 @safe unittest // issue 18801
3799 static import std.file;
3800 import std.string : stripLeft;
3802 auto deleteme = testFilename();
3803 scope(exit) std.file.remove(deleteme);
3806 auto writer = File(deleteme, "w,ccs=UTF-8").lockingTextWriter();
3807 writer.put("foo");
3809 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foo");
3812 auto writer = File(deleteme, "a,ccs=UTF-8").lockingTextWriter();
3813 writer.put("bar");
3815 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") == "foobar");
3817 @safe unittest // char/wchar -> wchar_t
3819 import core.stdc.locale : LC_CTYPE, setlocale;
3820 import core.stdc.wchar_ : fwide;
3821 import core.stdc.string : strlen;
3822 import std.algorithm.searching : any, endsWith;
3823 import std.conv : text;
3824 import std.meta : AliasSeq;
3825 import std.string : fromStringz, stripLeft;
3826 static import std.file;
3827 auto deleteme = testFilename();
3828 scope(exit) std.file.remove(deleteme);
3829 const char* oldCt = () @trusted {
3830 const(char)* p = setlocale(LC_CTYPE, null);
3831 // Subsequent calls to `setlocale` might invalidate this return value,
3832 // so duplicate it.
3833 // See: https://github.com/dlang/phobos/pull/7660
3834 return p ? p[0 .. strlen(p) + 1].idup.ptr : null;
3835 }();
3836 const utf8 = ["en_US.UTF-8", "C.UTF-8", ".65001"].any!((loc) @trusted {
3837 return setlocale(LC_CTYPE, loc.ptr).fromStringz.endsWith(loc);
3839 scope(exit) () @trusted { setlocale(LC_CTYPE, oldCt); } ();
3840 version (DIGITAL_MARS_STDIO) // DM can't handle Unicode above U+07FF.
3842 alias strs = AliasSeq!("xä\u07FE", "yö\u07FF"w);
3844 else
3846 alias strs = AliasSeq!("xä\U0001F607", "yö\U0001F608"w);
3849 auto f = File(deleteme, "w");
3850 version (MICROSOFT_STDIO)
3852 () @trusted { __setmode(fileno(f.getFP()), _O_U8TEXT); } ();
3854 else
3856 assert(fwide(f.getFP(), 1) == 1);
3858 auto writer = f.lockingTextWriter();
3859 assert(writer.orientation_ == 1);
3860 static foreach (s; strs) writer.put(s);
3862 assert(std.file.readText!string(deleteme).stripLeft("\uFEFF") ==
3863 text(strs));
3865 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18789
3867 static import std.file;
3868 auto deleteme = testFilename();
3869 scope(exit) std.file.remove(deleteme);
3870 // converting to char
3872 auto f = File(deleteme, "w");
3873 f.writeln("\U0001F608"w); // UTFException
3875 // converting to wchar_t
3877 auto f = File(deleteme, "w,ccs=UTF-16LE");
3878 // from char
3879 f.writeln("ö"); // writes garbage
3880 f.writeln("\U0001F608"); // ditto
3881 // from wchar
3882 f.writeln("\U0001F608"w); // leads to ErrnoException
3886 @safe unittest
3888 import std.exception : collectException;
3889 auto e = collectException({ File f; f.writeln("Hello!"); }());
3890 assert(e && e.msg == "Attempting to write to closed File");
3893 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=21592
3895 import std.exception : collectException;
3896 import std.utf : UTFException;
3897 static import std.file;
3898 auto deleteme = testFilename();
3899 scope(exit) std.file.remove(deleteme);
3900 auto f = File(deleteme, "w");
3901 auto e = collectException!UTFException(f.writeln(wchar(0xD801)));
3902 assert(e.next is null);
3905 version (StdStressTest)
3907 // https://issues.dlang.org/show_bug.cgi?id=15768
3908 @system unittest
3910 import std.parallelism : parallel;
3911 import std.range : iota;
3913 auto deleteme = testFilename();
3914 stderr = File(deleteme, "w");
3916 foreach (t; 1_000_000.iota.parallel)
3918 stderr.write("aaa");
3923 /// Used to specify the lock type for `File.lock` and `File.tryLock`.
3924 enum LockType
3927 * Specifies a _read (shared) lock. A _read lock denies all processes
3928 * write access to the specified region of the file, including the
3929 * process that first locks the region. All processes can _read the
3930 * locked region. Multiple simultaneous _read locks are allowed, as
3931 * long as there are no exclusive locks.
3933 read,
3936 * Specifies a read/write (exclusive) lock. A read/write lock denies all
3937 * other processes both read and write access to the locked file region.
3938 * If a segment has an exclusive lock, it may not have any shared locks
3939 * or other exclusive locks.
3941 readWrite
3944 struct LockingTextReader
3946 private File _f;
3947 private char _front;
3948 private bool _hasChar;
3950 this(File f)
3952 import std.exception : enforce;
3953 enforce(f.isOpen, "LockingTextReader: File must be open");
3954 _f = f;
3955 _FLOCK(_f._p.handle);
3958 this(this)
3960 _FLOCK(_f._p.handle);
3963 ~this()
3965 if (_hasChar)
3966 ungetc(_front, cast(FILE*)_f._p.handle);
3968 // File locking has its own reference count
3969 if (_f.isOpen) _FUNLOCK(_f._p.handle);
3972 void opAssign(LockingTextReader r)
3974 import std.algorithm.mutation : swap;
3975 swap(this, r);
3978 @property bool empty()
3980 if (!_hasChar)
3982 if (!_f.isOpen || _f.eof)
3983 return true;
3984 immutable int c = _FGETC(cast(_iobuf*) _f._p.handle);
3985 if (c == EOF)
3987 .destroy(_f);
3988 return true;
3990 _front = cast(char) c;
3991 _hasChar = true;
3993 return false;
3996 @property char front()
3998 if (!_hasChar)
4000 version (assert)
4002 import core.exception : RangeError;
4003 if (empty)
4004 throw new RangeError();
4006 else
4008 empty;
4011 return _front;
4014 void popFront()
4016 if (!_hasChar)
4017 empty;
4018 _hasChar = false;
4022 @system unittest
4024 // @system due to readf
4025 static import std.file;
4026 import std.range.primitives : isInputRange;
4028 static assert(isInputRange!LockingTextReader);
4029 auto deleteme = testFilename();
4030 std.file.write(deleteme, "1 2 3");
4031 scope(exit) std.file.remove(deleteme);
4032 int x;
4033 auto f = File(deleteme);
4034 f.readf("%s ", &x);
4035 assert(x == 1);
4036 f.readf("%d ", &x);
4037 assert(x == 2);
4038 f.readf("%d ", &x);
4039 assert(x == 3);
4042 // https://issues.dlang.org/show_bug.cgi?id=13686
4043 @system unittest
4045 import std.algorithm.comparison : equal;
4046 static import std.file;
4047 import std.utf : byDchar;
4049 auto deleteme = testFilename();
4050 std.file.write(deleteme, "Тест");
4051 scope(exit) std.file.remove(deleteme);
4053 string s;
4054 File(deleteme).readf("%s", &s);
4055 assert(s == "Тест");
4057 auto ltr = LockingTextReader(File(deleteme)).byDchar;
4058 assert(equal(ltr, "Тест".byDchar));
4061 // https://issues.dlang.org/show_bug.cgi?id=12320
4062 @system unittest
4064 static import std.file;
4065 auto deleteme = testFilename();
4066 std.file.write(deleteme, "ab");
4067 scope(exit) std.file.remove(deleteme);
4068 auto ltr = LockingTextReader(File(deleteme));
4069 assert(ltr.front == 'a');
4070 ltr.popFront();
4071 assert(ltr.front == 'b');
4072 ltr.popFront();
4073 assert(ltr.empty);
4076 // https://issues.dlang.org/show_bug.cgi?id=14861
4077 @system unittest
4079 // @system due to readf
4080 static import std.file;
4081 auto deleteme = testFilename();
4082 File fw = File(deleteme, "w");
4083 for (int i; i != 5000; i++)
4084 fw.writeln(i, ";", "Иванов;Пётр;Петрович");
4085 fw.close();
4086 scope(exit) std.file.remove(deleteme);
4087 // Test read
4088 File fr = File(deleteme, "r");
4089 scope (exit) fr.close();
4090 int nom; string fam, nam, ot;
4091 // Error format read
4092 while (!fr.eof)
4093 fr.readf("%s;%s;%s;%s\n", &nom, &fam, &nam, &ot);
4097 * Indicates whether `T` is a file handle, i.e. the type
4098 * is implicitly convertable to $(LREF File) or a pointer to a
4099 * $(REF FILE, core,stdc,stdio).
4101 * Returns:
4102 * `true` if `T` is a file handle, `false` otherwise.
4104 template isFileHandle(T)
4106 enum isFileHandle = is(T : FILE*) ||
4107 is(T : File);
4111 @safe unittest
4113 static assert(isFileHandle!(FILE*));
4114 static assert(isFileHandle!(File));
4118 * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
4120 private @property File trustedStdout() @trusted
4122 return stdout;
4125 /***********************************
4126 Writes its arguments in text format to standard output (without a trailing newline).
4128 Params:
4129 args = the items to write to `stdout`
4131 Throws: In case of an I/O error, throws an `StdioException`.
4133 Example:
4134 Reads `stdin` and writes it to `stdout` with an argument
4135 counter.
4137 import std.stdio;
4139 void main()
4141 string line;
4143 for (size_t count = 0; (line = readln) !is null; count++)
4145 write("Input ", count, ": ", line, "\n");
4150 void write(T...)(T args)
4151 if (!is(T[0] : File))
4153 trustedStdout.write(args);
4156 @system unittest
4158 static import std.file;
4160 scope(failure) printf("Failed test at line %d\n", __LINE__);
4161 void[] buf;
4162 if (false) write(buf);
4163 // test write
4164 auto deleteme = testFilename();
4165 auto f = File(deleteme, "w");
4166 f.write("Hello, ", "world number ", 42, "!");
4167 f.close();
4168 scope(exit) { std.file.remove(deleteme); }
4169 assert(cast(char[]) std.file.read(deleteme) == "Hello, world number 42!");
4172 /***********************************
4173 * Equivalent to `write(args, '\n')`. Calling `writeln` without
4174 * arguments is valid and just prints a newline to the standard
4175 * output.
4177 * Params:
4178 * args = the items to write to `stdout`
4180 * Throws:
4181 * In case of an I/O error, throws an $(LREF StdioException).
4182 * Example:
4183 * Reads `stdin` and writes it to `stdout` with an argument
4184 * counter.
4186 import std.stdio;
4188 void main()
4190 string line;
4192 for (size_t count = 0; (line = readln) !is null; count++)
4194 writeln("Input ", count, ": ", line);
4199 void writeln(T...)(T args)
4201 static if (T.length == 0)
4203 import std.exception : enforce;
4205 enforce(fputc('\n', .trustedStdout._p.handle) != EOF, "fputc failed");
4207 else static if (T.length == 1 &&
4208 is(T[0] : const(char)[]) &&
4209 (is(T[0] == U[], U) || __traits(isStaticArray, T[0])))
4211 // Specialization for strings - a very frequent case
4212 auto w = .trustedStdout.lockingTextWriter();
4214 static if (__traits(isStaticArray, T[0]))
4216 w.put(args[0][]);
4218 else
4220 w.put(args[0]);
4222 w.put('\n');
4224 else
4226 // Most general instance
4227 trustedStdout.write(args, '\n');
4231 @safe unittest
4233 // Just make sure the call compiles
4234 if (false) writeln();
4236 if (false) writeln("wyda");
4238 // https://issues.dlang.org/show_bug.cgi?id=8040
4239 if (false) writeln(null);
4240 if (false) writeln(">", null, "<");
4242 // https://issues.dlang.org/show_bug.cgi?id=14041
4243 if (false)
4245 char[8] a;
4246 writeln(a);
4247 immutable b = a;
4248 b.writeln;
4249 const c = a[];
4250 c.writeln;
4254 @system unittest
4256 static import std.file;
4258 scope(failure) printf("Failed test at line %d\n", __LINE__);
4260 // test writeln
4261 auto deleteme = testFilename();
4262 auto f = File(deleteme, "w");
4263 scope(exit) { std.file.remove(deleteme); }
4264 f.writeln("Hello, ", "world number ", 42, "!");
4265 f.close();
4266 version (Windows)
4267 assert(cast(char[]) std.file.read(deleteme) ==
4268 "Hello, world number 42!\r\n");
4269 else
4270 assert(cast(char[]) std.file.read(deleteme) ==
4271 "Hello, world number 42!\n");
4273 // test writeln on stdout
4274 auto saveStdout = stdout;
4275 scope(exit) stdout = saveStdout;
4276 stdout.open(deleteme, "w");
4277 writeln("Hello, ", "world number ", 42, "!");
4278 stdout.close();
4279 version (Windows)
4280 assert(cast(char[]) std.file.read(deleteme) ==
4281 "Hello, world number 42!\r\n");
4282 else
4283 assert(cast(char[]) std.file.read(deleteme) ==
4284 "Hello, world number 42!\n");
4286 stdout.open(deleteme, "w");
4287 writeln("Hello!"c);
4288 writeln("Hello!"w); // https://issues.dlang.org/show_bug.cgi?id=8386
4289 writeln("Hello!"d); // https://issues.dlang.org/show_bug.cgi?id=8386
4290 writeln("embedded\0null"c); // https://issues.dlang.org/show_bug.cgi?id=8730
4291 stdout.close();
4292 version (Windows)
4293 assert(cast(char[]) std.file.read(deleteme) ==
4294 "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
4295 else
4296 assert(cast(char[]) std.file.read(deleteme) ==
4297 "Hello!\nHello!\nHello!\nembedded\0null\n");
4300 @system unittest
4302 static import std.file;
4304 auto deleteme = testFilename();
4305 auto f = File(deleteme, "w");
4306 scope(exit) { std.file.remove(deleteme); }
4308 enum EI : int { A, B }
4309 enum ED : double { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4310 enum EC : char { A = 0, B } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4311 enum ES : string { A = "aaa", B = "bbb" }
4313 f.writeln(EI.A); // false, but A on 2.058
4314 f.writeln(EI.B); // true, but B on 2.058
4316 f.writeln(ED.A); // A
4317 f.writeln(ED.B); // B
4319 f.writeln(EC.A); // A
4320 f.writeln(EC.B); // B
4322 f.writeln(ES.A); // A
4323 f.writeln(ES.B); // B
4325 f.close();
4326 version (Windows)
4327 assert(cast(char[]) std.file.read(deleteme) ==
4328 "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
4329 else
4330 assert(cast(char[]) std.file.read(deleteme) ==
4331 "A\nB\nA\nB\nA\nB\nA\nB\n");
4334 @system unittest
4336 static auto useInit(T)(T ltw)
4338 T val;
4339 val = ltw;
4340 val = T.init;
4341 return val;
4343 useInit(stdout.lockingTextWriter());
4346 @system unittest
4348 // https://issues.dlang.org/show_bug.cgi?id=21920
4349 void function(string) printer = &writeln!string;
4350 if (false) printer("Hello");
4354 /***********************************
4355 Writes formatted data to standard output (without a trailing newline).
4357 Params:
4358 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4359 When passed as a compile-time argument, the string will be statically checked
4360 against the argument types passed.
4361 args = Items to write.
4363 Note: In older versions of Phobos, it used to be possible to write:
4365 ------
4366 writef(stderr, "%s", "message");
4367 ------
4369 to print a message to `stderr`. This syntax is no longer supported, and has
4370 been superceded by:
4372 ------
4373 stderr.writef("%s", "message");
4374 ------
4377 void writef(alias fmt, A...)(A args)
4378 if (isSomeString!(typeof(fmt)))
4380 import std.format : checkFormatException;
4382 alias e = checkFormatException!(fmt, A);
4383 static assert(!e, e);
4384 return .writef(fmt, args);
4387 /// ditto
4388 void writef(Char, A...)(in Char[] fmt, A args)
4390 trustedStdout.writef(fmt, args);
4393 @system unittest
4395 static import std.file;
4397 scope(failure) printf("Failed test at line %d\n", __LINE__);
4399 // test writef
4400 auto deleteme = testFilename();
4401 auto f = File(deleteme, "w");
4402 scope(exit) { std.file.remove(deleteme); }
4403 f.writef!"Hello, %s world number %s!"("nice", 42);
4404 f.close();
4405 assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
4406 // test write on stdout
4407 auto saveStdout = stdout;
4408 scope(exit) stdout = saveStdout;
4409 stdout.open(deleteme, "w");
4410 writef!"Hello, %s world number %s!"("nice", 42);
4411 stdout.close();
4412 assert(cast(char[]) std.file.read(deleteme) == "Hello, nice world number 42!");
4415 /***********************************
4416 * Equivalent to $(D writef(fmt, args, '\n')).
4418 void writefln(alias fmt, A...)(A args)
4419 if (isSomeString!(typeof(fmt)))
4421 import std.format : checkFormatException;
4423 alias e = checkFormatException!(fmt, A);
4424 static assert(!e, e);
4425 return .writefln(fmt, args);
4428 /// ditto
4429 void writefln(Char, A...)(in Char[] fmt, A args)
4431 trustedStdout.writefln(fmt, args);
4434 @system unittest
4436 static import std.file;
4438 scope(failure) printf("Failed test at line %d\n", __LINE__);
4440 // test File.writefln
4441 auto deleteme = testFilename();
4442 auto f = File(deleteme, "w");
4443 scope(exit) { std.file.remove(deleteme); }
4444 f.writefln!"Hello, %s world number %s!"("nice", 42);
4445 f.close();
4446 version (Windows)
4447 assert(cast(char[]) std.file.read(deleteme) ==
4448 "Hello, nice world number 42!\r\n");
4449 else
4450 assert(cast(char[]) std.file.read(deleteme) ==
4451 "Hello, nice world number 42!\n",
4452 cast(char[]) std.file.read(deleteme));
4454 // test writefln
4455 auto saveStdout = stdout;
4456 scope(exit) stdout = saveStdout;
4457 stdout.open(deleteme, "w");
4458 writefln!"Hello, %s world number %s!"("nice", 42);
4459 stdout.close();
4460 version (Windows)
4461 assert(cast(char[]) std.file.read(deleteme) ==
4462 "Hello, nice world number 42!\r\n");
4463 else
4464 assert(cast(char[]) std.file.read(deleteme) ==
4465 "Hello, nice world number 42!\n");
4469 * Reads formatted data from `stdin` using $(REF formattedRead, std,_format).
4470 * Params:
4471 * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4472 * When passed as a compile-time argument, the string will be statically checked
4473 * against the argument types passed.
4474 * args = Items to be read.
4475 * Returns:
4476 * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
4477 * this number will be less than the number of variables provided.
4478 * Example:
4479 ----
4480 // test.d
4481 void main()
4483 import std.stdio;
4484 foreach (_; 0 .. 3)
4486 int a;
4487 readf!" %d"(a);
4488 writeln(++a);
4491 ----
4492 $(CONSOLE
4493 % echo "1 2 3" | rdmd test.d
4499 uint readf(alias format, A...)(auto ref A args)
4500 if (isSomeString!(typeof(format)))
4502 import std.format : checkFormatException;
4504 alias e = checkFormatException!(format, A);
4505 static assert(!e, e);
4506 return .readf(format, args);
4509 /// ditto
4510 uint readf(A...)(scope const(char)[] format, auto ref A args)
4512 return stdin.readf(format, args);
4515 @system unittest
4517 float f;
4518 if (false) readf("%s", &f);
4520 char a;
4521 wchar b;
4522 dchar c;
4523 if (false) readf("%s %s %s", a, b, c);
4524 // backwards compatibility with pointers
4525 if (false) readf("%s %s %s", a, &b, c);
4526 if (false) readf("%s %s %s", &a, &b, &c);
4529 /**********************************
4530 * Read line from `stdin`.
4532 * This version manages its own read buffer, which means one memory allocation per call. If you are not
4533 * retaining a reference to the read data, consider the `readln(buf)` version, which may offer
4534 * better performance as it can reuse its read buffer.
4536 * Returns:
4537 * The line that was read, including the line terminator character.
4538 * Params:
4539 * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
4540 * terminator = Line terminator (by default, `'\n'`).
4541 * Note:
4542 * String terminators are not supported due to ambiguity with readln(buf) below.
4543 * Throws:
4544 * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4545 * Example:
4546 * Reads `stdin` and writes it to `stdout`.
4548 import std.stdio;
4550 void main()
4552 string line;
4553 while ((line = readln()) !is null)
4554 write(line);
4558 S readln(S = string)(dchar terminator = '\n')
4559 if (isSomeString!S)
4561 return stdin.readln!S(terminator);
4564 /**********************************
4565 * Read line from `stdin` and write it to buf[], including terminating character.
4567 * This can be faster than $(D line = readln()) because you can reuse
4568 * the buffer for each call. Note that reusing the buffer means that you
4569 * must copy the previous contents if you wish to retain them.
4571 * Returns:
4572 * `size_t` 0 for end of file, otherwise number of characters read
4573 * Params:
4574 * buf = Buffer used to store the resulting line data. buf is resized as necessary.
4575 * terminator = Line terminator (by default, `'\n'`). Use $(REF newline, std,ascii)
4576 * for portability (unless the file was opened in text mode).
4577 * Throws:
4578 * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4579 * Example:
4580 * Reads `stdin` and writes it to `stdout`.
4582 import std.stdio;
4584 void main()
4586 char[] buf;
4587 while (readln(buf))
4588 write(buf);
4592 size_t readln(C)(ref C[] buf, dchar terminator = '\n')
4593 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum))
4595 return stdin.readln(buf, terminator);
4598 /** ditto */
4599 size_t readln(C, R)(ref C[] buf, R terminator)
4600 if (isSomeChar!C && is(Unqual!C == C) && !is(C == enum) &&
4601 isBidirectionalRange!R && is(typeof(terminator.front == dchar.init)))
4603 return stdin.readln(buf, terminator);
4606 @safe unittest
4608 import std.meta : AliasSeq;
4610 //we can't actually test readln, so at the very least,
4611 //we test compilability
4612 void foo()
4614 readln();
4615 readln('\t');
4616 static foreach (String; AliasSeq!(string, char[], wstring, wchar[], dstring, dchar[]))
4618 readln!String();
4619 readln!String('\t');
4621 static foreach (String; AliasSeq!(char[], wchar[], dchar[]))
4623 String buf;
4624 readln(buf);
4625 readln(buf, '\t');
4626 readln(buf, "<br />");
4632 * Convenience function that forwards to `core.sys.posix.stdio.fopen`
4633 * (to `_wfopen` on Windows)
4634 * with appropriately-constructed C-style strings.
4636 private FILE* _fopen(R1, R2)(R1 name, R2 mode = "r")
4637 if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
4638 (isSomeFiniteCharInputRange!R2 || isSomeString!R2))
4640 import std.internal.cstring : tempCString;
4642 auto namez = name.tempCString!FSChar();
4643 auto modez = mode.tempCString!FSChar();
4645 static _fopenImpl(scope const(FSChar)* namez, scope const(FSChar)* modez) @trusted nothrow @nogc
4647 version (Windows)
4649 return _wfopen(namez, modez);
4651 else version (Posix)
4654 * The new opengroup large file support API is transparently
4655 * included in the normal C bindings. http://opengroup.org/platform/lfs.html#1.0
4656 * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
4657 * the normal functions work fine. If not, then large file support
4658 * probably isn't available. Do not use the old transitional API
4659 * (the native extern(C) fopen64, http://www.unix.org/version2/whatsnew/lfs20mar.html#3.0)
4661 import core.sys.posix.stdio : fopen;
4662 return fopen(namez, modez);
4664 else
4666 return fopen(namez, modez);
4669 return _fopenImpl(namez, modez);
4672 version (Posix)
4674 /***********************************
4675 * Convenience function that forwards to `core.sys.posix.stdio.popen`
4676 * with appropriately-constructed C-style strings.
4678 FILE* _popen(R1, R2)(R1 name, R2 mode = "r") @trusted nothrow @nogc
4679 if ((isSomeFiniteCharInputRange!R1 || isSomeString!R1) &&
4680 (isSomeFiniteCharInputRange!R2 || isSomeString!R2))
4682 import std.internal.cstring : tempCString;
4684 auto namez = name.tempCString!FSChar();
4685 auto modez = mode.tempCString!FSChar();
4687 static popenImpl(const(FSChar)* namez, const(FSChar)* modez) @trusted nothrow @nogc
4689 import core.sys.posix.stdio : popen;
4690 return popen(namez, modez);
4692 return popenImpl(namez, modez);
4697 * Convenience function that forwards to `core.stdc.stdio.fwrite`
4699 private auto trustedFwrite(T)(FILE* f, const T[] obj) @trusted
4701 return fwrite(obj.ptr, T.sizeof, obj.length, f);
4705 * Convenience function that forwards to `core.stdc.stdio.fread`
4707 private auto trustedFread(T)(FILE* f, T[] obj) @trusted
4709 return fread(obj.ptr, T.sizeof, obj.length, f);
4713 * Iterates through the lines of a file by using `foreach`.
4715 * Example:
4717 ---------
4718 void main()
4720 foreach (string line; lines(stdin))
4722 ... use line ...
4725 ---------
4726 The line terminator (`'\n'` by default) is part of the string read (it
4727 could be missing in the last line of the file). Several types are
4728 supported for `line`, and the behavior of `lines`
4729 changes accordingly:
4731 $(OL $(LI If `line` has type `string`, $(D
4732 wstring), or `dstring`, a new string of the respective type
4733 is allocated every read.) $(LI If `line` has type $(D
4734 char[]), `wchar[]`, `dchar[]`, the line's content
4735 will be reused (overwritten) across reads.) $(LI If `line`
4736 has type `immutable(ubyte)[]`, the behavior is similar to
4737 case (1), except that no UTF checking is attempted upon input.) $(LI
4738 If `line` has type `ubyte[]`, the behavior is
4739 similar to case (2), except that no UTF checking is attempted upon
4740 input.))
4742 In all cases, a two-symbols versions is also accepted, in which case
4743 the first symbol (of integral type, e.g. `ulong` or $(D
4744 uint)) tracks the zero-based number of the current line.
4746 Example:
4747 ----
4748 foreach (ulong i, string line; lines(stdin))
4750 ... use line ...
4752 ----
4754 In case of an I/O error, an `StdioException` is thrown.
4756 See_Also:
4757 $(LREF byLine)
4760 struct lines
4762 private File f;
4763 private dchar terminator = '\n';
4766 Constructor.
4767 Params:
4768 f = File to read lines from.
4769 terminator = Line separator (`'\n'` by default).
4771 this(File f, dchar terminator = '\n')
4773 this.f = f;
4774 this.terminator = terminator;
4777 int opApply(D)(scope D dg)
4779 import std.traits : Parameters;
4780 alias Parms = Parameters!(dg);
4781 static if (isSomeString!(Parms[$ - 1]))
4783 int result = 0;
4784 static if (is(Parms[$ - 1] : const(char)[]))
4785 alias C = char;
4786 else static if (is(Parms[$ - 1] : const(wchar)[]))
4787 alias C = wchar;
4788 else static if (is(Parms[$ - 1] : const(dchar)[]))
4789 alias C = dchar;
4790 C[] line;
4791 static if (Parms.length == 2)
4792 Parms[0] i = 0;
4793 for (;;)
4795 import std.conv : to;
4797 if (!f.readln(line, terminator)) break;
4798 auto copy = to!(Parms[$ - 1])(line);
4799 static if (Parms.length == 2)
4801 result = dg(i, copy);
4802 ++i;
4804 else
4806 result = dg(copy);
4808 if (result != 0) break;
4810 return result;
4812 else
4814 // raw read
4815 return opApplyRaw(dg);
4818 // no UTF checking
4819 int opApplyRaw(D)(scope D dg)
4821 import std.conv : to;
4822 import std.exception : assumeUnique;
4823 import std.traits : Parameters;
4825 alias Parms = Parameters!(dg);
4826 enum duplicate = is(Parms[$ - 1] : immutable(ubyte)[]);
4827 int result = 1;
4828 int c = void;
4829 _FLOCK(f._p.handle);
4830 scope(exit) _FUNLOCK(f._p.handle);
4831 ubyte[] buffer;
4832 static if (Parms.length == 2)
4833 Parms[0] line = 0;
4834 while ((c = _FGETC(cast(_iobuf*) f._p.handle)) != -1)
4836 buffer ~= to!(ubyte)(c);
4837 if (c == terminator)
4839 static if (duplicate)
4840 auto arg = assumeUnique(buffer);
4841 else
4842 alias arg = buffer;
4843 // unlock the file while calling the delegate
4844 _FUNLOCK(f._p.handle);
4845 scope(exit) _FLOCK(f._p.handle);
4846 static if (Parms.length == 1)
4848 result = dg(arg);
4850 else
4852 result = dg(line, arg);
4853 ++line;
4855 if (result) break;
4856 static if (!duplicate)
4857 buffer.length = 0;
4860 // can only reach when _FGETC returned -1
4861 if (!f.eof) throw new StdioException("Error in reading file"); // error occured
4862 return result;
4866 @system unittest
4868 static import std.file;
4869 import std.meta : AliasSeq;
4871 scope(failure) printf("Failed test at line %d\n", __LINE__);
4873 auto deleteme = testFilename();
4874 scope(exit) { std.file.remove(deleteme); }
4876 alias TestedWith =
4877 AliasSeq!(string, wstring, dstring,
4878 char[], wchar[], dchar[]);
4879 foreach (T; TestedWith)
4881 // test looping with an empty file
4882 std.file.write(deleteme, "");
4883 auto f = File(deleteme, "r");
4884 foreach (T line; lines(f))
4886 assert(false);
4888 f.close();
4890 // test looping with a file with three lines
4891 std.file.write(deleteme, "Line one\nline two\nline three\n");
4892 f.open(deleteme, "r");
4893 uint i = 0;
4894 foreach (T line; lines(f))
4896 if (i == 0) assert(line == "Line one\n");
4897 else if (i == 1) assert(line == "line two\n");
4898 else if (i == 2) assert(line == "line three\n");
4899 else assert(false);
4900 ++i;
4902 f.close();
4904 // test looping with a file with three lines, last without a newline
4905 std.file.write(deleteme, "Line one\nline two\nline three");
4906 f.open(deleteme, "r");
4907 i = 0;
4908 foreach (T line; lines(f))
4910 if (i == 0) assert(line == "Line one\n");
4911 else if (i == 1) assert(line == "line two\n");
4912 else if (i == 2) assert(line == "line three");
4913 else assert(false);
4914 ++i;
4916 f.close();
4919 // test with ubyte[] inputs
4920 alias TestedWith2 = AliasSeq!(immutable(ubyte)[], ubyte[]);
4921 foreach (T; TestedWith2)
4923 // test looping with an empty file
4924 std.file.write(deleteme, "");
4925 auto f = File(deleteme, "r");
4926 foreach (T line; lines(f))
4928 assert(false);
4930 f.close();
4932 // test looping with a file with three lines
4933 std.file.write(deleteme, "Line one\nline two\nline three\n");
4934 f.open(deleteme, "r");
4935 uint i = 0;
4936 foreach (T line; lines(f))
4938 if (i == 0) assert(cast(char[]) line == "Line one\n");
4939 else if (i == 1) assert(cast(char[]) line == "line two\n",
4940 T.stringof ~ " " ~ cast(char[]) line);
4941 else if (i == 2) assert(cast(char[]) line == "line three\n");
4942 else assert(false);
4943 ++i;
4945 f.close();
4947 // test looping with a file with three lines, last without a newline
4948 std.file.write(deleteme, "Line one\nline two\nline three");
4949 f.open(deleteme, "r");
4950 i = 0;
4951 foreach (T line; lines(f))
4953 if (i == 0) assert(cast(char[]) line == "Line one\n");
4954 else if (i == 1) assert(cast(char[]) line == "line two\n");
4955 else if (i == 2) assert(cast(char[]) line == "line three");
4956 else assert(false);
4957 ++i;
4959 f.close();
4963 static foreach (T; AliasSeq!(ubyte[]))
4965 // test looping with a file with three lines, last without a newline
4966 // using a counter too this time
4967 std.file.write(deleteme, "Line one\nline two\nline three");
4968 auto f = File(deleteme, "r");
4969 uint i = 0;
4970 foreach (ulong j, T line; lines(f))
4972 if (i == 0) assert(cast(char[]) line == "Line one\n");
4973 else if (i == 1) assert(cast(char[]) line == "line two\n");
4974 else if (i == 2) assert(cast(char[]) line == "line three");
4975 else assert(false);
4976 ++i;
4978 f.close();
4983 Iterates through a file a chunk at a time by using `foreach`.
4985 Example:
4987 ---------
4988 void main()
4990 foreach (ubyte[] buffer; chunks(stdin, 4096))
4992 ... use buffer ...
4995 ---------
4997 The content of `buffer` is reused across calls. In the
4998 example above, `buffer.length` is 4096 for all iterations,
4999 except for the last one, in which case `buffer.length` may
5000 be less than 4096 (but always greater than zero).
5002 In case of an I/O error, an `StdioException` is thrown.
5004 auto chunks(File f, size_t size)
5006 return ChunksImpl(f, size);
5008 private struct ChunksImpl
5010 private File f;
5011 private size_t size;
5012 // private string fileName; // Currently, no use
5014 this(File f, size_t size)
5017 assert(size, "size must be larger than 0");
5021 this.f = f;
5022 this.size = size;
5025 int opApply(D)(scope D dg)
5027 import core.stdc.stdlib : alloca;
5028 import std.exception : enforce;
5030 enforce(f.isOpen, "Attempting to read from an unopened file");
5031 enum maxStackSize = 1024 * 16;
5032 ubyte[] buffer = void;
5033 if (size < maxStackSize)
5034 buffer = (cast(ubyte*) alloca(size))[0 .. size];
5035 else
5036 buffer = new ubyte[size];
5037 size_t r = void;
5038 int result = 1;
5039 uint tally = 0;
5040 while ((r = trustedFread(f._p.handle, buffer)) > 0)
5042 assert(r <= size);
5043 if (r != size)
5045 // error occured
5046 if (!f.eof) throw new StdioException(null);
5047 buffer.length = r;
5049 static if (is(typeof(dg(tally, buffer))))
5051 if ((result = dg(tally, buffer)) != 0) break;
5053 else
5055 if ((result = dg(buffer)) != 0) break;
5057 ++tally;
5059 return result;
5063 @system unittest
5065 static import std.file;
5067 scope(failure) printf("Failed test at line %d\n", __LINE__);
5069 auto deleteme = testFilename();
5070 scope(exit) { std.file.remove(deleteme); }
5072 // test looping with an empty file
5073 std.file.write(deleteme, "");
5074 auto f = File(deleteme, "r");
5075 foreach (ubyte[] line; chunks(f, 4))
5077 assert(false);
5079 f.close();
5081 // test looping with a file with three lines
5082 std.file.write(deleteme, "Line one\nline two\nline three\n");
5083 f = File(deleteme, "r");
5084 uint i = 0;
5085 foreach (ubyte[] line; chunks(f, 3))
5087 if (i == 0) assert(cast(char[]) line == "Lin");
5088 else if (i == 1) assert(cast(char[]) line == "e o");
5089 else if (i == 2) assert(cast(char[]) line == "ne\n");
5090 else break;
5091 ++i;
5093 f.close();
5096 // Issue 21730 - null ptr dereferenced in ChunksImpl.opApply (SIGSEGV)
5097 @system unittest
5099 import std.exception : assertThrown;
5100 static import std.file;
5102 auto deleteme = testFilename();
5103 scope(exit) { if (std.file.exists(deleteme)) std.file.remove(deleteme); }
5105 auto err1 = File(deleteme, "w+x");
5106 err1.close;
5107 std.file.remove(deleteme);
5108 assertThrown(() {foreach (ubyte[] buf; chunks(err1, 4096)) {}}());
5112 Writes an array or range to a file.
5113 Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
5114 Similar to $(REF write, std,file), strings are written as-is,
5115 rather than encoded according to the `File`'s $(HTTP
5116 en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
5117 orientation).
5119 void toFile(T)(T data, string fileName)
5120 if (is(typeof(copy(data, stdout.lockingBinaryWriter))))
5122 copy(data, File(fileName, "wb").lockingBinaryWriter);
5125 @system unittest
5127 static import std.file;
5129 auto deleteme = testFilename();
5130 scope(exit) { std.file.remove(deleteme); }
5132 "Test".toFile(deleteme);
5133 assert(std.file.readText(deleteme) == "Test");
5136 /*********************
5137 * Thrown if I/O errors happen.
5139 class StdioException : Exception
5141 static import core.stdc.errno;
5142 /// Operating system error code.
5143 uint errno;
5146 Initialize with a message and an error code.
5148 this(string message, uint e = core.stdc.errno.errno) @trusted
5150 import std.exception : errnoString;
5151 errno = e;
5152 auto sysmsg = errnoString(errno);
5153 // If e is 0, we don't use the system error message. (The message
5154 // is "Success", which is rather pointless for an exception.)
5155 super(e == 0 ? message
5156 : (message ? message ~ " (" ~ sysmsg ~ ")" : sysmsg));
5159 /** Convenience functions that throw an `StdioException`. */
5160 static void opCall(string msg)
5162 throw new StdioException(msg);
5165 /// ditto
5166 static void opCall()
5168 throw new StdioException(null, core.stdc.errno.errno);
5172 enum StdFileHandle: string
5174 stdin = "core.stdc.stdio.stdin",
5175 stdout = "core.stdc.stdio.stdout",
5176 stderr = "core.stdc.stdio.stderr",
5179 // Undocumented but public because the std* handles are aliasing it.
5180 @property ref File makeGlobal(StdFileHandle _iob)()
5182 __gshared File.Impl impl;
5183 __gshared File result;
5185 // Use an inline spinlock to make sure the initializer is only run once.
5186 // We assume there will be at most uint.max / 2 threads trying to initialize
5187 // `handle` at once and steal the high bit to indicate that the globals have
5188 // been initialized.
5189 static shared uint spinlock;
5190 import core.atomic : atomicLoad, atomicOp, MemoryOrder;
5191 if (atomicLoad!(MemoryOrder.acq)(spinlock) <= uint.max / 2)
5193 for (;;)
5195 if (atomicLoad!(MemoryOrder.acq)(spinlock) > uint.max / 2)
5196 break;
5197 if (atomicOp!"+="(spinlock, 1) == 1)
5199 with (StdFileHandle)
5200 assert(_iob == stdin || _iob == stdout || _iob == stderr);
5201 impl.handle = mixin(_iob);
5202 result._p = &impl;
5203 atomicOp!"+="(spinlock, uint.max / 2);
5204 break;
5206 atomicOp!"-="(spinlock, 1);
5209 return result;
5212 /** The standard input stream.
5214 Returns:
5215 stdin as a $(LREF File).
5217 Note:
5218 The returned $(LREF File) wraps $(REF stdin,core,stdc,stdio), and
5219 is therefore thread global. Reassigning `stdin` to a different
5220 `File` must be done in a single-threaded or locked context in
5221 order to avoid race conditions.
5223 All reading from `stdin` automatically locks the file globally,
5224 and will cause all other threads calling `read` to wait until
5225 the lock is released.
5227 alias stdin = makeGlobal!(StdFileHandle.stdin);
5230 @safe unittest
5232 // Read stdin, sort lines, write to stdout
5233 import std.algorithm.mutation : copy;
5234 import std.algorithm.sorting : sort;
5235 import std.array : array;
5236 import std.typecons : Yes;
5238 void main()
5240 stdin // read from stdin
5241 .byLineCopy(Yes.keepTerminator) // copying each line
5242 .array() // convert to array of lines
5243 .sort() // sort the lines
5244 .copy( // copy output of .sort to an OutputRange
5245 stdout.lockingTextWriter()); // the OutputRange
5250 The standard output stream.
5252 Returns:
5253 stdout as a $(LREF File).
5255 Note:
5256 The returned $(LREF File) wraps $(REF stdout,core,stdc,stdio), and
5257 is therefore thread global. Reassigning `stdout` to a different
5258 `File` must be done in a single-threaded or locked context in
5259 order to avoid race conditions.
5261 All writing to `stdout` automatically locks the file globally,
5262 and will cause all other threads calling `write` to wait until
5263 the lock is released.
5265 alias stdout = makeGlobal!(StdFileHandle.stdout);
5268 @safe unittest
5270 void main()
5272 stdout.writeln("Write a message to stdout.");
5277 @safe unittest
5279 void main()
5281 import std.algorithm.iteration : filter, map, sum;
5282 import std.format : format;
5283 import std.range : iota, tee;
5285 int len;
5286 const r = 6.iota
5287 .filter!(a => a % 2) // 1 3 5
5288 .map!(a => a * 2) // 2 6 10
5289 .tee!(_ => stdout.writefln("len: %d", len++))
5290 .sum;
5292 assert(r == 18);
5297 @safe unittest
5299 void main()
5301 import std.algorithm.mutation : copy;
5302 import std.algorithm.iteration : map;
5303 import std.format : format;
5304 import std.range : iota;
5306 10.iota
5307 .map!(e => "N: %d".format(e))
5308 .copy(stdout.lockingTextWriter()); // the OutputRange
5313 The standard error stream.
5315 Returns:
5316 stderr as a $(LREF File).
5318 Note:
5319 The returned $(LREF File) wraps $(REF stderr,core,stdc,stdio), and
5320 is therefore thread global. Reassigning `stderr` to a different
5321 `File` must be done in a single-threaded or locked context in
5322 order to avoid race conditions.
5324 All writing to `stderr` automatically locks the file globally,
5325 and will cause all other threads calling `write` to wait until
5326 the lock is released.
5328 alias stderr = makeGlobal!(StdFileHandle.stderr);
5331 @safe unittest
5333 void main()
5335 stderr.writeln("Write a message to stderr.");
5339 @system unittest
5341 static import std.file;
5342 import std.typecons : tuple;
5344 scope(failure) printf("Failed test at line %d\n", __LINE__);
5345 auto deleteme = testFilename();
5347 std.file.write(deleteme, "1 2\n4 1\n5 100");
5348 scope(exit) std.file.remove(deleteme);
5350 File f = File(deleteme);
5351 scope(exit) f.close();
5352 auto t = [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
5353 uint i;
5354 foreach (e; f.byRecord!(int, int)("%s %s"))
5356 //writeln(e);
5357 assert(e == t[i++]);
5359 assert(i == 3);
5363 @safe unittest
5365 // Retain backwards compatibility
5366 // https://issues.dlang.org/show_bug.cgi?id=17472
5367 static assert(is(typeof(stdin) == File));
5368 static assert(is(typeof(stdout) == File));
5369 static assert(is(typeof(stderr) == File));
5372 // roll our own appender, but with "safe" arrays
5373 private struct ReadlnAppender
5375 char[] buf;
5376 size_t pos;
5377 bool safeAppend = false;
5379 void initialize(char[] b)
5381 buf = b;
5382 pos = 0;
5384 @property char[] data() @trusted
5386 if (safeAppend)
5387 assumeSafeAppend(buf.ptr[0 .. pos]);
5388 return buf.ptr[0 .. pos];
5391 bool reserveWithoutAllocating(size_t n)
5393 if (buf.length >= pos + n) // buf is already large enough
5394 return true;
5396 immutable curCap = buf.capacity;
5397 if (curCap >= pos + n)
5399 buf.length = curCap;
5400 /* Any extra capacity we end up not using can safely be claimed
5401 by someone else. */
5402 safeAppend = true;
5403 return true;
5406 return false;
5408 void reserve(size_t n) @trusted
5410 import core.stdc.string : memcpy;
5411 if (!reserveWithoutAllocating(n))
5413 size_t ncap = buf.length * 2 + 128 + n;
5414 char[] nbuf = new char[ncap];
5415 memcpy(nbuf.ptr, buf.ptr, pos);
5416 buf = nbuf;
5417 // Allocated a new buffer. No one else knows about it.
5418 safeAppend = true;
5421 void putchar(char c) @trusted
5423 reserve(1);
5424 buf.ptr[pos++] = c;
5426 void putdchar(dchar dc) @trusted
5428 import std.utf : encode, UseReplacementDchar;
5430 char[4] ubuf;
5431 immutable size = encode!(UseReplacementDchar.yes)(ubuf, dc);
5432 reserve(size);
5433 foreach (c; ubuf)
5434 buf.ptr[pos++] = c;
5436 void putonly(char[] b) @trusted
5438 import core.stdc.string : memcpy;
5439 assert(pos == 0); // assume this is the only put call
5440 if (reserveWithoutAllocating(b.length))
5441 memcpy(buf.ptr + pos, b.ptr, b.length);
5442 else
5443 buf = b.dup;
5444 pos = b.length;
5448 // Private implementation of readln
5449 private size_t readlnImpl(FILE* fps, ref char[] buf, dchar terminator, File.Orientation orientation)
5451 version (DIGITAL_MARS_STDIO)
5453 _FLOCK(fps);
5454 scope(exit) _FUNLOCK(fps);
5456 /* Since fps is now locked, we can create an "unshared" version
5457 * of fp.
5459 auto fp = cast(_iobuf*) fps;
5461 ReadlnAppender app;
5462 app.initialize(buf);
5464 if (__fhnd_info[fp._file] & FHND_WCHAR)
5465 { /* Stream is in wide characters.
5466 * Read them and convert to chars.
5468 static assert(wchar_t.sizeof == 2);
5469 for (int c = void; (c = _FGETWC(fp)) != -1; )
5471 if ((c & ~0x7F) == 0)
5473 app.putchar(cast(char) c);
5474 if (c == terminator)
5475 break;
5477 else
5479 if (c >= 0xD800 && c <= 0xDBFF)
5481 int c2 = void;
5482 if ((c2 = _FGETWC(fp)) != -1 ||
5483 c2 < 0xDC00 && c2 > 0xDFFF)
5485 StdioException("unpaired UTF-16 surrogate");
5487 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5489 app.putdchar(cast(dchar) c);
5492 if (ferror(fps))
5493 StdioException();
5496 else if (fp._flag & _IONBF)
5498 /* Use this for unbuffered I/O, when running
5499 * across buffer boundaries, or for any but the common
5500 * cases.
5503 int c;
5504 while ((c = _FGETC(fp)) != -1)
5506 app.putchar(cast(char) c);
5507 if (c == terminator)
5509 buf = app.data;
5510 return buf.length;
5515 if (ferror(fps))
5516 StdioException();
5518 else
5520 int u = fp._cnt;
5521 char* p = fp._ptr;
5522 int i;
5523 if (fp._flag & _IOTRAN)
5524 { /* Translated mode ignores \r and treats ^Z as end-of-file
5526 char c;
5527 while (1)
5529 if (i == u) // if end of buffer
5530 goto L1; // give up
5531 c = p[i];
5532 i++;
5533 if (c != '\r')
5535 if (c == terminator)
5536 break;
5537 if (c != 0x1A)
5538 continue;
5539 goto L1;
5541 else
5542 { if (i != u && p[i] == terminator)
5543 break;
5544 goto L1;
5547 app.putonly(p[0 .. i]);
5548 app.buf[i - 1] = cast(char) terminator;
5549 if (terminator == '\n' && c == '\r')
5550 i++;
5552 else
5554 while (1)
5556 if (i == u) // if end of buffer
5557 goto L1; // give up
5558 auto c = p[i];
5559 i++;
5560 if (c == terminator)
5561 break;
5563 app.putonly(p[0 .. i]);
5565 fp._cnt -= i;
5566 fp._ptr += i;
5569 buf = app.data;
5570 return buf.length;
5572 else version (MICROSOFT_STDIO)
5574 _FLOCK(fps);
5575 scope(exit) _FUNLOCK(fps);
5577 /* Since fps is now locked, we can create an "unshared" version
5578 * of fp.
5580 auto fp = cast(_iobuf*) fps;
5582 ReadlnAppender app;
5583 app.initialize(buf);
5585 int c;
5586 while ((c = _FGETC(fp)) != -1)
5588 app.putchar(cast(char) c);
5589 if (c == terminator)
5591 buf = app.data;
5592 return buf.length;
5597 if (ferror(fps))
5598 StdioException();
5599 buf = app.data;
5600 return buf.length;
5602 else static if (__traits(compiles, core.sys.posix.stdio.getdelim))
5604 import core.stdc.stdlib : free;
5605 import core.stdc.wchar_ : fwide;
5607 if (orientation == File.Orientation.wide)
5609 /* Stream is in wide characters.
5610 * Read them and convert to chars.
5612 _FLOCK(fps);
5613 scope(exit) _FUNLOCK(fps);
5614 auto fp = cast(_iobuf*) fps;
5615 version (Windows)
5617 buf.length = 0;
5618 for (int c = void; (c = _FGETWC(fp)) != -1; )
5620 if ((c & ~0x7F) == 0)
5621 { buf ~= c;
5622 if (c == terminator)
5623 break;
5625 else
5627 if (c >= 0xD800 && c <= 0xDBFF)
5629 int c2 = void;
5630 if ((c2 = _FGETWC(fp)) != -1 ||
5631 c2 < 0xDC00 && c2 > 0xDFFF)
5633 StdioException("unpaired UTF-16 surrogate");
5635 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5637 import std.utf : encode;
5638 encode(buf, c);
5641 if (ferror(fp))
5642 StdioException();
5643 return buf.length;
5645 else version (Posix)
5647 buf.length = 0;
5648 for (int c; (c = _FGETWC(fp)) != -1; )
5650 import std.utf : encode;
5652 if ((c & ~0x7F) == 0)
5653 buf ~= cast(char) c;
5654 else
5655 encode(buf, cast(dchar) c);
5656 if (c == terminator)
5657 break;
5659 if (ferror(fps))
5660 StdioException();
5661 return buf.length;
5663 else
5665 static assert(0);
5669 static char *lineptr = null;
5670 static size_t n = 0;
5671 scope(exit)
5673 if (n > 128 * 1024)
5675 // Bound memory used by readln
5676 free(lineptr);
5677 lineptr = null;
5678 n = 0;
5682 auto s = core.sys.posix.stdio.getdelim(&lineptr, &n, terminator, fps);
5683 if (s < 0)
5685 if (ferror(fps))
5686 StdioException();
5687 buf.length = 0; // end of file
5688 return 0;
5691 if (s <= buf.length)
5693 buf = buf[0 .. s];
5694 buf[] = lineptr[0 .. s];
5696 else
5698 buf = lineptr[0 .. s].dup;
5700 return s;
5702 else // version (NO_GETDELIM)
5704 import core.stdc.wchar_ : fwide;
5706 _FLOCK(fps);
5707 scope(exit) _FUNLOCK(fps);
5708 auto fp = cast(_iobuf*) fps;
5709 if (orientation == File.Orientation.wide)
5711 /* Stream is in wide characters.
5712 * Read them and convert to chars.
5714 version (Windows)
5716 buf.length = 0;
5717 for (int c; (c = _FGETWC(fp)) != -1; )
5719 if ((c & ~0x7F) == 0)
5720 { buf ~= c;
5721 if (c == terminator)
5722 break;
5724 else
5726 if (c >= 0xD800 && c <= 0xDBFF)
5728 int c2 = void;
5729 if ((c2 = _FGETWC(fp)) != -1 ||
5730 c2 < 0xDC00 && c2 > 0xDFFF)
5732 StdioException("unpaired UTF-16 surrogate");
5734 c = ((c - 0xD7C0) << 10) + (c2 - 0xDC00);
5736 import std.utf : encode;
5737 encode(buf, c);
5740 if (ferror(fp))
5741 StdioException();
5742 return buf.length;
5744 else version (Posix)
5746 import std.utf : encode;
5747 buf.length = 0;
5748 for (int c; (c = _FGETWC(fp)) != -1; )
5750 if ((c & ~0x7F) == 0)
5751 buf ~= cast(char) c;
5752 else
5753 encode(buf, cast(dchar) c);
5754 if (c == terminator)
5755 break;
5757 if (ferror(fps))
5758 StdioException();
5759 return buf.length;
5761 else
5763 static assert(0);
5767 // Narrow stream
5768 // First, fill the existing buffer
5769 for (size_t bufPos = 0; bufPos < buf.length; )
5771 immutable c = _FGETC(fp);
5772 if (c == -1)
5774 buf.length = bufPos;
5775 goto endGame;
5777 buf[bufPos++] = cast(char) c;
5778 if (c == terminator)
5780 // No need to test for errors in file
5781 buf.length = bufPos;
5782 return bufPos;
5785 // Then, append to it
5786 for (int c; (c = _FGETC(fp)) != -1; )
5788 buf ~= cast(char) c;
5789 if (c == terminator)
5791 // No need to test for errors in file
5792 return buf.length;
5796 endGame:
5797 if (ferror(fps))
5798 StdioException();
5799 return buf.length;
5803 @system unittest
5805 static import std.file;
5806 auto deleteme = testFilename();
5807 scope(exit) std.file.remove(deleteme);
5809 std.file.write(deleteme, "abcd\n0123456789abcde\n1234\n");
5810 File f = File(deleteme, "rb");
5812 char[] ln = new char[2];
5813 f.readln(ln);
5815 assert(ln == "abcd\n");
5816 char[] t = ln[0 .. 2];
5817 t ~= 't';
5818 assert(t == "abt");
5819 // https://issues.dlang.org/show_bug.cgi?id=13856: ln stomped to "abtd"
5820 assert(ln == "abcd\n");
5822 // it can also stomp the array length
5823 ln = new char[4];
5824 f.readln(ln);
5825 assert(ln == "0123456789abcde\n");
5827 char[100] buf;
5828 ln = buf[];
5829 f.readln(ln);
5830 assert(ln == "1234\n");
5831 assert(ln.ptr == buf.ptr); // avoid allocation, buffer is good enough
5834 /** Experimental network access via the File interface
5836 Opens a TCP connection to the given host and port, then returns
5837 a File struct with read and write access through the same interface
5838 as any other file (meaning writef and the byLine ranges work!).
5840 Authors:
5841 Adam D. Ruppe
5843 Bugs:
5844 Only works on Linux
5846 version (linux)
5848 File openNetwork(string host, ushort port)
5850 import core.stdc.string : memcpy;
5851 import core.sys.posix.arpa.inet : htons;
5852 import core.sys.posix.netdb : gethostbyname;
5853 import core.sys.posix.netinet.in_ : sockaddr_in;
5854 static import core.sys.posix.unistd;
5855 static import sock = core.sys.posix.sys.socket;
5856 import std.conv : to;
5857 import std.exception : enforce;
5858 import std.internal.cstring : tempCString;
5860 auto h = enforce( gethostbyname(host.tempCString()),
5861 new StdioException("gethostbyname"));
5863 int s = sock.socket(sock.AF_INET, sock.SOCK_STREAM, 0);
5864 enforce(s != -1, new StdioException("socket"));
5866 scope(failure)
5868 // want to make sure it doesn't dangle if something throws. Upon
5869 // normal exit, the File struct's reference counting takes care of
5870 // closing, so we don't need to worry about success
5871 core.sys.posix.unistd.close(s);
5874 sockaddr_in addr;
5876 addr.sin_family = sock.AF_INET;
5877 addr.sin_port = htons(port);
5878 memcpy(&addr.sin_addr.s_addr, h.h_addr, h.h_length);
5880 enforce(sock.connect(s, cast(sock.sockaddr*) &addr, addr.sizeof) != -1,
5881 new StdioException("Connect failed"));
5883 File f;
5884 f.fdopen(s, "w+", host ~ ":" ~ to!string(port));
5885 return f;
5889 version (StdUnittest) private string testFilename(string file = __FILE__, size_t line = __LINE__) @safe
5891 import std.conv : text;
5892 import std.file : deleteme;
5893 import std.path : baseName;
5895 // filename intentionally contains non-ASCII (Russian) characters for
5896 // https://issues.dlang.org/show_bug.cgi?id=7648
5897 return text(deleteme, "-детка.", baseName(file), ".", line);