1 // Written in the D programming language.
4 $(SCRIPT inhibitQuickIndex = 1;)
7 $(TR $(TH Category) $(TH Symbols))
8 $(TR $(TD File handles) $(TD
17 $(TR $(TD Reading) $(TD
23 $(TR $(TD Writing) $(TD
31 $(MYREF KeepTerminator)
33 $(MYREF StdioException)
37 Standard I/O functions that extend $(LINK2 https://dlang.org/phobos/core_stdc_stdio.html, core.stdc.stdio). $(B core.stdc.stdio)
38 is $(D_PARAM public)ally imported when importing $(B std.stdio).
40 There are three layers of I/O:
42 $(LI The lowest layer is the operating system layer. The two main schemes are Windows and Posix.)
43 $(LI C's $(TT stdio.h) which unifies the two operating system schemes.)
44 $(LI $(TT std.stdio), this module, unifies the various $(TT stdio.h) implementations into
45 a high level package for D programs.)
48 Source: $(PHOBOSSRC std/stdio.d)
49 Copyright: Copyright The D Language Foundation 2007-.
50 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
51 Authors: $(HTTP digitalmars.com, Walter Bright),
52 $(HTTP erdani.org, Andrei Alexandrescu),
55 CSTDIO=$(HTTP cplusplus.com/reference/cstdio/$1/, $1)
62 The three layers have many terms for their data structures and types.
63 Here we try to bring some sanity to them for the intrepid code spelunker.
69 A Windows handle is an opaque object of type HANDLE.
70 The `HANDLE` for standard devices can be retrieved with
71 Windows `GetStdHandle()`.
75 file descriptor, aka fileno, aka fildes
77 An int from 0..`FOPEN_MAX`, which is an index into some internal data
79 0 is for `stdin`, 1 for `stdout`, 2 for `stderr`.
80 Negative values usually indicate an error.
86 A struct that encapsulates the C library's view of the operating system
87 files. A `FILE` should only be referred to via a pointer.
91 A field of `FILE` which is the Posix file descriptor for Posix systems, and
92 and an index into an array of file `HANDLE`s for Windows.
93 This array is how Posix behavior is emulated on Windows.
94 For Digital Mars C, that array is `__osfhnd[]`, and is initialized
95 at program start by the C runtime library.
96 In this module, they are typed as `fileno_t`.
98 `stdin`, `stdout`, `stderr`
100 Global pointers to `FILE` representing standard input, output, and error streams.
101 Being global means there are synchronization issues when multiple threads
102 are doing I/O on the same streams.
108 import core
.stdc
.stddef
: wchar_t
;
109 public import core
.stdc
.stdio
;
110 import std
.algorithm
.mutation
: copy
;
111 import std
.meta
: allSatisfy
;
112 import std
.range
: ElementEncodingType
, empty
, front
, isBidirectionalRange
,
113 isInputRange
, isSomeFiniteCharInputRange
, put
;
114 import std
.traits
: isSomeChar
, isSomeString
, Unqual
;
115 import std
.typecons
: Flag
, No
, Yes
;
118 If flag `KeepTerminator` is set to `KeepTerminator.yes`, then the delimiter
119 is included in the strings returned.
121 alias KeepTerminator
= Flag
!"keepTerminator";
123 version (CRuntime_Microsoft
)
126 else version (CRuntime_Glibc
)
129 else version (CRuntime_Bionic
)
131 version = GENERIC_IO
;
133 else version (CRuntime_Musl
)
135 version = GENERIC_IO
;
137 else version (CRuntime_UClibc
)
139 version = GENERIC_IO
;
143 version = GENERIC_IO
;
148 version = GENERIC_IO
;
153 version = GENERIC_IO
;
156 else version (WatchOS
)
158 version = GENERIC_IO
;
161 else version (FreeBSD
)
163 version = GENERIC_IO
;
165 else version (NetBSD
)
167 version = GENERIC_IO
;
169 else version (OpenBSD
)
171 version = GENERIC_IO
;
173 else version (DragonFlyBSD
)
175 version = GENERIC_IO
;
177 else version (Solaris
)
179 version = GENERIC_IO
;
183 static assert(0, "unsupported operating system");
186 // Character type used for operating system filesystem APIs
189 private alias FSChar
= wchar;
193 private alias FSChar
= char;
196 private alias fileno_t
= int; // file descriptor, fildes, fileno
200 // core.stdc.stdio.fopen expects file names to be
201 // encoded in CP_ACP on Windows instead of UTF-8.
202 /+ Waiting for druntime pull 299
204 extern (C
) nothrow @nogc FILE
* _wfopen(scope const wchar* filename
, scope const wchar* mode
);
205 extern (C
) nothrow @nogc FILE
* _wfreopen(scope const wchar* filename
, scope const wchar* mode
, FILE
* fp
);
207 import core
.sys
.windows
.basetsd
: HANDLE
;
212 static import core
.sys
.posix
.stdio
; // getdelim, flockfile
215 version (CRuntime_Microsoft
)
217 private alias _FPUTC
= _fputc_nolock
;
218 private alias _FPUTWC
= _fputwc_nolock
;
219 private alias _FGETC
= _fgetc_nolock
;
220 private alias _FGETWC
= _fgetwc_nolock
;
221 private alias _FLOCK
= _lock_file
;
222 private alias _FUNLOCK
= _unlock_file
;
224 else version (CRuntime_Glibc
)
226 private alias _FPUTC
= fputc_unlocked
;
227 private alias _FPUTWC
= fputwc_unlocked
;
228 private alias _FGETC
= fgetc_unlocked
;
229 private alias _FGETWC
= fgetwc_unlocked
;
230 private alias _FLOCK
= core
.sys
.posix
.stdio
.flockfile
;
231 private alias _FUNLOCK
= core
.sys
.posix
.stdio
.funlockfile
;
233 else version (GENERIC_IO
)
240 static import core
.stdc
.wchar_
;
242 pragma(mangle
, fputc
.mangleof
) int _FPUTC(int c
, _iobuf
* fp
);
243 pragma(mangle
, core
.stdc
.wchar_
.fputwc
.mangleof
) int _FPUTWC(wchar_t c
, _iobuf
* fp
);
244 pragma(mangle
, fgetc
.mangleof
) int _FGETC(_iobuf
* fp
);
245 pragma(mangle
, core
.stdc
.wchar_
.fgetwc
.mangleof
) int _FGETWC(_iobuf
* fp
);
250 private alias _FLOCK
= core
.sys
.posix
.stdio
.flockfile
;
251 private alias _FUNLOCK
= core
.sys
.posix
.stdio
.funlockfile
;
255 static assert(0, "don't know how to lock files on GENERIC_IO");
260 static assert(0, "unsupported C I/O system");
263 private extern (C
) @nogc nothrow
265 pragma(mangle
, _FPUTC
.mangleof
) int trustedFPUTC(int ch
, _iobuf
* h
) @trusted;
266 pragma(mangle
, _FPUTWC
.mangleof
) int trustedFPUTWC(wchar_t ch
, _iobuf
* h
) @trusted;
269 //------------------------------------------------------------------------------
270 private struct ByRecordImpl(Fields
...)
273 import std
.typecons
: Tuple
;
277 Tuple
!(Fields
) current
;
281 this(File f
, string format
)
285 this.format
= format
;
286 popFront(); // prime the range
289 /// Range primitive implementations.
290 @property bool empty()
296 @property ref Tuple
!(Fields
) front()
304 import std
.conv
: text
;
305 import std
.exception
: enforce
;
306 import std
.format
.read
: formattedRead
;
307 import std
.string
: chomp
;
309 enforce(file
.isOpen
, "ByRecord: File must be open");
318 formattedRead(line
, format
, ¤t
);
319 enforce(line
.empty
, text("Leftover characters in record: `",
325 template byRecord(Fields
...)
327 auto byRecord(File f
, string format
)
329 return typeof(return)(f
, format
);
334 Encapsulates a `FILE*`. Generally D does not attempt to provide
335 thin wrappers over equivalent functions in the C standard library, but
336 manipulating `FILE*` values directly is unsafe and error-prone in
337 many ways. The `File` type ensures safe manipulation, automatic
338 file closing, and a lot of convenience.
340 The underlying `FILE*` handle is maintained in a reference-counted
341 manner, such that as soon as the last `File` variable bound to a
342 given `FILE*` goes out of scope, the underlying `FILE*` is
343 automatically closed.
350 void main(string[] args)
352 auto f = File("test.txt", "w"); // open for writing
356 auto g = f; // now g and f write to the same file
357 // internal reference count is 2
358 g.write(", ", args[1]);
359 // g exits scope, reference count decreases to 1
362 // f exits scope, reference count falls to zero,
363 // underlying `FILE*` is closed.
375 import core
.atomic
: atomicOp
, atomicStore
, atomicLoad
;
376 import std
.range
.primitives
: ElementEncodingType
;
377 import std
.traits
: isScalarType
, isArray
;
378 enum Orientation
{ unknown
, narrow
, wide
}
382 FILE
* handle
= null; // Is null iff this Impl is closed by another File
383 shared uint refs
= uint.max
/ 2;
384 bool isPopened
; // true iff the stream has been created by popen()
385 Orientation orientation
;
388 private string _name
;
390 package this(FILE
* handle
, string name
, uint refs
= 1, bool isPopened
= false) @trusted @nogc nothrow
392 import core
.stdc
.stdlib
: malloc
;
395 _p
= cast(Impl
*) malloc(Impl
.sizeof
);
398 import core
.exception
: onOutOfMemoryError
;
399 onOutOfMemoryError();
401 initImpl(handle
, name
, refs
, isPopened
);
404 private void initImpl(FILE
* handle
, string name
, uint refs
= 1, bool isPopened
= false) @nogc nothrow pure @safe
408 atomicStore(_p
.refs
, refs
);
409 _p
.isPopened
= isPopened
;
410 _p
.orientation
= Orientation
.unknown
;
415 Constructor taking the name of the file to open and the open mode.
417 Copying one `File` object to another results in the two `File`
418 objects referring to the same underlying file.
420 The destructor automatically closes the file as soon as no `File`
421 object refers to it anymore.
424 name = range or string representing the file _name
425 stdioOpenmode = range or string represting the open mode
426 (with the same semantics as in the C standard library
427 $(CSTDIO fopen) function)
429 Throws: `ErrnoException` if the file could not be opened.
431 this(string name
, scope const(char)[] stdioOpenmode
= "rb") @safe
433 import std
.conv
: text
;
434 import std
.exception
: errnoEnforce
;
436 this(errnoEnforce(_fopen(name
, stdioOpenmode
),
437 text("Cannot open file `", name
, "' in mode `",
438 stdioOpenmode
, "'")),
441 // MSVCRT workaround (https://issues.dlang.org/show_bug.cgi?id=14422)
442 version (CRuntime_Microsoft
)
444 setAppendWin(stdioOpenmode
);
449 this(R1
, R2
)(R1 name
)
450 if (isSomeFiniteCharInputRange
!R1
)
452 import std
.conv
: to
;
453 this(name
.to
!string
, "rb");
457 this(R1
, R2
)(R1 name
, R2 mode
)
458 if (isSomeFiniteCharInputRange
!R1
&&
459 isSomeFiniteCharInputRange
!R2
)
461 import std
.conv
: to
;
462 this(name
.to
!string
, mode
.to
!string
);
467 static import std
.file
;
468 import std
.utf
: byChar
;
469 auto deleteme
= testFilename();
470 auto f
= File(deleteme
.byChar
, "w".byChar
);
472 std
.file
.remove(deleteme
);
480 this(this) @safe pure nothrow @nogc
483 assert(atomicLoad(_p
.refs
));
484 atomicOp
!"+="(_p
.refs
, 1);
488 Assigns a file to another. The target of the assignment gets detached
489 from whatever file it was attached to, and attaches itself to the new
492 ref File
opAssign(File rhs
) @safe return
494 import std
.algorithm
.mutation
: swap
;
500 // https://issues.dlang.org/show_bug.cgi?id=20129
504 aa
.require(0, File
.init
);
508 Detaches from the current file (throwing on failure), and then attempts to
509 _open file `name` with mode `stdioOpenmode`. The mode has the
510 same semantics as in the C standard library $(CSTDIO fopen) function.
512 Throws: `ErrnoException` in case of error.
514 void open(string name
, scope const(char)[] stdioOpenmode
= "rb") @trusted
516 resetFile(name
, stdioOpenmode
, false);
519 // https://issues.dlang.org/show_bug.cgi?id=20585
524 f
.open("doesn't exist");
531 f
.close(); // to check not crash here
534 private void resetFile(string name
, scope const(char)[] stdioOpenmode
, bool isPopened
) @trusted
536 import core
.stdc
.stdlib
: malloc
;
537 import std
.exception
: enforce
;
538 import std
.conv
: text
;
539 import std
.exception
: errnoEnforce
;
551 errnoEnforce(handle
= _popen(name
, stdioOpenmode
),
552 "Cannot run command `"~name
~"'");
556 errnoEnforce(handle
= _fopen(name
, stdioOpenmode
),
557 text("Cannot open file `", name
, "' in mode `",
558 stdioOpenmode
, "'"));
563 assert(isPopened
== false);
564 errnoEnforce(handle
= _fopen(name
, stdioOpenmode
),
565 text("Cannot open file `", name
, "' in mode `",
566 stdioOpenmode
, "'"));
568 _p
= cast(Impl
*) enforce(malloc(Impl
.sizeof
), "Out of memory");
569 initImpl(handle
, name
, 1, isPopened
);
570 version (CRuntime_Microsoft
)
572 setAppendWin(stdioOpenmode
);
576 private void closeHandles() @trusted
579 import std
.exception
: errnoEnforce
;
583 import core
.sys
.posix
.stdio
: pclose
;
584 import std
.format
: format
;
588 auto res
= pclose(_p
.handle
);
589 errnoEnforce(res
!= -1,
590 "Could not close pipe `"~_name
~"'");
597 auto handle
= _p
.handle
;
599 // fclose disassociates the FILE* even in case of error (https://issues.dlang.org/show_bug.cgi?id=19751)
600 errnoEnforce(.fclose(handle
) == 0,
601 "Could not close file `"~_name
~"'");
605 version (CRuntime_Microsoft
)
607 private void setAppendWin(scope const(char)[] stdioOpenmode
) @safe
610 foreach (c
; stdioOpenmode
)
616 if (append
&& !update
)
622 Reuses the `File` object to either open a different file, or change
623 the file mode. If `name` is `null`, the mode of the currently open
624 file is changed; otherwise, a new file is opened, reusing the C
625 `FILE*`. The function has the same semantics as in the C standard
626 library $(CSTDIO freopen) function.
628 Note: Calling `reopen` with a `null` `name` is not implemented
631 Throws: `ErrnoException` in case of error.
633 void reopen(string name
, scope const(char)[] stdioOpenmode
= "rb") @trusted
635 import std
.conv
: text
;
636 import std
.exception
: enforce
, errnoEnforce
;
637 import std
.internal
.cstring
: tempCString
;
639 enforce(isOpen
, "Attempting to reopen() an unopened file");
641 auto namez
= (name
== null ? _name
: name
).tempCString
!FSChar();
642 auto modez
= stdioOpenmode
.tempCString
!FSChar();
644 FILE
* fd
= _p
.handle
;
646 fd
= _wfreopen(namez
, modez
, fd
);
648 fd
= freopen(namez
, modez
, fd
);
650 errnoEnforce(fd
, name
651 ?
text("Cannot reopen file `", name
, "' in mode `", stdioOpenmode
, "'")
652 : text("Cannot reopen file in mode `", stdioOpenmode
, "'"));
658 @safe unittest // Test changing filename
660 import std
.exception
: assertThrown
, assertNotThrown
;
661 static import std
.file
;
663 auto deleteme
= testFilename();
664 std
.file
.write(deleteme
, "foo");
665 scope(exit
) std
.file
.remove(deleteme
);
666 auto f
= File(deleteme
);
667 assert(f
.readln() == "foo");
669 auto deleteme2
= testFilename();
670 std
.file
.write(deleteme2
, "bar");
671 scope(exit
) std
.file
.remove(deleteme2
);
673 assert(f
.name
== deleteme2
);
674 assert(f
.readln() == "bar");
678 version (CRuntime_Microsoft
) {} else // Not implemented
679 @safe unittest // Test changing mode
681 import std
.exception
: assertThrown
, assertNotThrown
;
682 static import std
.file
;
684 auto deleteme
= testFilename();
685 std
.file
.write(deleteme
, "foo");
686 scope(exit
) std
.file
.remove(deleteme
);
687 auto f
= File(deleteme
, "r+");
688 assert(f
.readln() == "foo");
694 assert(f
.name
== deleteme
);
696 assert(std
.file
.readText(deleteme
) == "barbaz");
700 Detaches from the current file (throwing on failure), and then runs a command
701 by calling the C standard library function $(HTTP
702 pubs.opengroup.org/onlinepubs/7908799/xsh/popen.html, popen).
704 Throws: `ErrnoException` in case of error.
706 version (Posix
) void popen(string command
, scope const(char)[] stdioOpenmode
= "r") @safe
708 resetFile(command
, stdioOpenmode
,true);
712 First calls `detach` (throwing on failure), then attempts to
713 associate the given file descriptor with the `File`, and sets the file's name to `null`.
715 The mode must be compatible with the mode of the file descriptor.
717 Throws: `ErrnoException` in case of error.
719 fd = File descriptor to associate with this `File`.
720 stdioOpenmode = Mode to associate with this File. The mode has the same
721 semantics as in the POSIX library function $(HTTP
722 pubs.opengroup.org/onlinepubs/7908799/xsh/fdopen.html, fdopen)
723 and must be compatible with `fd`.
725 void fdopen(int fd
, scope const(char)[] stdioOpenmode
= "rb") @safe
727 fdopen(fd
, stdioOpenmode
, null);
730 package void fdopen(int fd
, scope const(char)[] stdioOpenmode
, string name
) @trusted
732 import std
.exception
: errnoEnforce
;
733 import std
.internal
.cstring
: tempCString
;
735 auto modez
= stdioOpenmode
.tempCString();
738 version (CRuntime_Microsoft
)
740 auto fp
= _fdopen(fd
, modez
);
745 import core
.sys
.posix
.stdio
: fdopen
;
746 auto fp
= fdopen(fd
, modez
);
750 static assert(0, "no fdopen() available");
752 this = File(fp
, name
);
755 // Declare a dummy HANDLE to allow generating documentation
756 // for Windows-only methods.
757 version (StdDdoc
) { version (Windows
) {} else alias HANDLE
= int; }
760 First calls `detach` (throwing on failure), and then attempts to
761 associate the given Windows `HANDLE` with the `File`. The mode must
762 be compatible with the access attributes of the handle. Windows only.
764 Throws: `ErrnoException` in case of error.
767 void windowsHandleOpen(HANDLE handle
, scope const(char)[] stdioOpenmode
);
770 void windowsHandleOpen(HANDLE handle
, scope const(char)[] stdioOpenmode
)
772 import core
.stdc
.stdint
: intptr_t
;
773 import std
.exception
: errnoEnforce
;
774 import std
.format
: format
;
776 // Create file descriptors from the handles
779 foreach (c
; stdioOpenmode
)
782 case 'r': mode |
= _O_RDONLY
; break;
783 case '+': mode
&=~_O_RDONLY
; break;
784 case 'a': mode |
= _O_APPEND
; break;
785 case 'b': mode |
= _O_BINARY
; break;
786 case 't': mode |
= _O_TEXT
; break;
787 case ',': break modeLoop
;
791 auto fd
= _open_osfhandle(cast(intptr_t
) handle
, mode
);
793 errnoEnforce(fd
>= 0, "Cannot open Windows HANDLE");
794 fdopen(fd
, stdioOpenmode
, "HANDLE(%s)".format(handle
));
798 /** Returns `true` if the file is opened. */
799 @property bool isOpen() const @safe pure nothrow
801 return _p
!is null && _p
.handle
;
805 Returns `true` if the file is at end (see $(CSTDIO feof)).
807 Throws: `Exception` if the file is not opened.
809 @property bool eof() const @trusted pure
811 import std
.exception
: enforce
;
813 enforce(_p
&& _p
.handle
, "Calling eof() against an unopened file.");
814 return .feof(cast(FILE
*) _p
.handle
) != 0;
818 Returns the name last used to initialize this `File`, if any.
820 Some functions that create or initialize the `File` set the name field to `null`.
821 Examples include $(LREF tmpfile), $(LREF wrapFile), and $(LREF fdopen). See the
822 documentation of those functions for details.
824 Returns: The name last used to initialize this this file, or `null` otherwise.
826 @property string
name() const @safe pure nothrow return
832 If the file is closed or not yet opened, returns `true`. Otherwise, returns
833 $(CSTDIO ferror) for the file handle.
835 @property bool error() const @trusted pure nothrow
837 return !isOpen ||
.ferror(cast(FILE
*) _p
.handle
);
842 // https://issues.dlang.org/show_bug.cgi?id=12349
843 static import std
.file
;
844 auto deleteme
= testFilename();
845 auto f
= File(deleteme
, "w");
846 scope(exit
) std
.file
.remove(deleteme
);
853 Detaches from the underlying file. If the sole owner, calls `close`.
855 Throws: `ErrnoException` on failure if closing the file.
857 void detach() @trusted
859 import core
.stdc
.stdlib
: free
;
862 scope(exit
) _p
= null;
864 if (atomicOp
!"-="(_p
.refs
, 1) == 0)
866 scope(exit
) free(_p
);
873 static import std
.file
;
875 auto deleteme
= testFilename();
876 scope(exit
) std
.file
.remove(deleteme
);
877 auto f
= File(deleteme
, "w");
882 assert(f
._p
.refs
== 1);
887 If the file was closed or not yet opened, succeeds vacuously. Otherwise
888 closes the file (by calling $(CSTDIO fclose)),
889 throwing on error. Even if an exception is thrown, afterwards the $(D
890 File) object is empty. This is different from `detach` in that it
891 always closes the file; consequently, all other `File` objects
892 referring to the same handle will see a closed file henceforth.
894 Throws: `ErrnoException` on error.
896 void close() @trusted
898 import core
.stdc
.stdlib
: free
;
899 import std
.exception
: errnoEnforce
;
901 if (!_p
) return; // succeed vacuously
904 if (atomicOp
!"-="(_p
.refs
, 1) == 0)
906 _p
= null; // start a new life
908 if (!_p
.handle
) return; // Impl is closed by another File
910 scope(exit
) _p
.handle
= null; // nullify the handle anyway
915 If the file is closed or not yet opened, succeeds vacuously. Otherwise, returns
916 $(CSTDIO clearerr) for the file handle.
918 void clearerr() @safe pure nothrow
920 _p
is null || _p
.handle
is null ||
921 .clearerr(_p
.handle
);
925 Flushes the C `FILE` buffers.
927 Calls $(CSTDIO fflush) for the file handle.
929 Throws: `Exception` if the file is not opened or if the call to `fflush` fails.
931 void flush() @trusted
933 import std
.exception
: enforce
, errnoEnforce
;
935 enforce(isOpen
, "Attempting to flush() in an unopened file");
936 errnoEnforce(.fflush(_p
.handle
) == 0);
941 // https://issues.dlang.org/show_bug.cgi?id=12349
942 import std
.exception
: assertThrown
;
943 static import std
.file
;
945 auto deleteme
= testFilename();
946 auto f
= File(deleteme
, "w");
947 scope(exit
) std
.file
.remove(deleteme
);
950 assertThrown(f
.flush());
954 Forces any data buffered by the OS to be written to disk.
955 Call $(LREF flush) before calling this function to flush the C `FILE` buffers first.
958 $(HTTP msdn.microsoft.com/en-us/library/windows/desktop/aa364439%28v=vs.85%29.aspx,
959 `FlushFileBuffers`) on Windows,
960 $(HTTP developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html,
961 `F_FULLFSYNC fcntl`) on Darwin and
962 $(HTTP pubs.opengroup.org/onlinepubs/7908799/xsh/fsync.html,
963 `fsync`) on POSIX for the file handle.
965 Throws: `Exception` if the file is not opened or if the OS call fails.
969 import std
.exception
: enforce
;
971 enforce(isOpen
, "Attempting to sync() an unopened file");
975 import core
.sys
.windows
.winbase
: FlushFileBuffers
;
976 wenforce(FlushFileBuffers(windowsHandle
), "FlushFileBuffers failed");
978 else version (Darwin
)
980 import core
.sys
.darwin
.fcntl
: fcntl
, F_FULLFSYNC
;
981 import std
.exception
: errnoEnforce
;
982 errnoEnforce(fcntl(fileno
, F_FULLFSYNC
, 0) != -1, "fcntl failed");
986 import core
.sys
.posix
.unistd
: fsync
;
987 import std
.exception
: errnoEnforce
;
988 errnoEnforce(fsync(fileno
) == 0, "fsync failed");
993 Calls $(CSTDIO fread) for the
994 file handle. The number of items to read and the size of
995 each item is inferred from the size and type of the input array, respectively.
997 Returns: The slice of `buffer` containing the data that was actually read.
998 This will be shorter than `buffer` if EOF was reached before the buffer
999 could be filled. If the buffer is empty, it will be returned.
1001 Throws: `ErrnoException` if the file is not opened or the call to `fread` fails.
1003 `rawRead` always reads in binary mode on Windows.
1005 T
[] rawRead(T
)(T
[] buffer
)
1007 import std
.exception
: enforce
, errnoEnforce
;
1011 enforce(isOpen
, "Attempting to read from an unopened file");
1014 immutable fileno_t fd
= .fileno(_p
.handle
);
1015 immutable mode
= ._setmode(fd
, _O_BINARY
);
1016 scope(exit
) ._setmode(fd
, mode
);
1018 immutable freadResult
= trustedFread(_p
.handle
, buffer
);
1019 assert(freadResult
<= buffer
.length
); // fread return guarantee
1020 if (freadResult
!= buffer
.length
) // error or eof
1022 errnoEnforce(!error
);
1023 return buffer
[0 .. freadResult
];
1031 static import std
.file
;
1033 auto testFile
= std
.file
.deleteme();
1034 std
.file
.write(testFile
, "\r\n\n\r\n");
1035 scope(exit
) std
.file
.remove(testFile
);
1037 auto f
= File(testFile
, "r");
1038 auto buf
= f
.rawRead(new char[5]);
1040 assert(buf
== "\r\n\n\r\n");
1043 // https://issues.dlang.org/show_bug.cgi?id=24685
1044 static assert(!__traits(compiles
, (File f
) @safe { int*[1] bar
; f
.rawRead(bar
[]); }));
1046 // https://issues.dlang.org/show_bug.cgi?id=21729
1049 import std
.exception
: assertThrown
;
1053 assertThrown(f
.rawRead(u
));
1056 // https://issues.dlang.org/show_bug.cgi?id=21728
1059 static if (__traits(compiles
, { import std
.process
: pipe
; })) // not available for iOS
1061 import std
.process
: pipe
;
1062 import std
.exception
: assertThrown
;
1067 assertThrown(p
.readEnd
.rawRead(u
));
1071 // https://issues.dlang.org/show_bug.cgi?id=13893
1074 import std
.exception
: assertNotThrown
;
1078 assertNotThrown(f
.rawRead(u
));
1082 Calls $(CSTDIO fwrite) for the file
1083 handle. The number of items to write and the size of each
1084 item is inferred from the size and type of the input array, respectively. An
1085 error is thrown if the buffer could not be written in its entirety.
1087 `rawWrite` always writes in binary mode on Windows.
1089 Throws: `ErrnoException` if the file is not opened or if the call to `fwrite` fails.
1091 void rawWrite(T
)(in T
[] buffer
)
1093 import std
.conv
: text
;
1094 import std
.exception
: errnoEnforce
;
1098 immutable fileno_t fd
= .fileno(_p
.handle
);
1099 immutable oldMode
= ._setmode(fd
, _O_BINARY
);
1101 if (oldMode
!= _O_BINARY
)
1103 // need to flush the data that was written with the original mode
1104 ._setmode(fd
, oldMode
);
1105 flush(); // before changing translation mode ._setmode(fd, _O_BINARY);
1106 ._setmode(fd
, _O_BINARY
);
1111 if (oldMode
!= _O_BINARY
)
1114 ._setmode(fd
, oldMode
);
1119 auto result
= trustedFwrite(_p
.handle
, buffer
);
1120 if (result
== result
.max
) result
= 0;
1121 errnoEnforce(result
== buffer
.length
,
1122 text("Wrote ", result
, " instead of ", buffer
.length
,
1123 " objects of type ", T
.stringof
, " to file `",
1130 static import std
.file
;
1132 auto testFile
= std
.file
.deleteme();
1133 auto f
= File(testFile
, "w");
1134 scope(exit
) std
.file
.remove(testFile
);
1136 f
.rawWrite("\r\n\n\r\n");
1138 assert(std
.file
.read(testFile
) == "\r\n\n\r\n");
1142 Calls $(CSTDIO fseek)
1143 for the file handle to move its position indicator.
1146 offset = Binary files: Number of bytes to offset from origin.$(BR)
1147 Text files: Either zero, or a value returned by $(LREF tell).
1148 origin = Binary files: Position used as reference for the offset, must be
1149 one of $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio),
1150 $(REF_ALTTEXT SEEK_CUR, SEEK_CUR, core,stdc,stdio) or
1151 $(REF_ALTTEXT SEEK_END, SEEK_END, core,stdc,stdio).$(BR)
1152 Text files: Shall necessarily be
1153 $(REF_ALTTEXT SEEK_SET, SEEK_SET, core,stdc,stdio).
1155 Throws: `Exception` if the file is not opened.
1156 `ErrnoException` if the call to `fseek` fails.
1158 void seek(long offset
, int origin
= SEEK_SET
) @trusted
1160 import std
.conv
: to
, text
;
1161 import std
.exception
: enforce
, errnoEnforce
;
1163 // Some libc sanitize the whence input (e.g. glibc), but some don't,
1164 // e.g. Microsoft runtime crashes on an invalid origin,
1165 // and Musl additionally accept SEEK_DATA & SEEK_HOLE (Linux extension).
1166 // To provide a consistent behavior cross platform, we use the glibc check
1167 // See also https://issues.dlang.org/show_bug.cgi?id=19797
1168 enforce(origin
== SEEK_SET || origin
== SEEK_CUR || origin
== SEEK_END
,
1169 "Invalid `origin` argument passed to `seek`, must be one of: SEEK_SET, SEEK_CUR, SEEK_END");
1171 enforce(isOpen
, "Attempting to seek() in an unopened file");
1174 version (CRuntime_Microsoft
)
1176 alias fseekFun
= _fseeki64
;
1181 alias fseekFun
= fseek
;
1185 else version (Posix
)
1187 import core
.sys
.posix
.stdio
: fseeko
, off_t
;
1188 alias fseekFun
= fseeko
;
1190 errnoEnforce(fseekFun(_p
.handle
, to
!off_t(offset
), origin
) == 0,
1191 "Could not seek in file `"~_name
~"'");
1196 import std
.conv
: text
;
1197 static import std
.file
;
1198 import std
.exception
;
1200 auto deleteme
= testFilename();
1201 auto f
= File(deleteme
, "w+");
1202 scope(exit
) { f
.close(); std
.file
.remove(deleteme
); }
1203 f
.rawWrite("abcdefghijklmnopqrstuvwxyz");
1205 assert(f
.readln() == "hijklmnopqrstuvwxyz");
1207 version (CRuntime_Bionic
)
1208 auto bigOffset
= int.max
- 100;
1210 auto bigOffset
= cast(ulong) int.max
+ 100;
1212 assert(f
.tell
== bigOffset
, text(f
.tell
));
1213 // Uncomment the tests below only if you want to wait for
1215 // f.rawWrite("abcdefghijklmnopqrstuvwxyz");
1216 // f.seek(-3, SEEK_END);
1217 // assert(f.readln() == "xyz");
1219 assertThrown(f
.seek(0, ushort.max
));
1223 Calls $(CSTDIO ftell)
1224 for the managed file handle, which returns the current value of
1225 the position indicator of the file handle.
1227 Throws: `Exception` if the file is not opened.
1228 `ErrnoException` if the call to `ftell` fails.
1230 @property ulong tell() const @trusted
1232 import std
.exception
: enforce
, errnoEnforce
;
1234 enforce(isOpen
, "Attempting to tell() in an unopened file");
1237 version (CRuntime_Microsoft
)
1238 immutable result
= _ftelli64(cast(FILE
*) _p
.handle
);
1240 immutable result
= ftell(cast(FILE
*) _p
.handle
);
1242 else version (Posix
)
1244 import core
.sys
.posix
.stdio
: ftello
;
1245 immutable result
= ftello(cast(FILE
*) _p
.handle
);
1247 errnoEnforce(result
!= -1,
1248 "Query ftell() failed for file `"~_name
~"'");
1255 import std
.conv
: text
;
1256 static import std
.file
;
1258 auto testFile
= std
.file
.deleteme();
1259 std
.file
.write(testFile
, "abcdefghijklmnopqrstuvwqxyz");
1260 scope(exit
) { std
.file
.remove(testFile
); }
1262 auto f
= File(testFile
);
1263 auto a
= new ubyte[4];
1265 assert(f
.tell
== 4, text(f
.tell
));
1269 Calls $(CSTDIO rewind) for the file handle.
1271 Throws: `Exception` if the file is not opened.
1275 import std
.exception
: enforce
;
1277 enforce(isOpen
, "Attempting to rewind() an unopened file");
1282 Calls $(CSTDIO setvbuf) for the file handle.
1284 Throws: `Exception` if the file is not opened.
1285 `ErrnoException` if the call to `setvbuf` fails.
1287 void setvbuf(size_t size
, int mode
= _IOFBF
) @trusted
1289 import std
.exception
: enforce
, errnoEnforce
;
1291 enforce(isOpen
, "Attempting to call setvbuf() on an unopened file");
1292 errnoEnforce(.setvbuf(_p
.handle
, null, mode
, size
) == 0,
1293 "Could not set buffering for file `"~_name
~"'");
1297 Calls $(CSTDIO setvbuf) for the file handle.
1299 Throws: `Exception` if the file is not opened.
1300 `ErrnoException` if the call to `setvbuf` fails.
1302 void setvbuf(void[] buf
, int mode
= _IOFBF
) @trusted
1304 import std
.exception
: enforce
, errnoEnforce
;
1306 enforce(isOpen
, "Attempting to call setvbuf() on an unopened file");
1307 errnoEnforce(.setvbuf(_p
.handle
,
1308 cast(char*) buf
.ptr
, mode
, buf
.length
) == 0,
1309 "Could not set buffering for file `"~_name
~"'");
1315 import core
.sys
.windows
.winbase
: OVERLAPPED
;
1316 import core
.sys
.windows
.winnt
: BOOL
, ULARGE_INTEGER
;
1317 import std
.windows
.syserror
: wenforce
;
1319 private BOOL
lockImpl(alias F
, Flags
...)(ulong start
, ulong length
,
1322 if (!start
&& !length
)
1324 ULARGE_INTEGER liStart
= void, liLength
= void;
1325 liStart
.QuadPart
= start
;
1326 liLength
.QuadPart
= length
;
1327 OVERLAPPED overlapped
;
1328 overlapped
.Offset
= liStart
.LowPart
;
1329 overlapped
.OffsetHigh
= liStart
.HighPart
;
1330 overlapped
.hEvent
= null;
1331 return F(windowsHandle
, flags
, 0, liLength
.LowPart
,
1332 liLength
.HighPart
, &overlapped
);
1337 private int lockImpl(int operation
, short l_type
,
1338 ulong start
, ulong length
)
1340 import core
.sys
.posix
.fcntl
: fcntl
, flock
, off_t
;
1341 import core
.sys
.posix
.unistd
: getpid
;
1342 import std
.conv
: to
;
1346 fl
.l_whence
= SEEK_SET
;
1347 fl
.l_start
= to
!off_t(start
);
1348 fl
.l_len
= to
!off_t(length
);
1349 fl
.l_pid
= getpid();
1350 return fcntl(fileno
, operation
, &fl
);
1355 Locks the specified file segment. If the file segment is already locked
1356 by another process, waits until the existing lock is released.
1357 If both `start` and `length` are zero, the entire file is locked.
1359 Locks created using `lock` and `tryLock` have the following properties:
1361 $(LI All locks are automatically released when the process terminates.)
1362 $(LI Locks are not inherited by child processes.)
1363 $(LI Closing a file will release all locks associated with the file. On POSIX,
1364 even locks acquired via a different `File` will be released as well.)
1365 $(LI Not all NFS implementations correctly implement file locking.)
1368 void lock(LockType lockType
= LockType
.readWrite
,
1369 ulong start
= 0, ulong length
= 0)
1371 import std
.exception
: enforce
;
1373 enforce(isOpen
, "Attempting to call lock() on an unopened file");
1376 import core
.sys
.posix
.fcntl
: F_RDLCK
, F_SETLKW
, F_WRLCK
;
1377 import std
.exception
: errnoEnforce
;
1378 immutable short type
= lockType
== LockType
.readWrite
1379 ? F_WRLCK
: F_RDLCK
;
1380 errnoEnforce(lockImpl(F_SETLKW
, type
, start
, length
) != -1,
1381 "Could not set lock for file `"~_name
~"'");
1386 import core
.sys
.windows
.winbase
: LockFileEx
, LOCKFILE_EXCLUSIVE_LOCK
;
1387 immutable type
= lockType
== LockType
.readWrite ?
1388 LOCKFILE_EXCLUSIVE_LOCK
: 0;
1389 wenforce(lockImpl
!LockFileEx(start
, length
, type
),
1390 "Could not set lock for file `"~_name
~"'");
1393 static assert(false);
1397 Attempts to lock the specified file segment.
1398 If both `start` and `length` are zero, the entire file is locked.
1399 Returns: `true` if the lock was successful, and `false` if the
1400 specified file segment was already locked.
1402 bool tryLock(LockType lockType
= LockType
.readWrite
,
1403 ulong start
= 0, ulong length
= 0)
1405 import std
.exception
: enforce
;
1407 enforce(isOpen
, "Attempting to call tryLock() on an unopened file");
1410 import core
.stdc
.errno
: EACCES
, EAGAIN
, errno
;
1411 import core
.sys
.posix
.fcntl
: F_RDLCK
, F_SETLK
, F_WRLCK
;
1412 import std
.exception
: errnoEnforce
;
1413 immutable short type
= lockType
== LockType
.readWrite
1414 ? F_WRLCK
: F_RDLCK
;
1415 immutable res
= lockImpl(F_SETLK
, type
, start
, length
);
1416 if (res
== -1 && (errno
== EACCES || errno
== EAGAIN
))
1418 errnoEnforce(res
!= -1, "Could not set lock for file `"~_name
~"'");
1424 import core
.sys
.windows
.winbase
: GetLastError
, LockFileEx
, LOCKFILE_EXCLUSIVE_LOCK
,
1425 LOCKFILE_FAIL_IMMEDIATELY
;
1426 import core
.sys
.windows
.winerror
: ERROR_IO_PENDING
, ERROR_LOCK_VIOLATION
;
1427 immutable type
= lockType
== LockType
.readWrite
1428 ? LOCKFILE_EXCLUSIVE_LOCK
: 0;
1429 immutable res
= lockImpl
!LockFileEx(start
, length
,
1430 type | LOCKFILE_FAIL_IMMEDIATELY
);
1431 if (!res
&& (GetLastError() == ERROR_IO_PENDING
1432 ||
GetLastError() == ERROR_LOCK_VIOLATION
))
1434 wenforce(res
, "Could not set lock for file `"~_name
~"'");
1438 static assert(false);
1442 Removes the lock over the specified file segment.
1444 void unlock(ulong start
= 0, ulong length
= 0)
1446 import std
.exception
: enforce
;
1448 enforce(isOpen
, "Attempting to call unlock() on an unopened file");
1451 import core
.sys
.posix
.fcntl
: F_SETLK
, F_UNLCK
;
1452 import std
.exception
: errnoEnforce
;
1453 errnoEnforce(lockImpl(F_SETLK
, F_UNLCK
, start
, length
) != -1,
1454 "Could not remove lock for file `"~_name
~"'");
1459 import core
.sys
.windows
.winbase
: UnlockFileEx
;
1460 wenforce(lockImpl
!UnlockFileEx(start
, length
),
1461 "Could not remove lock for file `"~_name
~"'");
1464 static assert(false);
1470 static import std
.file
;
1471 auto deleteme
= testFilename();
1472 scope(exit
) std
.file
.remove(deleteme
);
1473 auto f
= File(deleteme
, "wb");
1474 assert(f
.tryLock());
1475 auto g
= File(deleteme
, "wb");
1476 assert(!g
.tryLock());
1477 assert(!g
.tryLock(LockType
.read
));
1479 f
.lock(LockType
.read
);
1480 assert(!g
.tryLock());
1481 assert(g
.tryLock(LockType
.read
));
1489 static if (__traits(compiles
, { import std
.process
: spawnProcess
; }))
1491 static import std
.file
;
1492 auto deleteme
= testFilename();
1493 scope(exit
) std
.file
.remove(deleteme
);
1495 // Since locks are per-process, we cannot test lock failures within
1496 // the same process. fork() is used to create a second process.
1497 static void runForked(void delegate() code
)
1499 import core
.sys
.posix
.sys
.wait : waitpid
;
1500 import core
.sys
.posix
.unistd
: fork
, _exit
;
1502 if ((child
= fork()) == 0)
1509 assert(waitpid(child
, &status
, 0) != -1);
1510 assert(status
== 0, "Fork crashed");
1514 auto f
= File(deleteme
, "w+b");
1518 auto g
= File(deleteme
, "a+b");
1519 assert(g
.tryLock());
1521 assert(g
.tryLock(LockType
.read
));
1524 assert(f
.tryLock());
1527 auto g
= File(deleteme
, "a+b");
1528 assert(!g
.tryLock());
1529 assert(!g
.tryLock(LockType
.read
));
1533 f
.lock(LockType
.read
);
1536 auto g
= File(deleteme
, "a+b");
1537 assert(!g
.tryLock());
1538 assert(g
.tryLock(LockType
.read
));
1547 Writes its arguments in text format to the file.
1549 Throws: `Exception` if the file is not opened.
1550 `ErrnoException` on an error writing to the file.
1552 void write(S
...)(S args
)
1554 import std
.traits
: isBoolean
, isIntegral
, isAggregateType
;
1555 import std
.utf
: UTFException
;
1556 auto w
= lockingTextWriter();
1561 alias A
= typeof(arg
);
1562 static if (isAggregateType
!A ||
is(A
== enum))
1564 import std
.format
.write
: formattedWrite
;
1566 formattedWrite(w
, "%s", arg
);
1568 else static if (isSomeString
!A
)
1572 else static if (isIntegral
!A
)
1574 import std
.conv
: toTextRange
;
1576 toTextRange(arg
, w
);
1578 else static if (isBoolean
!A
)
1580 put(w
, arg ?
"true" : "false");
1582 else static if (isSomeChar
!A
)
1588 import std
.format
.write
: formattedWrite
;
1590 // Most general case
1591 formattedWrite(w
, "%s", arg
);
1594 catch (UTFException e
)
1596 /* Reset the writer so that it doesn't throw another
1597 UTFException on destruction. */
1598 w
.highSurrogate
= '\0';
1605 Writes its arguments in text format to the file, followed by a newline.
1607 Throws: `Exception` if the file is not opened.
1608 `ErrnoException` on an error writing to the file.
1610 void writeln(S
...)(S args
)
1616 Writes its arguments in text format to the file, according to the
1620 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
1621 When passed as a compile-time argument, the string will be statically checked
1622 against the argument types passed.
1623 args = Items to write.
1625 Throws: `Exception` if the file is not opened.
1626 `ErrnoException` on an error writing to the file.
1628 void writef(alias fmt
, A
...)(A args
)
1629 if (isSomeString
!(typeof(fmt
)))
1631 import std
.format
: checkFormatException
;
1633 alias e
= checkFormatException
!(fmt
, A
);
1634 static assert(!e
, e
);
1635 return this.writef(fmt
, args
);
1639 void writef(Char
, A
...)(in Char
[] fmt
, A args
)
1641 import std
.format
.write
: formattedWrite
;
1643 formattedWrite(lockingTextWriter(), fmt
, args
);
1646 /// Equivalent to `file.writef(fmt, args, '\n')`.
1647 void writefln(alias fmt
, A
...)(A args
)
1648 if (isSomeString
!(typeof(fmt
)))
1650 import std
.format
: checkFormatException
;
1652 alias e
= checkFormatException
!(fmt
, A
);
1653 static assert(!e
, e
);
1654 return this.writefln(fmt
, args
);
1658 void writefln(Char
, A
...)(in Char
[] fmt
, A args
)
1660 import std
.format
.write
: formattedWrite
;
1662 auto w
= lockingTextWriter();
1663 formattedWrite(w
, fmt
, args
);
1668 Read line from the file handle and return it as a specified type.
1670 This version manages its own read buffer, which means one memory allocation per call. If you are not
1671 retaining a reference to the read data, consider the `File.readln(buf)` version, which may offer
1672 better performance as it can reuse its read buffer.
1675 S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
1676 terminator = Line terminator (by default, `'\n'`).
1679 String terminators are not supported due to ambiguity with readln(buf) below.
1682 The line that was read, including the line terminator character.
1685 `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
1689 // Reads `stdin` and writes it to `stdout`.
1695 while ((line = stdin.readln()) !is null)
1700 S
readln(S
= string
)(dchar terminator
= '\n') @safe
1703 Unqual
!(ElementEncodingType
!S
)[] buf
;
1704 readln(buf
, terminator
);
1705 return (() @trusted => cast(S
) buf
)();
1710 import std
.algorithm
.comparison
: equal
;
1711 static import std
.file
;
1712 import std
.meta
: AliasSeq
;
1714 auto deleteme
= testFilename();
1715 std
.file
.write(deleteme
, "hello\nworld\n");
1716 scope(exit
) std
.file
.remove(deleteme
);
1717 static foreach (String
; AliasSeq
!(string
, char[], wstring
, wchar[], dstring
, dchar[]))
1719 auto witness
= [ "hello\n", "world\n" ];
1720 auto f
= File(deleteme
);
1723 while ((buf
= f
.readln
!String()).length
)
1725 assert(i
< witness
.length
);
1726 assert(equal(buf
, witness
[i
++]));
1728 assert(i
== witness
.length
);
1734 static import std
.file
;
1735 import std
.typecons
: Tuple
;
1737 auto deleteme
= testFilename();
1738 std
.file
.write(deleteme
, "cześć \U0002000D");
1739 scope(exit
) std
.file
.remove(deleteme
);
1740 uint[] lengths
= [12,8,7];
1741 static foreach (uint i
, C
; Tuple
!(char, wchar, dchar).Types
)
1743 immutable(C
)[] witness
= "cześć \U0002000D";
1744 auto buf
= File(deleteme
).readln
!(immutable(C
)[])();
1745 assert(buf
.length
== lengths
[i
]);
1746 assert(buf
== witness
);
1751 Read line from the file handle and write it to `buf[]`, including
1752 terminating character.
1754 This can be faster than $(D line = File.readln()) because you can reuse
1755 the buffer for each call. Note that reusing the buffer means that you
1756 must copy the previous contents if you wish to retain them.
1759 buf = Buffer used to store the resulting line data. buf is
1760 enlarged if necessary, then set to the slice exactly containing the line.
1761 terminator = Line terminator (by default, `'\n'`). Use
1762 $(REF newline, std,ascii) for portability (unless the file was opened in
1766 0 for end of file, otherwise number of characters read.
1767 The return value will always be equal to `buf.length`.
1769 Throws: `StdioException` on I/O error, or `UnicodeException` on Unicode
1774 // Read lines from `stdin` into a string
1775 // Ignore lines starting with '#'
1776 // Write the string to `stdout`
1784 while (stdin.readln(buf))
1796 This method can be more efficient than the one in the previous example
1797 because `stdin.readln(buf)` reuses (if possible) memory allocated
1798 for `buf`, whereas $(D line = stdin.readln()) makes a new memory allocation
1801 For even better performance you can help `readln` by passing in a
1802 large buffer to avoid memory reallocations. This can be done by reusing the
1803 largest buffer returned by `readln`:
1807 // Read lines from `stdin` and count words
1808 import std.array, std.stdio;
1819 if (line.length > buf.length)
1822 words += line.split.length;
1828 This is actually what $(LREF byLine) does internally, so its usage
1829 is recommended if you want to process a complete file.
1831 size_t
readln(C
)(ref C
[] buf
, dchar terminator
= '\n') @safe
1832 if (isSomeChar
!C
&& is(Unqual
!C
== C
) && !is(C
== enum))
1834 import std
.exception
: enforce
;
1836 static if (is(C
== char))
1838 enforce(_p
&& _p
.handle
, "Attempt to read from an unopened file.");
1839 if (_p
.orientation
== Orientation
.unknown
)
1841 import core
.stdc
.wchar_
: fwide
;
1842 auto w
= fwide(_p
.handle
, 0);
1843 if (w
< 0) _p
.orientation
= Orientation
.narrow
;
1844 else if (w
> 0) _p
.orientation
= Orientation
.wide
;
1846 return readlnImpl(_p
.handle
, buf
, terminator
, _p
.orientation
);
1850 string s
= readln(terminator
);
1857 import std
.utf
: codeLength
;
1858 buf
.length
= codeLength
!C(s
);
1869 static import std
.file
;
1870 auto deleteme
= testFilename();
1871 std
.file
.write(deleteme
, "123\n456789");
1872 scope(exit
) std
.file
.remove(deleteme
);
1874 auto file
= File(deleteme
);
1875 char[] buffer
= new char[10];
1876 char[] line
= buffer
;
1878 auto beyond
= line
.length
;
1879 buffer
[beyond
] = 'a';
1880 file
.readln(line
); // should not write buffer beyond line
1881 assert(buffer
[beyond
] == 'a');
1884 // https://issues.dlang.org/show_bug.cgi?id=15293
1887 // @system due to readln
1888 static import std
.file
;
1889 auto deleteme
= testFilename();
1890 std
.file
.write(deleteme
, "a\n\naa");
1891 scope(exit
) std
.file
.remove(deleteme
);
1893 auto file
= File(deleteme
);
1897 file
.readln(buffer
, '\n');
1900 file
.readln(line
, '\n');
1903 file
.readln(line
, '\n');
1905 assert(line
[0 .. 1].capacity
== 0);
1909 size_t
readln(C
, R
)(ref C
[] buf
, R terminator
) @safe
1910 if (isSomeChar
!C
&& is(Unqual
!C
== C
) && !is(C
== enum) &&
1911 isBidirectionalRange
!R
&& is(typeof(terminator
.front
== dchar.init
)))
1913 import std
.algorithm
.mutation
: swap
;
1914 import std
.algorithm
.searching
: endsWith
;
1915 import std
.range
.primitives
: back
;
1917 auto last
= terminator
.back
;
1922 if (!readln(buf2
, last
) ||
endsWith(buf2
, terminator
))
1941 static import std
.file
;
1942 import std
.typecons
: Tuple
;
1944 auto deleteme
= testFilename();
1945 std
.file
.write(deleteme
, "hello\n\rworld\nhow\n\rare ya");
1946 scope(exit
) std
.file
.remove(deleteme
);
1947 foreach (C
; Tuple
!(char, wchar, dchar).Types
)
1949 immutable(C
)[][] witness
= [ "hello\n\r", "world\nhow\n\r", "are ya" ];
1950 auto f
= File(deleteme
);
1953 while (f
.readln(buf
, "\n\r"))
1955 assert(i
< witness
.length
);
1956 assert(buf
== witness
[i
++]);
1958 assert(buf
.length
== 0);
1963 * Reads formatted _data from the file using $(REF formattedRead, std,_format).
1965 * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
1966 * When passed as a compile-time argument, the string will be statically checked
1967 * against the argument types passed.
1968 * data = Items to be read.
1970 * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
1971 * this number will be less than the number of variables provided.
1978 auto f = File("input");
1988 % echo "1 2 3" > input
1995 uint readf(alias format
, Data
...)(auto ref Data data
)
1996 if (isSomeString
!(typeof(format
)))
1998 import std
.format
: checkFormatException
;
2000 alias e
= checkFormatException
!(format
, Data
);
2001 static assert(!e
, e
);
2002 return this.readf(format
, data
);
2006 uint readf(Data
...)(scope const(char)[] format
, auto ref Data data
)
2008 import std
.format
.read
: formattedRead
;
2011 auto input
= LockingTextReader(this);
2012 return formattedRead(input
, format
, data
);
2018 static import std
.file
;
2020 auto deleteme
= std
.file
.deleteme();
2021 std
.file
.write(deleteme
, "hello\nworld\ntrue\nfalse\n");
2022 scope(exit
) std
.file
.remove(deleteme
);
2024 auto f
= File(deleteme
);
2026 assert(s
== "hello", "["~s
~"]");
2028 assert(s
== "world", "["~s
~"]");
2031 f
.readf("%s\n%s\n", b1
, b2
);
2032 assert(b1
== true && b2
== false);
2035 // backwards compatibility with pointers
2038 // @system due to readf
2039 static import std
.file
;
2041 auto deleteme
= testFilename();
2042 std
.file
.write(deleteme
, "hello\nworld\ntrue\nfalse\n");
2043 scope(exit
) std
.file
.remove(deleteme
);
2045 auto f
= File(deleteme
);
2046 f
.readf("%s\n", &s
);
2047 assert(s
== "hello", "["~s
~"]");
2048 f
.readf("%s\n", &s
);
2049 assert(s
== "world", "["~s
~"]");
2051 // https://issues.dlang.org/show_bug.cgi?id=11698
2053 f
.readf("%s\n%s\n", &b1
, &b2
);
2054 assert(b1
== true && b2
== false);
2057 // backwards compatibility (mixed)
2060 // @system due to readf
2061 static import std
.file
;
2063 auto deleteme
= testFilename();
2064 std
.file
.write(deleteme
, "hello\nworld\ntrue\nfalse\n");
2065 scope(exit
) std
.file
.remove(deleteme
);
2067 auto f
= File(deleteme
);
2068 f
.readf("%s\n%s\n", s1
, &s2
);
2069 assert(s1
== "hello");
2070 assert(s2
== "world");
2072 // https://issues.dlang.org/show_bug.cgi?id=11698
2074 f
.readf("%s\n%s\n", &b1
, b2
);
2075 assert(b1
== true && b2
== false);
2078 // Nice error of std.stdio.readf with newlines
2079 // https://issues.dlang.org/show_bug.cgi?id=12260
2082 static import std
.file
;
2084 auto deleteme
= testFilename();
2085 std
.file
.write(deleteme
, "1\n2");
2086 scope(exit
) std
.file
.remove(deleteme
);
2088 auto f
= File(deleteme
);
2089 f
.readf("%s", &input
);
2091 import std
.conv
: ConvException
;
2092 import std
.exception
: collectException
;
2093 assert(collectException
!ConvException(f
.readf("%s", &input
)).msg
==
2094 "Unexpected '\\n' when converting from type LockingTextReader to type int");
2098 Returns a temporary file by calling $(CSTDIO tmpfile).
2099 Note that the created file has no $(LREF name).*/
2100 static File
tmpfile() @safe
2102 import std
.exception
: errnoEnforce
;
2104 return File(errnoEnforce(.tmpfile(),
2105 "Could not create temporary file with tmpfile()"),
2110 Unsafe function that wraps an existing `FILE*`. The resulting $(D
2111 File) never takes the initiative in closing the file.
2112 Note that the created file has no $(LREF name)*/
2113 /*private*/ static File
wrapFile(FILE
* f
) @safe
2115 import std
.exception
: enforce
;
2117 return File(enforce(f
, "Could not wrap null FILE*"),
2118 null, /*uint.max / 2*/ 9999);
2122 Returns the `FILE*` corresponding to this object.
2124 FILE
* getFP() @safe pure
2126 import std
.exception
: enforce
;
2128 enforce(_p
&& _p
.handle
,
2129 "Attempting to call getFP() on an unopened file");
2135 static import core
.stdc
.stdio
;
2136 assert(stdout
.getFP() == core
.stdc
.stdio
.stdout
);
2140 Returns the file number corresponding to this object.
2142 @property fileno_t
fileno() const @trusted
2144 import std
.exception
: enforce
;
2146 enforce(isOpen
, "Attempting to call fileno() on an unopened file");
2147 return .fileno(cast(FILE
*) _p
.handle
);
2151 Returns the underlying operating system `HANDLE` (Windows only).
2154 @property HANDLE
windowsHandle();
2157 @property HANDLE
windowsHandle()
2159 return cast(HANDLE
)_get_osfhandle(fileno
);
2163 // Note: This was documented until 2013/08
2165 Range that reads one line at a time. Returned by $(LREF byLine).
2167 Allows to directly use range operations on lines of a file.
2169 private struct ByLineImpl(Char
, Terminator
)
2172 import std
.typecons
: borrow
, RefCountedAutoInitialize
, SafeRefCounted
;
2174 /* Ref-counting stops the source range's Impl
2175 * from getting out of sync after the range is copied, e.g.
2176 * when accessing range.front, then using std.range.take,
2177 * then accessing range.front again. */
2178 alias PImpl
= SafeRefCounted
!(Impl
, RefCountedAutoInitialize
.no
);
2181 static if (isScalarType
!Terminator
)
2182 enum defTerm
= '\n';
2184 enum defTerm
= cast(Terminator
)"\n";
2187 this(File f
, KeepTerminator kt
= No
.keepTerminator
,
2188 Terminator terminator
= defTerm
)
2190 impl
= PImpl(f
, kt
, terminator
);
2193 /* Verifiably `@safe` when built with -preview=DIP1000. */
2194 @property bool empty() @trusted
2196 // Using `ref` is actually necessary here.
2197 return impl
.borrow
!((ref i
) => i
.empty
);
2200 /* Verifiably `@safe` when built with -preview=DIP1000. */
2201 @property Char
[] front() @trusted
2203 // Using `ref` is likely optional here.
2204 return impl
.borrow
!((ref i
) => i
.front
);
2207 /* Verifiably `@safe` when built with -preview=DIP1000. */
2208 void popFront() @trusted
2210 return impl
.borrow
!((ref i
) => i
.popFront());
2220 Terminator terminator
;
2221 KeepTerminator keepTerminator
;
2226 this(File f
, KeepTerminator kt
, Terminator terminator
)
2229 this.terminator
= terminator
;
2230 keepTerminator
= kt
;
2233 // Range primitive implementations.
2234 @property bool empty()
2237 return line
is null;
2240 @property Char
[] front()
2257 import std
.algorithm
.searching
: endsWith
;
2258 assert(file
.isOpen
);
2260 file
.readln(line
, terminator
);
2261 if (line
.length
> buffer
.length
)
2270 else if (keepTerminator
== No
.keepTerminator
2271 && endsWith(line
, terminator
))
2273 static if (isScalarType
!Terminator
)
2275 else static if (isArray
!Terminator
)
2278 is(immutable ElementEncodingType
!Terminator
== immutable Char
));
2279 const tlen
= terminator
.length
;
2282 static assert(false);
2283 line
= line
[0 .. line
.length
- tlen
];
2291 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2292 set up to read from the file handle one line at a time.
2294 The element type for the range will be `Char[]`. Range primitives
2295 may throw `StdioException` on I/O error.
2298 Each `front` will not persist after $(D
2299 popFront) is called, so the caller must copy its contents (e.g. by
2300 calling `to!string`) when retention is needed. If the caller needs
2301 to retain a copy of every line, use the $(LREF byLineCopy) function
2305 Char = Character type for each line, defaulting to `char`.
2306 keepTerminator = Use `Yes.keepTerminator` to include the
2307 terminator at the end of each line.
2308 terminator = Line separator (`'\n'` by default). Use
2309 $(REF newline, std,ascii) for portability (unless the file was opened in
2314 import std.algorithm, std.stdio, std.string;
2315 // Count words in a file using ranges.
2318 auto file = File("file.txt"); // Open for reading
2319 const wordCount = file.byLine() // Read lines
2320 .map!split // Split into words
2321 .map!(a => a.length) // Count words per line
2322 .sum(); // Total word count
2329 import std.range, std.stdio;
2330 // Read lines using foreach.
2333 auto file = File("file.txt"); // Open for reading
2334 auto range = file.byLine();
2335 // Print first three lines
2336 foreach (line; range.take(3))
2338 // Print remaining lines beginning with '#'
2339 foreach (line; range)
2341 if (!line.empty && line[0] == '#')
2346 Notice that neither example accesses the line data returned by
2347 `front` after the corresponding `popFront` call is made (because
2348 the contents may well have changed).
2351 Windows specific Example:
2359 foreach (line; File("file.txt").byLine(No.keepTerminator, "\r\n"))
2361 writeln("|"~line~"|");
2362 if (line == "HelloWorld")
2363 writeln("^This Line is here.");
2368 auto byLine(Terminator
= char, Char
= char)
2369 (KeepTerminator keepTerminator
= No
.keepTerminator
,
2370 Terminator terminator
= '\n')
2371 if (isScalarType
!Terminator
)
2373 return ByLineImpl
!(Char
, Terminator
)(this, keepTerminator
, terminator
);
2377 auto byLine(Terminator
, Char
= char)
2378 (KeepTerminator keepTerminator
, Terminator terminator
)
2379 if (is(immutable ElementEncodingType
!Terminator
== immutable Char
))
2381 return ByLineImpl
!(Char
, Terminator
)(this, keepTerminator
, terminator
);
2386 static import std
.file
;
2387 auto deleteme
= testFilename();
2388 std
.file
.write(deleteme
, "hi");
2389 scope(success
) std
.file
.remove(deleteme
);
2391 import std
.meta
: AliasSeq
;
2392 static foreach (T
; AliasSeq
!(char, wchar, dchar))
2394 auto blc
= File(deleteme
).byLine
!(T
, T
);
2395 assert(blc
.front
== "hi");
2396 // check front is cached
2397 assert(blc
.front
is blc
.front
);
2401 // https://issues.dlang.org/show_bug.cgi?id=19980
2404 static import std
.file
;
2405 auto deleteme
= testFilename();
2406 std
.file
.write(deleteme
, "Line 1\nLine 2\nLine 3\n");
2407 scope(success
) std
.file
.remove(deleteme
);
2409 auto f
= File(deleteme
);
2412 assert(f
.byLine().front
== "Line 1");
2415 private struct ByLineCopy(Char
, Terminator
)
2418 import std
.typecons
: borrow
, RefCountedAutoInitialize
, SafeRefCounted
;
2420 /* Ref-counting stops the source range's ByLineCopyImpl
2421 * from getting out of sync after the range is copied, e.g.
2422 * when accessing range.front, then using std.range.take,
2423 * then accessing range.front again. */
2424 alias Impl
= SafeRefCounted
!(ByLineCopyImpl
!(Char
, Terminator
),
2425 RefCountedAutoInitialize
.no
);
2429 this(File f
, KeepTerminator kt
, Terminator terminator
)
2431 impl
= Impl(f
, kt
, terminator
);
2434 /* Verifiably `@safe` when built with -preview=DIP1000. */
2435 @property bool empty() @trusted
2437 // Using `ref` is actually necessary here.
2438 return impl
.borrow
!((ref i
) => i
.empty
);
2441 /* Verifiably `@safe` when built with -preview=DIP1000. */
2442 @property Char
[] front() @trusted
2444 // Using `ref` is likely optional here.
2445 return impl
.borrow
!((ref i
) => i
.front
);
2448 /* Verifiably `@safe` when built with -preview=DIP1000. */
2449 void popFront() @trusted
2451 impl
.borrow
!((ref i
) => i
.popFront());
2455 private struct ByLineCopyImpl(Char
, Terminator
)
2457 ByLineImpl
!(Unqual
!Char
, Terminator
).Impl impl
;
2462 this(File f
, KeepTerminator kt
, Terminator terminator
)
2464 impl
= ByLineImpl
!(Unqual
!Char
, Terminator
).Impl(f
, kt
, terminator
);
2467 @property bool empty()
2476 line
= impl
.front
.dup
;
2490 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2491 set up to read from the file handle one line
2492 at a time. Each line will be newly allocated. `front` will cache
2493 its value to allow repeated calls without unnecessary allocations.
2495 Note: Due to caching byLineCopy can be more memory-efficient than
2496 `File.byLine.map!idup`.
2498 The element type for the range will be `Char[]`. Range
2499 primitives may throw `StdioException` on I/O error.
2502 Char = Character type for each line, defaulting to $(D immutable char).
2503 keepTerminator = Use `Yes.keepTerminator` to include the
2504 terminator at the end of each line.
2505 terminator = Line separator (`'\n'` by default). Use
2506 $(REF newline, std,ascii) for portability (unless the file was opened in
2511 import std.algorithm, std.array, std.stdio;
2512 // Print sorted lines of a file.
2515 auto sortedLines = File("file.txt") // Open for reading
2516 .byLineCopy() // Read persistent lines
2517 .array() // into an array
2518 .sort(); // then sort them
2519 foreach (line; sortedLines)
2524 $(REF readText, std,file)
2526 auto byLineCopy(Terminator
= char, Char
= immutable char)
2527 (KeepTerminator keepTerminator
= No
.keepTerminator
,
2528 Terminator terminator
= '\n')
2529 if (isScalarType
!Terminator
)
2531 return ByLineCopy
!(Char
, Terminator
)(this, keepTerminator
, terminator
);
2535 auto byLineCopy(Terminator
, Char
= immutable char)
2536 (KeepTerminator keepTerminator
, Terminator terminator
)
2537 if (is(immutable ElementEncodingType
!Terminator
== immutable Char
))
2539 return ByLineCopy
!(Char
, Terminator
)(this, keepTerminator
, terminator
);
2544 static assert(is(typeof(File("").byLine
.front
) == char[]));
2545 static assert(is(typeof(File("").byLineCopy
.front
) == string
));
2547 is(typeof(File("").byLineCopy
!(char, char).front
) == char[]));
2552 import std
.algorithm
.comparison
: equal
;
2553 static import std
.file
;
2555 auto deleteme
= testFilename();
2556 std
.file
.write(deleteme
, "");
2557 scope(success
) std
.file
.remove(deleteme
);
2560 auto f
= File(deleteme
);
2561 foreach (line
; f
.byLine())
2568 void test(Terminator
)(string txt
, in string
[] witness
,
2569 KeepTerminator kt
, Terminator term
, bool popFirstLine
= false)
2571 import std
.algorithm
.sorting
: sort
;
2572 import std
.array
: array
;
2573 import std
.conv
: text
;
2574 import std
.range
.primitives
: walkLength
;
2577 std
.file
.write(deleteme
, txt
);
2578 auto f
= File(deleteme
);
2584 auto lines
= f
.byLine(kt
, term
);
2590 assert(lines
.empty || lines
.front
is lines
.front
);
2591 foreach (line
; lines
)
2593 assert(line
== witness
[i
++]);
2595 assert(i
== witness
.length
, text(i
, " != ", witness
.length
));
2597 // https://issues.dlang.org/show_bug.cgi?id=11830
2598 auto walkedLength
= File(deleteme
).byLine(kt
, term
).walkLength
;
2599 assert(walkedLength
== witness
.length
, text(walkedLength
, " != ", witness
.length
));
2601 // test persistent lines
2602 assert(File(deleteme
).byLineCopy(kt
, term
).array
.sort() == witness
.dup
.sort());
2605 KeepTerminator kt
= No
.keepTerminator
;
2606 test("", null, kt
, '\n');
2607 test("\n", [ "" ], kt
, '\n');
2608 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt
, '\n');
2609 test("asd\ndef\nasdf", [ "asd", "def", "asdf" ], kt
, '\n', true);
2610 test("asd\ndef\nasdf\n", [ "asd", "def", "asdf" ], kt
, '\n');
2611 test("foo", [ "foo" ], kt
, '\n', true);
2612 test("bob\r\nmarge\r\nsteve\r\n", ["bob", "marge", "steve"],
2614 test("sue\r", ["sue"], kt
, '\r');
2616 kt
= Yes
.keepTerminator
;
2617 test("", null, kt
, '\n');
2618 test("\n", [ "\n" ], kt
, '\n');
2619 test("asd\ndef\nasdf", [ "asd\n", "def\n", "asdf" ], kt
, '\n');
2620 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt
, '\n');
2621 test("asd\ndef\nasdf\n", [ "asd\n", "def\n", "asdf\n" ], kt
, '\n', true);
2622 test("foo", [ "foo" ], kt
, '\n');
2623 test("bob\r\nmarge\r\nsteve\r\n", ["bob\r\n", "marge\r\n", "steve\r\n"],
2625 test("sue\r", ["sue\r"], kt
, '\r');
2630 import std
.algorithm
.comparison
: equal
;
2631 import std
.range
: drop
, take
;
2635 static import std
.file
;
2637 /* the C function tmpfile doesn't seem to work, even when called from C */
2638 auto deleteme
= testFilename();
2639 auto file
= File(deleteme
, "w+");
2640 scope(success
) std
.file
.remove(deleteme
);
2642 else version (CRuntime_Bionic
)
2644 static import std
.file
;
2646 /* the C function tmpfile doesn't work when called from a shared
2648 https://code.google.com/p/android/issues/detail?id=66815 */
2649 auto deleteme
= testFilename();
2650 auto file
= File(deleteme
, "w+");
2651 scope(success
) std
.file
.remove(deleteme
);
2654 auto file
= File
.tmpfile();
2655 file
.write("1\n2\n3\n");
2657 // https://issues.dlang.org/show_bug.cgi?id=9599
2659 File
.ByLineImpl
!(char, char) fbl
= file
.byLine();
2661 assert(fbl
.front
== "1");
2662 assert(fbl
.front
is fbl2
.front
);
2663 assert(fbl
.take(1).equal(["1"]));
2664 assert(fbl
.equal(["2", "3"]));
2666 assert(file
.isOpen
); // we still have a valid reference
2669 fbl
= file
.byLine();
2670 assert(!fbl
.drop(2).empty
);
2671 assert(fbl
.equal(["3"]));
2673 assert(file
.isOpen
);
2676 assert(!file
.isOpen
);
2681 static import std
.file
;
2682 auto deleteme
= testFilename();
2683 std
.file
.write(deleteme
, "hi");
2684 scope(success
) std
.file
.remove(deleteme
);
2686 auto blc
= File(deleteme
).byLineCopy
;
2688 // check front is cached
2689 assert(blc
.front
is blc
.front
);
2693 Creates an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2694 set up to parse one line at a time from the file into a tuple.
2696 Range primitives may throw `StdioException` on I/O error.
2699 format = tuple record $(REF_ALTTEXT _format, formattedRead, std, _format)
2702 The input range set up to parse one line at a time into a record tuple.
2706 It is similar to $(LREF byLine) and uses
2707 $(REF_ALTTEXT _format, formattedRead, std, _format) under the hood.
2709 template byRecord(Fields
...)
2711 auto byRecord(string format
)
2713 return ByRecordImpl
!(Fields
)(this, format
);
2720 static import std
.file
;
2721 import std
.typecons
: tuple
;
2723 // prepare test file
2724 auto testFile
= std
.file
.deleteme();
2725 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
2726 std
.file
.write(testFile
, "1 2\n4 1\n5 100");
2727 scope(exit
) std
.file
.remove(testFile
);
2729 File f
= File(testFile
);
2730 scope(exit
) f
.close();
2732 auto expected
= [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
2734 foreach (e
; f
.byRecord
!(int, int)("%s %s"))
2736 assert(e
== expected
[i
++]);
2740 // Note: This was documented until 2013/08
2742 * Range that reads a chunk at a time.
2744 private struct ByChunkImpl
2752 chunk_
= file_
.rawRead(chunk_
);
2753 if (chunk_
.length
== 0)
2758 this(File file
, size_t size
)
2760 this(file
, new ubyte[](size
));
2763 this(File file
, ubyte[] buffer
)
2765 import std
.exception
: enforce
;
2766 enforce(buffer
.length
, "size must be larger than 0");
2772 // `ByChunk`'s input range primitive operations.
2776 return !file_
.isOpen
;
2785 import core
.exception
: RangeError
;
2787 throw new RangeError();
2797 import core
.exception
: RangeError
;
2799 throw new RangeError();
2806 Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
2807 set up to read from the file handle a chunk at a time.
2809 The element type for the range will be `ubyte[]`. Range primitives
2810 may throw `StdioException` on I/O error.
2816 // Read standard input 4KB at a time
2817 foreach (ubyte[] buffer; stdin.byChunk(4096))
2824 The parameter may be a number (as shown in the example above) dictating the
2825 size of each chunk. Alternatively, `byChunk` accepts a
2826 user-provided buffer that it uses directly.
2832 // Read standard input 4KB at a time
2833 foreach (ubyte[] buffer; stdin.byChunk(new ubyte[4096]))
2840 In either case, the content of the buffer is reused across calls. That means
2841 `front` will not persist after `popFront` is called, so if retention is
2842 needed, the caller must copy its contents (e.g. by calling `buffer.dup`).
2844 In the example above, `buffer.length` is 4096 for all iterations, except
2845 for the last one, in which case `buffer.length` may be less than 4096 (but
2846 always greater than zero).
2848 With the mentioned limitations, `byChunk` works with any algorithm
2849 compatible with input ranges.
2853 // Efficient file copy, 1MB at a time.
2854 import std.algorithm, std.stdio;
2857 stdin.byChunk(1024 * 1024).copy(stdout.lockingTextWriter());
2861 $(REF joiner, std,algorithm,iteration) can be used to join chunks together into
2862 a single range lazily.
2865 import std.algorithm, std.stdio;
2869 static assert(is(typeof(stdin.byChunk(4096).front) == ubyte[]));
2871 static assert(is(typeof(stdin.byChunk(4096).joiner.front) == ubyte));
2875 Returns: A call to `byChunk` returns a range initialized with the `File`
2876 object and the appropriate buffer.
2878 Throws: If the user-provided size is zero or the user-provided buffer
2879 is empty, throws an `Exception`. In case of an I/O error throws
2882 auto byChunk(size_t chunkSize
)
2884 return ByChunkImpl(this, chunkSize
);
2887 auto byChunk(ubyte[] buffer
)
2889 return ByChunkImpl(this, buffer
);
2894 static import std
.file
;
2896 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
2898 auto deleteme
= testFilename();
2899 std
.file
.write(deleteme
, "asd\ndef\nasdf");
2901 auto witness
= ["asd\n", "def\n", "asdf" ];
2902 auto f
= File(deleteme
);
2907 std
.file
.remove(deleteme
);
2911 foreach (chunk
; f
.byChunk(4))
2912 assert(chunk
== cast(ubyte[]) witness
[i
++]);
2914 assert(i
== witness
.length
);
2919 static import std
.file
;
2921 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
2923 auto deleteme
= testFilename();
2924 std
.file
.write(deleteme
, "asd\ndef\nasdf");
2926 auto witness
= ["asd\n", "def\n", "asdf" ];
2927 auto f
= File(deleteme
);
2932 std
.file
.remove(deleteme
);
2936 foreach (chunk
; f
.byChunk(new ubyte[4]))
2937 assert(chunk
== cast(ubyte[]) witness
[i
++]);
2939 assert(i
== witness
.length
);
2942 // Note: This was documented until 2013/08
2944 `Range` that locks the file and allows fast writing to it.
2946 struct LockingTextWriter
2949 import std
.range
.primitives
: ElementType
, isInfinite
, isInputRange
;
2950 // Access the FILE* handle through the 'file_' member
2951 // to keep the object alive through refcounting
2954 // the unshared version of FILE* handle, extracted from the File object
2955 @property _iobuf
* handle_() @trusted { return cast(_iobuf
*) file_
._p
.handle
; }
2957 // the file's orientation (byte- or wide-oriented)
2960 // Buffers for when we need to transcode.
2961 wchar highSurrogate
= '\0'; // '\0' indicates empty
2962 void highSurrogateShouldBeEmpty() @safe
2964 import std
.utf
: UTFException
;
2965 if (highSurrogate
!= '\0')
2966 throw new UTFException("unpaired surrogate UTF-16 value");
2969 size_t rbuf8Filled
= 0;
2972 this(ref File f
) @trusted
2974 import std
.exception
: enforce
;
2976 enforce(f
._p
&& f
._p
.handle
, "Attempting to write to closed File");
2978 FILE
* fps
= f
._p
.handle
;
2980 version (CRuntime_Microsoft
)
2982 // Microsoft doesn't implement fwide. Instead, there's the
2983 // concept of ANSI/UNICODE mode. fputc doesn't work in UNICODE
2984 // mode; fputwc has to be used. So that essentially means
2985 // "wide-oriented" for us.
2986 immutable int mode
= _setmode(f
.fileno
, _O_TEXT
);
2987 // Set some arbitrary mode to obtain the previous one.
2988 if (mode
!= -1) // _setmode() succeeded
2990 _setmode(f
.fileno
, mode
); // Restore previous mode.
2991 if (mode
& (_O_WTEXT | _O_U16TEXT | _O_U8TEXT
))
2993 orientation_
= 1; // wide
2999 import core
.stdc
.wchar_
: fwide
;
3000 orientation_
= fwide(fps
, 0);
3008 if (auto p
= file_
._p
)
3010 if (p
.handle
) _FUNLOCK(p
.handle
);
3013 /* Destroy file_ before possibly throwing. Else it wouldn't be
3014 destroyed, and its reference count would be wrong. */
3015 highSurrogateShouldBeEmpty();
3020 if (auto p
= file_
._p
)
3022 if (p
.handle
) _FLOCK(p
.handle
);
3026 /// Range primitive implementations.
3027 void put(A
)(scope A writeme
)
3028 if ((isSomeChar
!(ElementType
!A
) ||
3029 is(ElementType
!A
: const(ubyte))) &&
3033 import std
.exception
: errnoEnforce
;
3035 alias C
= ElementEncodingType
!A
;
3036 static assert(!is(C
== void));
3037 static if (isSomeString
!A
&& C
.sizeof
== 1 ||
is(A
: const(ubyte)[]))
3039 if (orientation_
<= 0)
3041 //file.write(writeme); causes infinite recursion!!!
3042 //file.rawWrite(writeme);
3043 auto result
= trustedFwrite(file_
._p
.handle
, writeme
);
3044 if (result
!= writeme
.length
) errnoEnforce(0);
3049 // put each element in turn.
3050 foreach (c
; writeme
)
3057 void put(C
)(scope C c
) @safe
3058 if (isSomeChar
!C ||
is(C
: const(ubyte)))
3060 import std
.utf
: decodeFront
, encode
, stride
;
3062 static if (c
.sizeof
== 1)
3064 highSurrogateShouldBeEmpty();
3065 if (orientation_
<= 0) trustedFPUTC(c
, handle_
);
3066 else if (c
<= 0x7F) trustedFPUTWC(c
, handle_
);
3067 else if (c
>= 0b1100_0000) // start byte of multibyte sequence
3072 else // continuation byte of multibyte sequence
3074 rbuf8
[rbuf8Filled
] = c
;
3076 if (stride(rbuf8
[]) == rbuf8Filled
) // sequence is complete
3078 char[] str = rbuf8
[0 .. rbuf8Filled
];
3079 immutable dchar d
= decodeFront(str);
3080 wchar_t
[4 / wchar_t
.sizeof
] wbuf
;
3081 immutable size
= encode(wbuf
, d
);
3082 foreach (i
; 0 .. size
)
3083 trustedFPUTWC(wbuf
[i
], handle_
);
3088 else static if (c
.sizeof
== 2)
3090 import std
.utf
: decode
;
3094 highSurrogateShouldBeEmpty();
3095 if (orientation_
<= 0) trustedFPUTC(c
, handle_
);
3096 else trustedFPUTWC(c
, handle_
);
3098 else if (0xD800 <= c
&& c
<= 0xDBFF) // high surrogate
3100 highSurrogateShouldBeEmpty();
3103 else // standalone or low surrogate
3106 if (highSurrogate
!= '\0')
3108 immutable wchar[2] rbuf
= [highSurrogate
, c
];
3110 d
= decode(rbuf
[], index
);
3113 if (orientation_
<= 0)
3116 immutable size
= encode(wbuf
, d
);
3117 foreach (i
; 0 .. size
)
3118 trustedFPUTC(wbuf
[i
], handle_
);
3122 wchar_t
[4 / wchar_t
.sizeof
] wbuf
;
3123 immutable size
= encode(wbuf
, d
);
3124 foreach (i
; 0 .. size
)
3125 trustedFPUTWC(wbuf
[i
], handle_
);
3130 else // 32-bit characters
3132 import std
.utf
: encode
;
3134 highSurrogateShouldBeEmpty();
3135 if (orientation_
<= 0)
3139 trustedFPUTC(c
, handle_
);
3144 immutable len
= encode(buf
, c
);
3145 foreach (i
; 0 .. len
)
3146 trustedFPUTC(buf
[i
], handle_
);
3153 import std
.utf
: isValidDchar
;
3155 assert(isValidDchar(c
));
3158 trustedFPUTWC(cast(wchar_t
) c
, handle_
);
3162 trustedFPUTWC(cast(wchar_t
)
3163 ((((c
- 0x10000) >> 10) & 0x3FF)
3164 + 0xD800), handle_
);
3165 trustedFPUTWC(cast(wchar_t
)
3166 (((c
- 0x10000) & 0x3FF) + 0xDC00),
3170 else version (Posix
)
3172 trustedFPUTWC(cast(wchar_t
) c
, handle_
);
3184 * Output range which locks the file when created, and unlocks the file when it goes
3187 * Returns: An $(REF_ALTTEXT output range, isOutputRange, std, range, primitives)
3188 * which accepts string types, `ubyte[]`, individual character types, and
3189 * individual `ubyte`s.
3191 * Note: Writing either arrays of `char`s or `ubyte`s is faster than
3192 * writing each character individually from a range. For large amounts of data,
3193 * writing the contents in chunks using an intermediary array can result
3194 * in a speed increase.
3196 * Throws: $(REF UTFException, std, utf) if the data given is a `char` range
3197 * and it contains malformed UTF data.
3199 * See_Also: $(LREF byChunk) for an example.
3201 auto lockingTextWriter() @safe
3203 return LockingTextWriter(this);
3206 // An output range which optionally locks the file and puts it into
3207 // binary mode (similar to rawWrite). Because it needs to restore
3208 // the file mode on destruction, it is RefCounted on Windows.
3209 struct BinaryWriterImpl(bool locking
)
3211 import std
.traits
: hasIndirections
;
3213 // Access the FILE* handle through the 'file_' member
3214 // to keep the object alive through refcounting
3225 // Don't use this, but `File.lockingBinaryWriter()` instead.
3226 // Must be public for RefCounted and emplace() in druntime.
3227 this(scope ref File f
)
3229 import std
.exception
: enforce
;
3231 enforce(f
._p
&& f
._p
.handle
);
3233 FILE
* fps
= f
._p
.handle
;
3239 .fflush(fps
); // before changing translation mode
3241 oldMode
= ._setmode(fd
, _O_BINARY
);
3247 if (!file_
._p ||
!file_
._p
.handle
)
3250 FILE
* fps
= file_
._p
.handle
;
3254 .fflush(fps
); // before restoring translation mode
3255 ._setmode(fd
, oldMode
);
3261 void rawWrite(T
)(in T
[] buffer
)
3263 import std
.conv
: text
;
3264 import std
.exception
: errnoEnforce
;
3266 auto result
= trustedFwrite(file_
._p
.handle
, buffer
);
3267 if (result
== result
.max
) result
= 0;
3268 errnoEnforce(result
== buffer
.length
,
3269 text("Wrote ", result
, " instead of ", buffer
.length
,
3270 " objects of type ", T
.stringof
, " to file `",
3276 @disable this(this);
3282 if (auto p
= file_
._p
)
3284 if (p
.handle
) _FLOCK(p
.handle
);
3289 void put(T
)(auto ref scope const T value
)
3290 if (!hasIndirections
!T
&&
3293 rawWrite((&value
)[0 .. 1]);
3296 void put(T
)(scope const(T
)[] array
)
3297 if (!hasIndirections
!T
&&
3304 /** Returns an output range that locks the file and allows fast writing to it.
3307 Produce a grayscale image of the $(LINK2 https://en.wikipedia.org/wiki/Mandelbrot_set, Mandelbrot set)
3308 in binary $(LINK2 https://en.wikipedia.org/wiki/Netpbm_format, Netpbm format) to standard output.
3310 import std.algorithm, std.complex, std.range, std.stdio;
3315 writef("P5\n%d %d %d\n", size, size, ubyte.max);
3317 iota(-1, 3, 2.0/size).map!(y =>
3318 iota(-1.5, 0.5, 2.0/size).map!(x =>
3320 recurrence!((a, n) => x + y * complex(0, 1) + a[n-1]^^2)(complex(0))
3322 .countUntil!(z => z.re^^2 + z.im^^2 > 4))
3325 .copy(stdout.lockingBinaryWriter);
3329 auto lockingBinaryWriter()
3331 alias LockingBinaryWriterImpl
= BinaryWriterImpl
!true;
3335 import std
.typecons
: RefCounted
;
3336 alias LockingBinaryWriter
= RefCounted
!LockingBinaryWriterImpl
;
3339 alias LockingBinaryWriter
= LockingBinaryWriterImpl
;
3341 return LockingBinaryWriter(this);
3346 import std
.algorithm
.mutation
: reverse
;
3347 import std
.exception
: collectException
;
3348 static import std
.file
;
3349 import std
.range
: only
, retro
;
3350 import std
.string
: format
;
3352 auto deleteme
= testFilename();
3353 scope(exit
) collectException(std
.file
.remove(deleteme
));
3356 auto writer
= File(deleteme
, "wb").lockingBinaryWriter();
3357 auto input
= File(deleteme
, "rb");
3359 ubyte[1] byteIn
= [42];
3360 writer
.rawWrite(byteIn
);
3363 ubyte[1] byteOut
= input
.rawRead(new ubyte[1]);
3364 assert(byteIn
[0] == byteOut
[0]);
3367 auto output
= File(deleteme
, "wb");
3368 auto writer
= output
.lockingBinaryWriter();
3369 auto input
= File(deleteme
, "rb");
3371 T
[] readExact(T
)(T
[] buf
)
3373 auto result
= input
.rawRead(buf
);
3374 assert(result
.length
== buf
.length
,
3375 "Read %d out of %d bytes"
3376 .format(result
.length
, buf
.length
));
3382 byteIn
.only
.copy(writer
); output
.flush();
3383 ubyte byteOut
= readExact(new ubyte[1])[0];
3384 assert(byteIn
== byteOut
);
3387 ubyte[] bytesIn
= [1, 2, 3, 4, 5];
3388 bytesIn
.copy(writer
); output
.flush();
3389 ubyte[] bytesOut
= readExact(new ubyte[bytesIn
.length
]);
3390 scope(failure
) .writeln(bytesOut
);
3391 assert(bytesIn
== bytesOut
);
3393 // test ranges of values
3394 bytesIn
.retro
.copy(writer
); output
.flush();
3395 bytesOut
= readExact(bytesOut
);
3397 assert(bytesIn
== bytesOut
);
3400 "foobar".copy(writer
); output
.flush();
3401 char[] charsOut
= readExact(new char[6]);
3402 assert(charsOut
== "foobar");
3404 // test ranges of arrays
3405 only("foo", "bar").copy(writer
); output
.flush();
3406 charsOut
= readExact(charsOut
);
3407 assert(charsOut
== "foobar");
3409 // test that we are writing arrays as is,
3410 // without UTF-8 transcoding
3411 "foo"d
.copy(writer
); output
.flush();
3412 dchar[] dcharsOut
= readExact(new dchar[3]);
3413 assert(dcharsOut
== "foo");
3416 /** Returns the size of the file in bytes, ulong.max if file is not searchable or throws if the operation fails.
3419 import std.stdio, std.file;
3423 string deleteme = "delete.me";
3424 auto file_handle = File(deleteme, "w");
3425 file_handle.write("abc"); //create temporary file
3426 scope(exit) deleteme.remove; //remove temporary file at scope exit
3428 assert(file_handle.size() == 3); //check if file size is 3 bytes
3432 @property ulong size() @safe
3434 import std
.exception
: collectException
;
3437 if (collectException(pos
= tell
)) return ulong.max
;
3438 scope(exit
) seek(pos
);
3446 @system struct SystemToString
3454 @trusted struct TrustedToString
3462 @safe struct SafeToString
3470 @system void systemTests()
3472 //system code can write to files/stdout with anything!
3477 f
.write("just a string");
3478 f
.write("string with arg: ", 47);
3479 f
.write(SystemToString());
3480 f
.write(TrustedToString());
3481 f
.write(SafeToString());
3483 write("just a string");
3484 write("string with arg: ", 47);
3485 write(SystemToString());
3486 write(TrustedToString());
3487 write(SafeToString());
3489 f
.writeln("just a string");
3490 f
.writeln("string with arg: ", 47);
3491 f
.writeln(SystemToString());
3492 f
.writeln(TrustedToString());
3493 f
.writeln(SafeToString());
3495 writeln("just a string");
3496 writeln("string with arg: ", 47);
3497 writeln(SystemToString());
3498 writeln(TrustedToString());
3499 writeln(SafeToString());
3501 f
.writef("string with arg: %s", 47);
3502 f
.writef("%s", SystemToString());
3503 f
.writef("%s", TrustedToString());
3504 f
.writef("%s", SafeToString());
3506 writef("string with arg: %s", 47);
3507 writef("%s", SystemToString());
3508 writef("%s", TrustedToString());
3509 writef("%s", SafeToString());
3511 f
.writefln("string with arg: %s", 47);
3512 f
.writefln("%s", SystemToString());
3513 f
.writefln("%s", TrustedToString());
3514 f
.writefln("%s", SafeToString());
3516 writefln("string with arg: %s", 47);
3517 writefln("%s", SystemToString());
3518 writefln("%s", TrustedToString());
3519 writefln("%s", SafeToString());
3523 @safe void safeTests()
3527 //safe code can write to files only with @safe and @trusted code...
3530 f
.write("just a string");
3531 f
.write("string with arg: ", 47);
3532 f
.write(TrustedToString());
3533 f
.write(SafeToString());
3535 write("just a string");
3536 write("string with arg: ", 47);
3537 write(TrustedToString());
3538 write(SafeToString());
3540 f
.writeln("just a string");
3541 f
.writeln("string with arg: ", 47);
3542 f
.writeln(TrustedToString());
3543 f
.writeln(SafeToString());
3545 writeln("just a string");
3546 writeln("string with arg: ", 47);
3547 writeln(TrustedToString());
3548 writeln(SafeToString());
3550 f
.writef("string with arg: %s", 47);
3551 f
.writef("%s", TrustedToString());
3552 f
.writef("%s", SafeToString());
3554 writef("string with arg: %s", 47);
3555 writef("%s", TrustedToString());
3556 writef("%s", SafeToString());
3558 f
.writefln("string with arg: %s", 47);
3559 f
.writefln("%s", TrustedToString());
3560 f
.writefln("%s", SafeToString());
3562 writefln("string with arg: %s", 47);
3563 writefln("%s", TrustedToString());
3564 writefln("%s", SafeToString());
3567 static assert(!__traits(compiles
, f
.write(SystemToString().toString())));
3568 static assert(!__traits(compiles
, f
.writeln(SystemToString())));
3569 static assert(!__traits(compiles
, f
.writef("%s", SystemToString())));
3570 static assert(!__traits(compiles
, f
.writefln("%s", SystemToString())));
3572 static assert(!__traits(compiles
, write(SystemToString().toString())));
3573 static assert(!__traits(compiles
, writeln(SystemToString())));
3574 static assert(!__traits(compiles
, writef("%s", SystemToString())));
3575 static assert(!__traits(compiles
, writefln("%s", SystemToString())));
3584 import std
.exception
: collectException
;
3585 static import std
.file
;
3587 auto deleteme
= testFilename();
3588 scope(exit
) collectException(std
.file
.remove(deleteme
));
3589 std
.file
.write(deleteme
, "1 2 3");
3590 auto f
= File(deleteme
);
3591 assert(f
.size
== 5);
3592 assert(f
.tell
== 0);
3597 static import std
.file
;
3598 import std
.range
: chain
, only
, repeat
;
3599 import std
.range
.primitives
: isOutputRange
;
3601 auto deleteme
= testFilename();
3602 scope(exit
) std
.file
.remove(deleteme
);
3605 auto writer
= File(deleteme
, "w").lockingTextWriter();
3606 static assert(isOutputRange
!(typeof(writer
), dchar));
3611 writer
.put(chain(only('本'), only('語')));
3612 // https://issues.dlang.org/show_bug.cgi?id=11945
3613 writer
.put(repeat('#', 12));
3614 // https://issues.dlang.org/show_bug.cgi?id=17229
3615 writer
.put(cast(immutable(ubyte)[])"日本語");
3617 assert(File(deleteme
).readln() == "日本語日本語日本語日本語############日本語");
3620 @safe unittest // wchar -> char
3622 static import std
.file
;
3623 import std
.exception
: assertThrown
;
3624 import std
.utf
: UTFException
;
3626 auto deleteme
= testFilename();
3627 scope(exit
) std
.file
.remove(deleteme
);
3630 auto writer
= File(deleteme
, "w").lockingTextWriter();
3631 writer
.put("\U0001F608"w
);
3633 assert(std
.file
.readText
!string(deleteme
) == "\U0001F608");
3635 // Test invalid input: unpaired high surrogate
3637 immutable wchar surr
= "\U0001F608"w
[0];
3638 auto f
= File(deleteme
, "w");
3639 assertThrown
!UTFException(() {
3640 auto writer
= f
.lockingTextWriter();
3643 assertThrown
!UTFException(writer
.put(char('y')));
3644 assertThrown
!UTFException(writer
.put(wchar('y')));
3645 assertThrown
!UTFException(writer
.put(dchar('y')));
3646 assertThrown
!UTFException(writer
.put(surr
));
3647 // First `surr` is still unpaired at this point. `writer` gets
3648 // destroyed now, and the destructor throws a UTFException for
3649 // the unpaired surrogate.
3652 assert(std
.file
.readText
!string(deleteme
) == "x");
3654 // Test invalid input: unpaired low surrogate
3656 immutable wchar surr
= "\U0001F608"w
[1];
3657 auto writer
= File(deleteme
, "w").lockingTextWriter();
3658 assertThrown
!UTFException(writer
.put(surr
));
3660 assertThrown
!UTFException(writer
.put(surr
));
3662 assert(std
.file
.readText
!string(deleteme
) == "y");
3665 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18801
3667 static import std
.file
;
3668 import std
.string
: stripLeft
;
3670 auto deleteme
= testFilename();
3671 scope(exit
) std
.file
.remove(deleteme
);
3674 auto writer
= File(deleteme
, "w,ccs=UTF-8").lockingTextWriter();
3677 assert(std
.file
.readText
!string(deleteme
).stripLeft("\uFEFF") == "foo");
3680 auto writer
= File(deleteme
, "a,ccs=UTF-8").lockingTextWriter();
3683 assert(std
.file
.readText
!string(deleteme
).stripLeft("\uFEFF") == "foobar");
3685 @safe unittest // char/wchar -> wchar_t
3687 import core
.stdc
.locale
: LC_CTYPE
, setlocale
;
3688 import core
.stdc
.wchar_
: fwide
;
3689 import core
.stdc
.string
: strlen
;
3690 import std
.algorithm
.searching
: any
, endsWith
;
3691 import std
.conv
: text
;
3692 import std
.meta
: AliasSeq
;
3693 import std
.string
: fromStringz
, stripLeft
;
3694 static import std
.file
;
3695 auto deleteme
= testFilename();
3696 scope(exit
) std
.file
.remove(deleteme
);
3697 const char* oldCt
= () @trusted {
3698 const(char)* p
= setlocale(LC_CTYPE
, null);
3699 // Subsequent calls to `setlocale` might invalidate this return value,
3701 // See: https://github.com/dlang/phobos/pull/7660
3702 return p ? p
[0 .. strlen(p
) + 1].idup
.ptr
: null;
3704 const utf8
= ["en_US.UTF-8", "C.UTF-8", ".65001"].any
!((loc
) @trusted {
3705 return setlocale(LC_CTYPE
, loc
.ptr
).fromStringz
.endsWith(loc
);
3707 scope(exit
) () @trusted { setlocale(LC_CTYPE
, oldCt
); } ();
3708 alias strs
= AliasSeq
!("xä\U0001F607", "yö\U0001F608"w
);
3710 auto f
= File(deleteme
, "w");
3711 version (CRuntime_Microsoft
)
3713 () @trusted { _setmode(fileno(f
.getFP()), _O_U8TEXT
); } ();
3717 assert(fwide(f
.getFP(), 1) == 1);
3719 auto writer
= f
.lockingTextWriter();
3720 assert(writer
.orientation_
== 1);
3721 static foreach (s
; strs
) writer
.put(s
);
3723 assert(std
.file
.readText
!string(deleteme
).stripLeft("\uFEFF") ==
3726 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=18789
3728 static import std
.file
;
3729 auto deleteme
= testFilename();
3730 scope(exit
) std
.file
.remove(deleteme
);
3731 // converting to char
3733 auto f
= File(deleteme
, "w");
3734 f
.writeln("\U0001F608"w
); // UTFException
3736 // converting to wchar_t
3738 auto f
= File(deleteme
, "w,ccs=UTF-16LE");
3740 f
.writeln("ö"); // writes garbage
3741 f
.writeln("\U0001F608"); // ditto
3743 f
.writeln("\U0001F608"w
); // leads to ErrnoException
3749 import std
.exception
: collectException
;
3750 auto e
= collectException({ File f
; f
.writeln("Hello!"); }());
3751 assert(e
&& e
.msg
== "Attempting to write to closed File");
3754 @safe unittest // https://issues.dlang.org/show_bug.cgi?id=21592
3756 import std
.exception
: collectException
;
3757 import std
.utf
: UTFException
;
3758 static import std
.file
;
3759 auto deleteme
= testFilename();
3760 scope(exit
) std
.file
.remove(deleteme
);
3761 auto f
= File(deleteme
, "w");
3762 auto e
= collectException
!UTFException(f
.writeln(wchar(0xD801)));
3763 assert(e
.next
is null);
3766 version (StdStressTest
)
3768 // https://issues.dlang.org/show_bug.cgi?id=15768
3771 import std
.parallelism
: parallel
;
3772 import std
.range
: iota
;
3774 auto deleteme
= testFilename();
3775 stderr
= File(deleteme
, "w");
3777 foreach (t
; 1_000_000.iota
.parallel
)
3779 stderr
.write("aaa");
3784 /// Used to specify the lock type for `File.lock` and `File.tryLock`.
3788 * Specifies a _read (shared) lock. A _read lock denies all processes
3789 * write access to the specified region of the file, including the
3790 * process that first locks the region. All processes can _read the
3791 * locked region. Multiple simultaneous _read locks are allowed, as
3792 * long as there are no exclusive locks.
3797 * Specifies a read/write (exclusive) lock. A read/write lock denies all
3798 * other processes both read and write access to the locked file region.
3799 * If a segment has an exclusive lock, it may not have any shared locks
3800 * or other exclusive locks.
3805 struct LockingTextReader
3808 private char _front
;
3809 private bool _hasChar
;
3813 import std
.exception
: enforce
;
3814 enforce(f
.isOpen
, "LockingTextReader: File must be open");
3816 _FLOCK(_f
._p
.handle
);
3821 _FLOCK(_f
._p
.handle
);
3827 ungetc(_front
, cast(FILE
*)_f
._p
.handle
);
3829 // File locking has its own reference count
3830 if (_f
.isOpen
) _FUNLOCK(_f
._p
.handle
);
3833 void opAssign(LockingTextReader r
)
3835 import std
.algorithm
.mutation
: swap
;
3839 @property bool empty()
3843 if (!_f
.isOpen || _f
.eof
)
3845 immutable int c
= _FGETC(cast(_iobuf
*) _f
._p
.handle
);
3851 _front
= cast(char) c
;
3857 @property char front()
3863 import core
.exception
: RangeError
;
3865 throw new RangeError();
3885 // @system due to readf
3886 static import std
.file
;
3887 import std
.range
.primitives
: isInputRange
;
3889 static assert(isInputRange
!LockingTextReader
);
3890 auto deleteme
= testFilename();
3891 std
.file
.write(deleteme
, "1 2 3");
3892 scope(exit
) std
.file
.remove(deleteme
);
3894 auto f
= File(deleteme
);
3903 // https://issues.dlang.org/show_bug.cgi?id=13686
3906 import std
.algorithm
.comparison
: equal
;
3907 static import std
.file
;
3908 import std
.utf
: byDchar
;
3910 auto deleteme
= testFilename();
3911 std
.file
.write(deleteme
, "Тест");
3912 scope(exit
) std
.file
.remove(deleteme
);
3915 File(deleteme
).readf("%s", &s
);
3916 assert(s
== "Тест");
3918 auto ltr = LockingTextReader(File(deleteme
)).byDchar
;
3919 assert(equal(ltr, "Тест".byDchar
));
3922 // https://issues.dlang.org/show_bug.cgi?id=12320
3925 static import std
.file
;
3926 auto deleteme
= testFilename();
3927 std
.file
.write(deleteme
, "ab");
3928 scope(exit
) std
.file
.remove(deleteme
);
3929 auto ltr = LockingTextReader(File(deleteme
));
3930 assert(ltr.front
== 'a');
3932 assert(ltr.front
== 'b');
3937 // https://issues.dlang.org/show_bug.cgi?id=14861
3940 // @system due to readf
3941 static import std
.file
;
3942 auto deleteme
= testFilename();
3943 File fw
= File(deleteme
, "w");
3944 for (int i
; i
!= 5000; i
++)
3945 fw
.writeln(i
, ";", "Иванов;Пётр;Петрович");
3947 scope(exit
) std
.file
.remove(deleteme
);
3949 File fr
= File(deleteme
, "r");
3950 scope (exit
) fr
.close();
3951 int nom
; string fam
, nam
, ot
;
3952 // Error format read
3954 fr
.readf("%s;%s;%s;%s\n", &nom
, &fam
, &nam
, &ot
);
3958 * Indicates whether `T` is a file handle, i.e. the type
3959 * is implicitly convertable to $(LREF File) or a pointer to a
3960 * $(REF FILE, core,stdc,stdio).
3963 * `true` if `T` is a file handle, `false` otherwise.
3965 template isFileHandle(T
)
3967 enum isFileHandle
= is(T
: FILE
*) ||
3974 static assert(isFileHandle
!(FILE
*));
3975 static assert(isFileHandle
!(File
));
3979 * Property used by writeln/etc. so it can infer @safe since stdout is __gshared
3981 private @property File
trustedStdout() @trusted
3986 /***********************************
3987 Writes its arguments in text format to standard output (without a trailing newline).
3990 args = the items to write to `stdout`
3992 Throws: In case of an I/O error, throws an `StdioException`.
3995 Reads `stdin` and writes it to `stdout` with an argument
4004 for (size_t count = 0; (line = readln) !is null; count++)
4006 write("Input ", count, ": ", line, "\n");
4011 void write(T
...)(T args
)
4012 if (!is(T
[0] : File
))
4014 trustedStdout
.write(args
);
4019 static import std
.file
;
4021 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
4023 if (false) write(buf
);
4025 auto deleteme
= testFilename();
4026 auto f
= File(deleteme
, "w");
4027 f
.write("Hello, ", "world number ", 42, "!");
4029 scope(exit
) { std
.file
.remove(deleteme
); }
4030 assert(cast(char[]) std
.file
.read(deleteme
) == "Hello, world number 42!");
4033 /***********************************
4034 * Equivalent to `write(args, '\n')`. Calling `writeln` without
4035 * arguments is valid and just prints a newline to the standard
4039 * args = the items to write to `stdout`
4042 * In case of an I/O error, throws an $(LREF StdioException).
4044 * Reads `stdin` and writes it to `stdout` with an argument
4053 for (size_t count = 0; (line = readln) !is null; count++)
4055 writeln("Input ", count, ": ", line);
4060 void writeln(T
...)(T args
)
4062 static if (T
.length
== 0)
4064 import std
.exception
: enforce
;
4066 enforce(fputc('\n', .trustedStdout
._p
.handle
) != EOF
, "fputc failed");
4068 else static if (T
.length
== 1 &&
4069 is(T
[0] : const(char)[]) &&
4070 (is(T
[0] == U
[], U
) ||
__traits(isStaticArray
, T
[0])))
4072 // Specialization for strings - a very frequent case
4073 auto w
= .trustedStdout
.lockingTextWriter();
4075 static if (__traits(isStaticArray
, T
[0]))
4087 // Most general instance
4088 trustedStdout
.write(args
, '\n');
4094 // Just make sure the call compiles
4095 if (false) writeln();
4097 if (false) writeln("wyda");
4099 // https://issues.dlang.org/show_bug.cgi?id=8040
4100 if (false) writeln(null);
4101 if (false) writeln(">", null, "<");
4103 // https://issues.dlang.org/show_bug.cgi?id=14041
4117 static import std
.file
;
4119 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
4122 auto deleteme
= testFilename();
4123 auto f
= File(deleteme
, "w");
4124 scope(exit
) { std
.file
.remove(deleteme
); }
4125 f
.writeln("Hello, ", "world number ", 42, "!");
4128 assert(cast(char[]) std
.file
.read(deleteme
) ==
4129 "Hello, world number 42!\r\n");
4131 assert(cast(char[]) std
.file
.read(deleteme
) ==
4132 "Hello, world number 42!\n");
4134 // test writeln on stdout
4135 auto saveStdout
= stdout
;
4136 scope(exit
) stdout
= saveStdout
;
4137 stdout
.open(deleteme
, "w");
4138 writeln("Hello, ", "world number ", 42, "!");
4141 assert(cast(char[]) std
.file
.read(deleteme
) ==
4142 "Hello, world number 42!\r\n");
4144 assert(cast(char[]) std
.file
.read(deleteme
) ==
4145 "Hello, world number 42!\n");
4147 stdout
.open(deleteme
, "w");
4149 writeln("Hello!"w
); // https://issues.dlang.org/show_bug.cgi?id=8386
4150 writeln("Hello!"d
); // https://issues.dlang.org/show_bug.cgi?id=8386
4151 writeln("embedded\0null"c
); // https://issues.dlang.org/show_bug.cgi?id=8730
4154 assert(cast(char[]) std
.file
.read(deleteme
) ==
4155 "Hello!\r\nHello!\r\nHello!\r\nembedded\0null\r\n");
4157 assert(cast(char[]) std
.file
.read(deleteme
) ==
4158 "Hello!\nHello!\nHello!\nembedded\0null\n");
4163 static import std
.file
;
4165 auto deleteme
= testFilename();
4166 auto f
= File(deleteme
, "w");
4167 scope(exit
) { std
.file
.remove(deleteme
); }
4169 enum EI
: int { A
, B
}
4170 enum ED
: double { A
= 0, B
} // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4171 enum EC
: char { A
= 0, B
} // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
4172 enum ES
: string
{ A
= "aaa", B
= "bbb" }
4174 f
.writeln(EI
.A
); // false, but A on 2.058
4175 f
.writeln(EI
.B
); // true, but B on 2.058
4177 f
.writeln(ED
.A
); // A
4178 f
.writeln(ED
.B
); // B
4180 f
.writeln(EC
.A
); // A
4181 f
.writeln(EC
.B
); // B
4183 f
.writeln(ES
.A
); // A
4184 f
.writeln(ES
.B
); // B
4188 assert(cast(char[]) std
.file
.read(deleteme
) ==
4189 "A\r\nB\r\nA\r\nB\r\nA\r\nB\r\nA\r\nB\r\n");
4191 assert(cast(char[]) std
.file
.read(deleteme
) ==
4192 "A\nB\nA\nB\nA\nB\nA\nB\n");
4197 static auto useInit(T
)(T ltw
)
4204 useInit(stdout
.lockingTextWriter());
4209 // https://issues.dlang.org/show_bug.cgi?id=21920
4210 void function(string
) printer
= &writeln
!string
;
4211 if (false) printer("Hello");
4215 /***********************************
4216 Writes formatted data to standard output (without a trailing newline).
4219 fmt = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4220 When passed as a compile-time argument, the string will be statically checked
4221 against the argument types passed.
4222 args = Items to write.
4224 Note: In older versions of Phobos, it used to be possible to write:
4227 writef(stderr, "%s", "message");
4230 to print a message to `stderr`. This syntax is no longer supported, and has
4234 stderr.writef("%s", "message");
4238 void writef(alias fmt
, A
...)(A args
)
4239 if (isSomeString
!(typeof(fmt
)))
4241 import std
.format
: checkFormatException
;
4243 alias e
= checkFormatException
!(fmt
, A
);
4244 static assert(!e
, e
);
4245 return .writef(fmt
, args
);
4249 void writef(Char
, A
...)(in Char
[] fmt
, A args
)
4251 trustedStdout
.writef(fmt
, args
);
4256 static import std
.file
;
4258 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
4261 auto deleteme
= testFilename();
4262 auto f
= File(deleteme
, "w");
4263 scope(exit
) { std
.file
.remove(deleteme
); }
4264 f
.writef
!"Hello, %s world number %s!"("nice", 42);
4266 assert(cast(char[]) std
.file
.read(deleteme
) == "Hello, nice world number 42!");
4267 // test write on stdout
4268 auto saveStdout
= stdout
;
4269 scope(exit
) stdout
= saveStdout
;
4270 stdout
.open(deleteme
, "w");
4271 writef
!"Hello, %s world number %s!"("nice", 42);
4273 assert(cast(char[]) std
.file
.read(deleteme
) == "Hello, nice world number 42!");
4276 /***********************************
4277 * Equivalent to $(D writef(fmt, args, '\n')).
4279 void writefln(alias fmt
, A
...)(A args
)
4280 if (isSomeString
!(typeof(fmt
)))
4282 import std
.format
: checkFormatException
;
4284 alias e
= checkFormatException
!(fmt
, A
);
4285 static assert(!e
, e
);
4286 return .writefln(fmt
, args
);
4290 void writefln(Char
, A
...)(in Char
[] fmt
, A args
)
4292 trustedStdout
.writefln(fmt
, args
);
4297 static import std
.file
;
4299 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
4301 // test File.writefln
4302 auto deleteme
= testFilename();
4303 auto f
= File(deleteme
, "w");
4304 scope(exit
) { std
.file
.remove(deleteme
); }
4305 f
.writefln
!"Hello, %s world number %s!"("nice", 42);
4308 assert(cast(char[]) std
.file
.read(deleteme
) ==
4309 "Hello, nice world number 42!\r\n");
4311 assert(cast(char[]) std
.file
.read(deleteme
) ==
4312 "Hello, nice world number 42!\n",
4313 cast(char[]) std
.file
.read(deleteme
));
4316 auto saveStdout
= stdout
;
4317 scope(exit
) stdout
= saveStdout
;
4318 stdout
.open(deleteme
, "w");
4319 writefln
!"Hello, %s world number %s!"("nice", 42);
4322 assert(cast(char[]) std
.file
.read(deleteme
) ==
4323 "Hello, nice world number 42!\r\n");
4325 assert(cast(char[]) std
.file
.read(deleteme
) ==
4326 "Hello, nice world number 42!\n");
4330 * Reads formatted data from `stdin` using $(REF formattedRead, std,_format).
4332 * format = The $(REF_ALTTEXT format string, formattedWrite, std, _format).
4333 * When passed as a compile-time argument, the string will be statically checked
4334 * against the argument types passed.
4335 * args = Items to be read.
4337 * Same as `formattedRead`: The number of variables filled. If the input range `r` ends early,
4338 * this number will be less than the number of variables provided.
4354 % echo "1 2 3" | rdmd test.d
4360 uint readf(alias format
, A
...)(auto ref A args
)
4361 if (isSomeString
!(typeof(format
)))
4363 import std
.format
: checkFormatException
;
4365 alias e
= checkFormatException
!(format
, A
);
4366 static assert(!e
, e
);
4367 return .readf(format
, args
);
4371 uint readf(A
...)(scope const(char)[] format
, auto ref A args
)
4373 return stdin
.readf(format
, args
);
4379 if (false) readf("%s", &f
);
4384 if (false) readf("%s %s %s", a
, b
, c
);
4385 // backwards compatibility with pointers
4386 if (false) readf("%s %s %s", a
, &b
, c
);
4387 if (false) readf("%s %s %s", &a
, &b
, &c
);
4390 /**********************************
4391 * Read line from `stdin`.
4393 * This version manages its own read buffer, which means one memory allocation per call. If you are not
4394 * retaining a reference to the read data, consider the `readln(buf)` version, which may offer
4395 * better performance as it can reuse its read buffer.
4398 * The line that was read, including the line terminator character.
4400 * S = Template parameter; the type of the allocated buffer, and the type returned. Defaults to `string`.
4401 * terminator = Line terminator (by default, `'\n'`).
4403 * String terminators are not supported due to ambiguity with readln(buf) below.
4405 * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4407 * Reads `stdin` and writes it to `stdout`.
4414 while ((line = readln()) !is null)
4419 S
readln(S
= string
)(dchar terminator
= '\n')
4422 return stdin
.readln
!S(terminator
);
4425 /**********************************
4426 * Read line from `stdin` and write it to buf[], including terminating character.
4428 * This can be faster than $(D line = readln()) because you can reuse
4429 * the buffer for each call. Note that reusing the buffer means that you
4430 * must copy the previous contents if you wish to retain them.
4433 * `size_t` 0 for end of file, otherwise number of characters read
4435 * buf = Buffer used to store the resulting line data. buf is resized as necessary.
4436 * terminator = Line terminator (by default, `'\n'`). Use $(REF newline, std,ascii)
4437 * for portability (unless the file was opened in text mode).
4439 * `StdioException` on I/O error, or `UnicodeException` on Unicode conversion error.
4441 * Reads `stdin` and writes it to `stdout`.
4453 size_t
readln(C
)(ref C
[] buf
, dchar terminator
= '\n')
4454 if (isSomeChar
!C
&& is(Unqual
!C
== C
) && !is(C
== enum))
4456 return stdin
.readln(buf
, terminator
);
4460 size_t
readln(C
, R
)(ref C
[] buf
, R terminator
)
4461 if (isSomeChar
!C
&& is(Unqual
!C
== C
) && !is(C
== enum) &&
4462 isBidirectionalRange
!R
&& is(typeof(terminator
.front
== dchar.init
)))
4464 return stdin
.readln(buf
, terminator
);
4469 import std
.meta
: AliasSeq
;
4471 //we can't actually test readln, so at the very least,
4472 //we test compilability
4477 static foreach (String
; AliasSeq
!(string
, char[], wstring
, wchar[], dstring
, dchar[]))
4480 readln
!String('\t');
4482 static foreach (String
; AliasSeq
!(char[], wchar[], dchar[]))
4487 readln(buf
, "<br />");
4493 * Convenience function that forwards to `core.sys.posix.stdio.fopen`
4494 * (to `_wfopen` on Windows)
4495 * with appropriately-constructed C-style strings.
4497 private FILE
* _fopen(R1
, R2
)(R1 name
, R2 mode
= "r")
4498 if ((isSomeFiniteCharInputRange
!R1 || isSomeString
!R1
) &&
4499 (isSomeFiniteCharInputRange
!R2 || isSomeString
!R2
))
4501 import std
.internal
.cstring
: tempCString
;
4503 auto namez
= name
.tempCString
!FSChar();
4504 auto modez
= mode
.tempCString
!FSChar();
4506 static _fopenImpl(scope const(FSChar
)* namez
, scope const(FSChar
)* modez
) @trusted nothrow @nogc
4510 return _wfopen(namez
, modez
);
4512 else version (Posix
)
4515 * The new opengroup large file support API is transparently
4516 * included in the normal C bindings. https://www.opengroup.org/platform/lfs.html#1.0
4517 * if _FILE_OFFSET_BITS in druntime is 64, off_t is 64 bit and
4518 * the normal functions work fine. If not, then large file support
4519 * probably isn't available. Do not use the old transitional API
4520 * (the native extern(C) fopen64, https://unix.org/version2/whatsnew/lfs20mar.html#3.0)
4522 import core
.sys
.posix
.stdio
: fopen
;
4523 return fopen(namez
, modez
);
4527 return fopen(namez
, modez
);
4530 return _fopenImpl(namez
, modez
);
4535 /***********************************
4536 * Convenience function that forwards to `core.sys.posix.stdio.popen`
4537 * with appropriately-constructed C-style strings.
4539 FILE
* _popen(R1
, R2
)(R1 name
, R2 mode
= "r") @trusted nothrow @nogc
4540 if ((isSomeFiniteCharInputRange
!R1 || isSomeString
!R1
) &&
4541 (isSomeFiniteCharInputRange
!R2 || isSomeString
!R2
))
4543 import std
.internal
.cstring
: tempCString
;
4545 auto namez
= name
.tempCString
!FSChar();
4546 auto modez
= mode
.tempCString
!FSChar();
4548 static popenImpl(const(FSChar
)* namez
, const(FSChar
)* modez
) @trusted nothrow @nogc
4550 import core
.sys
.posix
.stdio
: popen
;
4551 return popen(namez
, modez
);
4553 return popenImpl(namez
, modez
);
4558 * Convenience function that forwards to `core.stdc.stdio.fwrite`
4560 private auto trustedFwrite(T
)(FILE
* f
, const T
[] obj
) @trusted
4562 return fwrite(obj
.ptr
, T
.sizeof
, obj
.length
, f
);
4566 * Convenience function that forwards to `core.stdc.stdio.fread`
4568 private auto trustedFread(T
)(FILE
* f
, T
[] obj
) @trusted
4569 if (!imported
!"std.traits".hasIndirections
!T
)
4571 return fread(obj
.ptr
, T
.sizeof
, obj
.length
, f
);
4574 private auto trustedFread(T
)(FILE
* f
, T
[] obj
) @system
4575 if (imported
!"std.traits".hasIndirections
!T
)
4577 return fread(obj
.ptr
, T
.sizeof
, obj
.length
, f
);
4581 * Iterates through the lines of a file by using `foreach`.
4588 foreach (string line; lines(stdin))
4594 The line terminator (`'\n'` by default) is part of the string read (it
4595 could be missing in the last line of the file). Several types are
4596 supported for `line`, and the behavior of `lines`
4597 changes accordingly:
4599 $(OL $(LI If `line` has type `string`, $(D
4600 wstring), or `dstring`, a new string of the respective type
4601 is allocated every read.) $(LI If `line` has type $(D
4602 char[]), `wchar[]`, `dchar[]`, the line's content
4603 will be reused (overwritten) across reads.) $(LI If `line`
4604 has type `immutable(ubyte)[]`, the behavior is similar to
4605 case (1), except that no UTF checking is attempted upon input.) $(LI
4606 If `line` has type `ubyte[]`, the behavior is
4607 similar to case (2), except that no UTF checking is attempted upon
4610 In all cases, a two-symbols versions is also accepted, in which case
4611 the first symbol (of integral type, e.g. `ulong` or $(D
4612 uint)) tracks the zero-based number of the current line.
4616 foreach (ulong i, string line; lines(stdin))
4622 In case of an I/O error, an `StdioException` is thrown.
4631 private dchar terminator
= '\n';
4636 f = File to read lines from.
4637 terminator = Line separator (`'\n'` by default).
4639 this(File f
, dchar terminator
= '\n') @safe
4642 this.terminator
= terminator
;
4645 int opApply(D
)(scope D dg
)
4647 import std
.traits
: Parameters
;
4648 alias Parms
= Parameters
!(dg
);
4649 static if (isSomeString
!(Parms
[$ - 1]))
4652 static if (is(Parms
[$ - 1] : const(char)[]))
4654 else static if (is(Parms
[$ - 1] : const(wchar)[]))
4656 else static if (is(Parms
[$ - 1] : const(dchar)[]))
4659 static if (Parms
.length
== 2)
4663 import std
.conv
: to
;
4665 if (!f
.readln(line
, terminator
)) break;
4666 auto copy
= to
!(Parms
[$ - 1])(line
);
4667 static if (Parms
.length
== 2)
4669 result
= dg(i
, copy
);
4676 if (result
!= 0) break;
4683 return opApplyRaw(dg
);
4687 int opApplyRaw(D
)(scope D dg
)
4689 import std
.conv
: to
;
4690 import std
.exception
: assumeUnique
;
4691 import std
.traits
: Parameters
;
4693 alias Parms
= Parameters
!(dg
);
4694 enum duplicate
= is(Parms
[$ - 1] : immutable(ubyte)[]);
4697 _FLOCK(f
._p
.handle
);
4698 scope(exit
) _FUNLOCK(f
._p
.handle
);
4700 static if (Parms
.length
== 2)
4702 while ((c
= _FGETC(cast(_iobuf
*) f
._p
.handle
)) != -1)
4704 buffer
~= to
!(ubyte)(c
);
4705 if (c
== terminator
)
4707 static if (duplicate
)
4708 auto arg
= assumeUnique(buffer
);
4711 // unlock the file while calling the delegate
4712 _FUNLOCK(f
._p
.handle
);
4713 scope(exit
) _FLOCK(f
._p
.handle
);
4714 static if (Parms
.length
== 1)
4720 result
= dg(line
, arg
);
4724 static if (!duplicate
)
4728 // can only reach when _FGETC returned -1
4729 if (!f
.eof
) throw new StdioException("Error in reading file"); // error occured
4737 As pointed out in <https://github.com/dlang/phobos/issues/10605>,
4738 it's a pity that `byLine()` & co. aren't @safe to use yet.
4740 This is a first attempt at working towards that goal.
4741 For now, this test doesn't do much; as there isn't much to do safely yet.
4743 auto deleteMe
= testFilename();
4744 scope(exit
) { imported
!"std.file".remove(deleteMe
); }
4748 auto f
= File(deleteMe
, "w");
4749 scope(exit
) { f
.close(); }
4750 foreach (i
; 1 .. 11)
4756 auto f
= File(deleteMe
, "r");
4757 scope(exit
) { f
.close(); }
4759 auto myLines
= lines(f
);
4760 foreach (string line
; myLines
)
4766 auto f
= File(deleteMe
, "r");
4767 scope(exit
) { f
.close(); }
4769 auto myByLineCopy
= f
.byLineCopy
;
4770 foreach (line
; myByLineCopy
)
4775 auto f
= File(deleteMe
, "r");
4776 scope(exit
) { f
.close(); }
4778 auto myByLine
= f
.byLine
;
4779 foreach (line
; myByLine
)
4786 static import std
.file
;
4787 import std
.meta
: AliasSeq
;
4789 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
4791 auto deleteme
= testFilename();
4792 scope(exit
) { std
.file
.remove(deleteme
); }
4795 AliasSeq
!(string
, wstring
, dstring
,
4796 char[], wchar[], dchar[]);
4797 foreach (T
; TestedWith
)
4799 // test looping with an empty file
4800 std
.file
.write(deleteme
, "");
4801 auto f
= File(deleteme
, "r");
4802 foreach (T line
; lines(f
))
4808 // test looping with a file with three lines
4809 std
.file
.write(deleteme
, "Line one\nline two\nline three\n");
4810 f
.open(deleteme
, "r");
4812 foreach (T line
; lines(f
))
4814 if (i
== 0) assert(line
== "Line one\n");
4815 else if (i
== 1) assert(line
== "line two\n");
4816 else if (i
== 2) assert(line
== "line three\n");
4822 // test looping with a file with three lines, last without a newline
4823 std
.file
.write(deleteme
, "Line one\nline two\nline three");
4824 f
.open(deleteme
, "r");
4826 foreach (T line
; lines(f
))
4828 if (i
== 0) assert(line
== "Line one\n");
4829 else if (i
== 1) assert(line
== "line two\n");
4830 else if (i
== 2) assert(line
== "line three");
4837 // test with ubyte[] inputs
4838 alias TestedWith2
= AliasSeq
!(immutable(ubyte)[], ubyte[]);
4839 foreach (T
; TestedWith2
)
4841 // test looping with an empty file
4842 std
.file
.write(deleteme
, "");
4843 auto f
= File(deleteme
, "r");
4844 foreach (T line
; lines(f
))
4850 // test looping with a file with three lines
4851 std
.file
.write(deleteme
, "Line one\nline two\nline three\n");
4852 f
.open(deleteme
, "r");
4854 foreach (T line
; lines(f
))
4856 if (i
== 0) assert(cast(char[]) line
== "Line one\n");
4857 else if (i
== 1) assert(cast(char[]) line
== "line two\n",
4858 T
.stringof
~ " " ~ cast(char[]) line
);
4859 else if (i
== 2) assert(cast(char[]) line
== "line three\n");
4865 // test looping with a file with three lines, last without a newline
4866 std
.file
.write(deleteme
, "Line one\nline two\nline three");
4867 f
.open(deleteme
, "r");
4869 foreach (T line
; lines(f
))
4871 if (i
== 0) assert(cast(char[]) line
== "Line one\n");
4872 else if (i
== 1) assert(cast(char[]) line
== "line two\n");
4873 else if (i
== 2) assert(cast(char[]) line
== "line three");
4881 static foreach (T
; AliasSeq
!(ubyte[]))
4883 // test looping with a file with three lines, last without a newline
4884 // using a counter too this time
4885 std
.file
.write(deleteme
, "Line one\nline two\nline three");
4886 auto f
= File(deleteme
, "r");
4888 foreach (ulong j
, T line
; lines(f
))
4890 if (i
== 0) assert(cast(char[]) line
== "Line one\n");
4891 else if (i
== 1) assert(cast(char[]) line
== "line two\n");
4892 else if (i
== 2) assert(cast(char[]) line
== "line three");
4901 Iterates through a file a chunk at a time by using `foreach`.
4908 foreach (ubyte[] buffer; chunks(stdin, 4096))
4915 The content of `buffer` is reused across calls. In the
4916 example above, `buffer.length` is 4096 for all iterations,
4917 except for the last one, in which case `buffer.length` may
4918 be less than 4096 (but always greater than zero).
4920 In case of an I/O error, an `StdioException` is thrown.
4922 auto chunks(File f
, size_t size
)
4924 return ChunksImpl(f
, size
);
4926 private struct ChunksImpl
4929 private size_t size
;
4930 // private string fileName; // Currently, no use
4932 this(File f
, size_t size
)
4935 assert(size
, "size must be larger than 0");
4943 int opApply(D
)(scope D dg
)
4945 import core
.stdc
.stdlib
: alloca
;
4946 import std
.exception
: enforce
;
4948 enforce(f
.isOpen
, "Attempting to read from an unopened file");
4949 enum maxStackSize
= 1024 * 16;
4950 ubyte[] buffer
= void;
4951 if (size
< maxStackSize
)
4952 buffer
= (cast(ubyte*) alloca(size
))[0 .. size
];
4954 buffer
= new ubyte[size
];
4958 while ((r
= trustedFread(f
._p
.handle
, buffer
)) > 0)
4964 if (!f
.eof
) throw new StdioException(null);
4967 static if (is(typeof(dg(tally
, buffer
))))
4969 if ((result
= dg(tally
, buffer
)) != 0) break;
4973 if ((result
= dg(buffer
)) != 0) break;
4983 static import std
.file
;
4985 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
4987 auto deleteme
= testFilename();
4988 scope(exit
) { std
.file
.remove(deleteme
); }
4990 // test looping with an empty file
4991 std
.file
.write(deleteme
, "");
4992 auto f
= File(deleteme
, "r");
4993 foreach (ubyte[] line
; chunks(f
, 4))
4999 // test looping with a file with three lines
5000 std
.file
.write(deleteme
, "Line one\nline two\nline three\n");
5001 f
= File(deleteme
, "r");
5003 foreach (ubyte[] line
; chunks(f
, 3))
5005 if (i
== 0) assert(cast(char[]) line
== "Lin");
5006 else if (i
== 1) assert(cast(char[]) line
== "e o");
5007 else if (i
== 2) assert(cast(char[]) line
== "ne\n");
5014 // Issue 21730 - null ptr dereferenced in ChunksImpl.opApply (SIGSEGV)
5017 import std
.exception
: assertThrown
;
5018 static import std
.file
;
5020 auto deleteme
= testFilename();
5021 scope(exit
) { if (std
.file
.exists(deleteme
)) std
.file
.remove(deleteme
); }
5023 auto err1
= File(deleteme
, "w+x");
5025 std
.file
.remove(deleteme
);
5026 assertThrown(() {foreach (ubyte[] buf
; chunks(err1
, 4096)) {}}());
5030 Writes an array or range to a file.
5031 Shorthand for $(D data.copy(File(fileName, "wb").lockingBinaryWriter)).
5032 Similar to $(REF write, std,file), strings are written as-is,
5033 rather than encoded according to the `File`'s $(HTTP
5034 en.cppreference.com/w/c/io#Narrow_and_wide_orientation,
5037 void toFile(T
)(T data
, string fileName
)
5038 if (is(typeof(copy(data
, stdout
.lockingBinaryWriter
))))
5040 copy(data
, File(fileName
, "wb").lockingBinaryWriter
);
5045 static import std
.file
;
5047 auto deleteme
= testFilename();
5048 scope(exit
) { std
.file
.remove(deleteme
); }
5050 "Test".toFile(deleteme
);
5051 assert(std
.file
.readText(deleteme
) == "Test");
5054 /*********************
5055 * Thrown if I/O errors happen.
5057 class StdioException
: Exception
5059 static import core
.stdc
.errno
;
5060 /// Operating system error code.
5064 Initialize with a message and an error code.
5066 this(string message
, uint e
= core
.stdc
.errno
.errno
) @trusted
5068 import std
.exception
: errnoString
;
5070 auto sysmsg
= errnoString(errno
);
5071 // If e is 0, we don't use the system error message. (The message
5072 // is "Success", which is rather pointless for an exception.)
5073 super(e
== 0 ? message
5074 : (message ? message
~ " (" ~ sysmsg
~ ")" : sysmsg
));
5077 /** Convenience functions that throw an `StdioException`. */
5078 static void opCall(string msg
) @safe
5080 throw new StdioException(msg
);
5084 static void opCall() @safe
5086 throw new StdioException(null, core
.stdc
.errno
.errno
);
5090 enum StdFileHandle
: string
5092 stdin
= "core.stdc.stdio.stdin",
5093 stdout
= "core.stdc.stdio.stdout",
5094 stderr
= "core.stdc.stdio.stderr",
5097 // Undocumented but public because the std* handles are aliasing it.
5098 @property ref File
makeGlobal(StdFileHandle _iob
)()
5100 __gshared File
.Impl impl
;
5101 __gshared File result
;
5103 // Use an inline spinlock to make sure the initializer is only run once.
5104 // We assume there will be at most uint.max / 2 threads trying to initialize
5105 // `handle` at once and steal the high bit to indicate that the globals have
5106 // been initialized.
5107 static shared uint spinlock
;
5108 import core
.atomic
: atomicLoad
, atomicOp
, MemoryOrder
;
5109 if (atomicLoad
!(MemoryOrder
.acq
)(spinlock
) <= uint.max
/ 2)
5113 if (atomicLoad
!(MemoryOrder
.acq
)(spinlock
) > uint.max
/ 2)
5115 if (atomicOp
!"+="(spinlock
, 1) == 1)
5117 with (StdFileHandle
)
5118 assert(_iob
== stdin || _iob
== stdout || _iob
== stderr
);
5119 impl
.handle
= cast() mixin(_iob
);
5121 atomicOp
!"+="(spinlock
, uint.max
/ 2);
5124 atomicOp
!"-="(spinlock
, 1);
5130 /** The standard input stream.
5133 stdin as a $(LREF File).
5136 The returned $(LREF File) wraps $(REF stdin,core,stdc,stdio), and
5137 is therefore thread global. Reassigning `stdin` to a different
5138 `File` must be done in a single-threaded or locked context in
5139 order to avoid race conditions.
5141 All reading from `stdin` automatically locks the file globally,
5142 and will cause all other threads calling `read` to wait until
5143 the lock is released.
5145 alias stdin
= makeGlobal
!(StdFileHandle
.stdin
);
5150 // Read stdin, sort lines, write to stdout
5151 import std
.algorithm
.mutation
: copy
;
5152 import std
.algorithm
.sorting
: sort
;
5153 import std
.array
: array
;
5154 import std
.typecons
: Yes
;
5158 stdin
// read from stdin
5159 .byLineCopy(Yes
.keepTerminator
) // copying each line
5160 .array() // convert to array of lines
5161 .sort() // sort the lines
5162 .copy( // copy output of .sort to an OutputRange
5163 stdout
.lockingTextWriter()); // the OutputRange
5168 The standard output stream.
5171 stdout as a $(LREF File).
5174 The returned $(LREF File) wraps $(REF stdout,core,stdc,stdio), and
5175 is therefore thread global. Reassigning `stdout` to a different
5176 `File` must be done in a single-threaded or locked context in
5177 order to avoid race conditions.
5179 All writing to `stdout` automatically locks the file globally,
5180 and will cause all other threads calling `write` to wait until
5181 the lock is released.
5183 alias stdout
= makeGlobal
!(StdFileHandle
.stdout
);
5190 stdout
.writeln("Write a message to stdout.");
5199 import std
.algorithm
.iteration
: filter
, map
, sum
;
5200 import std
.format
: format
;
5201 import std
.range
: iota
, tee
;
5205 .filter
!(a
=> a
% 2) // 1 3 5
5206 .map
!(a
=> a
* 2) // 2 6 10
5207 .tee
!(_
=> stdout
.writefln("len: %d", len
++))
5219 import std
.algorithm
.mutation
: copy
;
5220 import std
.algorithm
.iteration
: map
;
5221 import std
.format
: format
;
5222 import std
.range
: iota
;
5225 .map
!(e
=> "N: %d".format(e
))
5226 .copy(stdout
.lockingTextWriter()); // the OutputRange
5231 The standard error stream.
5234 stderr as a $(LREF File).
5237 The returned $(LREF File) wraps $(REF stderr,core,stdc,stdio), and
5238 is therefore thread global. Reassigning `stderr` to a different
5239 `File` must be done in a single-threaded or locked context in
5240 order to avoid race conditions.
5242 All writing to `stderr` automatically locks the file globally,
5243 and will cause all other threads calling `write` to wait until
5244 the lock is released.
5246 alias stderr
= makeGlobal
!(StdFileHandle
.stderr
);
5253 stderr
.writeln("Write a message to stderr.");
5259 static import std
.file
;
5260 import std
.typecons
: tuple
;
5262 scope(failure
) printf("Failed test at line %d\n", __LINE__
);
5263 auto deleteme
= testFilename();
5265 std
.file
.write(deleteme
, "1 2\n4 1\n5 100");
5266 scope(exit
) std
.file
.remove(deleteme
);
5268 File f
= File(deleteme
);
5269 scope(exit
) f
.close();
5270 auto t
= [ tuple(1, 2), tuple(4, 1), tuple(5, 100) ];
5272 foreach (e
; f
.byRecord
!(int, int)("%s %s"))
5275 assert(e
== t
[i
++]);
5283 // Retain backwards compatibility
5284 // https://issues.dlang.org/show_bug.cgi?id=17472
5285 static assert(is(typeof(stdin
) == File
));
5286 static assert(is(typeof(stdout
) == File
));
5287 static assert(is(typeof(stderr
) == File
));
5290 // roll our own appender, but with "safe" arrays
5291 private struct ReadlnAppender
5295 bool safeAppend
= false;
5297 void initialize(char[] b
) @safe
5302 @property char[] data() @trusted
5305 assumeSafeAppend(buf
.ptr
[0 .. pos
]);
5306 return buf
.ptr
[0 .. pos
];
5309 bool reserveWithoutAllocating(size_t n
)
5311 if (buf
.length
>= pos
+ n
) // buf is already large enough
5314 immutable curCap
= buf
.capacity
;
5315 if (curCap
>= pos
+ n
)
5317 buf
.length
= curCap
;
5318 /* Any extra capacity we end up not using can safely be claimed
5326 void reserve(size_t n
) @trusted
5328 import core
.stdc
.string
: memcpy
;
5329 if (!reserveWithoutAllocating(n
))
5331 size_t ncap
= buf
.length
* 2 + 128 + n
;
5332 char[] nbuf
= new char[ncap
];
5333 memcpy(nbuf
.ptr
, buf
.ptr
, pos
);
5335 // Allocated a new buffer. No one else knows about it.
5339 void putchar(char c
) @trusted
5344 void putdchar(dchar dc
) @trusted
5346 import std
.utf
: encode
, UseReplacementDchar
;
5349 immutable size
= encode
!(UseReplacementDchar
.yes
)(ubuf
, dc
);
5354 void putonly(const char[] b
) @trusted
5356 import core
.stdc
.string
: memcpy
;
5357 assert(pos
== 0); // assume this is the only put call
5358 if (reserveWithoutAllocating(b
.length
))
5359 memcpy(buf
.ptr
+ pos
, b
.ptr
, b
.length
);
5366 private struct LockedFile
5368 private @system _iobuf
* fp
;
5370 this(FILE
* fps
) @trusted
5373 // Since fps is now locked, we can cast away shared
5374 fp
= cast(_iobuf
*) fps
;
5378 @disable this(this);
5379 @disable void opAssign(LockedFile
);
5381 // these use unlocked fgetc calls
5382 @trusted fgetc() { return _FGETC(fp
); }
5383 @trusted fgetwc() { return _FGETWC(fp
); }
5387 _FUNLOCK(cast(FILE
*) fp
);
5396 auto lf
= LockedFile(fps
);
5397 static assert(!__traits(compiles
, lf
= LockedFile(fps
)));
5398 version (ShouldFail
)
5400 lf
.fps
= null; // error with -preview=systemVariables
5405 // Private implementation of readln
5406 private size_t
readlnImpl(FILE
* fps
, ref char[] buf
, dchar terminator
, File
.Orientation orientation
) @safe
5408 version (CRuntime_Microsoft
)
5410 auto lf
= LockedFile(fps
);
5413 app
.initialize(buf
);
5416 while ((c
= lf
.fgetc()) != -1)
5418 app
.putchar(cast(char) c
);
5419 if (c
== terminator
)
5432 else static if (__traits(compiles
, core
.sys
.posix
.stdio
.getdelim
))
5434 if (orientation
== File
.Orientation
.wide
)
5436 import core
.stdc
.wchar_
: fwide
;
5438 auto lf
= LockedFile(fps
);
5439 /* Stream is in wide characters.
5440 * Read them and convert to chars.
5445 for (int c
= void; (c
= lf
.fgetwc()) != -1; )
5447 if ((c
& ~0x7F) == 0)
5449 if (c
== terminator
)
5454 if (c
>= 0xD800 && c
<= 0xDBFF)
5457 if ((c2
= lf
.fgetwc()) != -1 ||
5458 c2
< 0xDC00 && c2
> 0xDFFF)
5460 StdioException("unpaired UTF-16 surrogate");
5462 c
= ((c
- 0xD7C0) << 10) + (c2
- 0xDC00);
5464 import std
.utf
: encode
;
5472 else version (Posix
)
5475 for (int c
; (c
= lf
.fgetwc()) != -1; )
5477 import std
.utf
: encode
;
5479 if ((c
& ~0x7F) == 0)
5480 buf
~= cast(char) c
;
5482 encode(buf
, cast(dchar) c
);
5483 if (c
== terminator
)
5495 return () @trusted {
5496 import core
.stdc
.stdlib
: free
;
5498 static char *lineptr
= null;
5499 static size_t n
= 0;
5504 // Bound memory used by readln
5511 const s
= core
.sys
.posix
.stdio
.getdelim(&lineptr
, &n
, terminator
, fps
);
5516 buf
.length
= 0; // end of file
5520 const line
= lineptr
[0 .. s
];
5521 if (s
<= buf
.length
)
5533 else // version (NO_GETDELIM)
5535 import core
.stdc
.wchar_
: fwide
;
5537 auto lf
= LockedFile(fps
);
5538 if (orientation
== File
.Orientation
.wide
)
5540 /* Stream is in wide characters.
5541 * Read them and convert to chars.
5546 for (int c
; (c
= lf
.fgetwc()) != -1; )
5548 if ((c
& ~0x7F) == 0)
5550 if (c
== terminator
)
5555 if (c
>= 0xD800 && c
<= 0xDBFF)
5558 if ((c2
= lf
.fgetwc()) != -1 ||
5559 c2
< 0xDC00 && c2
> 0xDFFF)
5561 StdioException("unpaired UTF-16 surrogate");
5563 c
= ((c
- 0xD7C0) << 10) + (c2
- 0xDC00);
5565 import std
.utf
: encode
;
5573 else version (Posix
)
5575 import std
.utf
: encode
;
5577 for (int c
; (c
= lf
.fgetwc()) != -1; )
5579 if ((c
& ~0x7F) == 0)
5580 buf
~= cast(char) c
;
5582 encode(buf
, cast(dchar) c
);
5583 if (c
== terminator
)
5597 // First, fill the existing buffer
5598 for (size_t bufPos
= 0; bufPos
< buf
.length
; )
5600 immutable c
= lf
.fgetc();
5603 buf
.length
= bufPos
;
5606 buf
[bufPos
++] = cast(char) c
;
5607 if (c
== terminator
)
5609 // No need to test for errors in file
5610 buf
.length
= bufPos
;
5614 // Then, append to it
5615 for (int c
; (c
= lf
.fgetc()) != -1; )
5617 buf
~= cast(char) c
;
5618 if (c
== terminator
)
5620 // No need to test for errors in file
5634 static import std
.file
;
5635 auto deleteme
= testFilename();
5636 scope(exit
) std
.file
.remove(deleteme
);
5638 std
.file
.write(deleteme
, "abcd\n0123456789abcde\n1234\n");
5639 File f
= File(deleteme
, "rb");
5641 char[] ln
= new char[2];
5644 assert(ln
== "abcd\n");
5645 char[] t
= ln
[0 .. 2];
5648 // https://issues.dlang.org/show_bug.cgi?id=13856: ln stomped to "abtd"
5649 assert(ln
== "abcd\n");
5651 // it can also stomp the array length
5654 assert(ln
== "0123456789abcde\n");
5659 assert(ln
== "1234\n");
5660 assert(ln
.ptr
== buf
.ptr
); // avoid allocation, buffer is good enough
5663 /** Experimental network access via the File interface
5665 Opens a TCP connection to the given host and port, then returns
5666 a File struct with read and write access through the same interface
5667 as any other file (meaning writef and the byLine ranges work!).
5677 File
openNetwork(string host
, ushort port
)
5679 import core
.stdc
.string
: memcpy
;
5680 import core
.sys
.posix
.arpa
.inet
: htons
;
5681 import core
.sys
.posix
.netdb
: gethostbyname
;
5682 import core
.sys
.posix
.netinet
.in_
: sockaddr_in
;
5683 static import core
.sys
.posix
.unistd
;
5684 static import sock
= core
.sys
.posix
.sys
.socket
;
5685 import std
.conv
: to
;
5686 import std
.exception
: enforce
;
5687 import std
.internal
.cstring
: tempCString
;
5689 auto h
= enforce( gethostbyname(host
.tempCString()),
5690 new StdioException("gethostbyname"));
5692 int s
= sock
.socket(sock
.AF_INET
, sock
.SOCK_STREAM
, 0);
5693 enforce(s
!= -1, new StdioException("socket"));
5697 // want to make sure it doesn't dangle if something throws. Upon
5698 // normal exit, the File struct's reference counting takes care of
5699 // closing, so we don't need to worry about success
5700 core
.sys
.posix
.unistd
.close(s
);
5705 addr
.sin_family
= sock
.AF_INET
;
5706 addr
.sin_port
= htons(port
);
5707 memcpy(&addr
.sin_addr
.s_addr
, h
.h_addr
, h
.h_length
);
5709 enforce(sock
.connect(s
, cast(sock
.sockaddr
*) &addr
, addr
.sizeof
) != -1,
5710 new StdioException("Connect failed"));
5713 f
.fdopen(s
, "w+", host
~ ":" ~ to
!string(port
));
5718 version (StdUnittest
) private string
testFilename(string file
= __FILE__
, size_t line
= __LINE__
) @safe
5720 import std
.conv
: text
;
5721 import std
.file
: deleteme
;
5722 import std
.path
: baseName
;
5724 // filename intentionally contains non-ASCII (Russian) characters for
5725 // https://issues.dlang.org/show_bug.cgi?id=7648
5726 return text(deleteme
, "-детка.", baseName(file
), ".", line
);