Introduction To F# I/O
F# IO enables you to read and write files, manage file streams, and interact with the file system in various ways. This article will guide you through the essential concepts and usage of F# IO.
Throughout this lesson, we will cover the process of reading input from the console when working with the stdin object, how to read and write files when working with the System.IO.File module, and how to format and print output to the console when using the printfn function.
To write to the console, we have used a combination of the printf and printfn functions.
Apart from these functions, the Core.Printf module of F# is also equipped with a variety of other methods which can be used to print and format by using placeholders such as % markers.
Below is a table with a brief description of each of the methods listed:
|bprintf : StringBuilder → BuilderFormat<‘T> → ‘T||This method prints to a StringBuilder object.|
|eprintf : TextWriterFormat<‘T> → ‘T||This command prints the formatted output to the standard error.|
|eprintfn : TextWriterFormat<‘T> → ‘T||A newline is added to the formatted output before it is printed to stderr.|
|failwithf : StringFormat<‘T,’Result> → ‘T||An exception is raised by printing the given result to a string buffer and then printing it to a string buffer.|
|fprintf : TextWriter → TextWriterFormat<‘T> → ‘T||The text is printed to a text writer.|
|fprintfn : TextWriter → TextWriterFormat<‘T> → ‘T||A new line is added to the end of the text when it is printed to a text writer.|
|kbprintf : (unit → ‘Result) → StringBuilder → BuilderFormat<‘T,’Result> → ‘T||The result is generated in the same way as bprintf, but it is called by the specified function.|
|kfprintf : (unit → ‘Result) → TextWriter → TextWriterFormat<‘T,’Result> → ‘T||A function that generates the result by calling the specified function, similar to fprintf.|
|kprintf : (string → ‘Result) → StringFormat<‘T,’Result> → ‘T||The function is called in the same way as printf, but the result is generated by the specified function. It is useful, for example, if the printing software forces a flush after the output has been entered into the channel, but not before, i.e. after all output has been entered.|
|ksprintf : (string → ‘Result) → StringFormat<‘T,’Result> → ‘T||This function is similar to sprintf, but it generates the result by calling the specified function.|
|printf : TextWriterFormat<‘T> → ‘T||Stdout is printed with the formatted output.|
|printfn : TextWriterFormat<‘T> → ‘T||Adds a newline to the end of the output when printing to stdout with formatted output.|
|sprintf : StringFormat<‘T> → ‘T||This function outputs the contents of an internal string buffer to an output string and returns an output string as a result.|
When working with F# IO, it is important to specify the input and output formats using a format specification that meets your programming needs.
To indicate that a string is a placeholder for a format, a % marker is used in the string.
The format placeholder is written in a specific syntax that includes a percentage symbol followed by a character that specifies the type of format.
For example, %d is used for integer format, %f is used for floating-point format, %s is used for string format, and so on.
The format placeholders can also include additional options that define the width, alignment, and precision of the value being formatted. For example, %5d specifies that the integer value should be formatted with a minimum width of 5 characters, while %-10s specifies that the string value should be formatted with a minimum width of 10 characters and left-aligned.
By using these format specifications, you can ensure that your input and output values are properly formatted according to your desired format and display requirements.
Types are interpreted as follows:
|%b||Attempts to format a bool, which will be either true or false.|
|%c||Characters can be formatted using this function.|
|%s||A string can be formatted in accordance with its contents, without having to understand any escape characters in the string.|
|%d, %i||A decimal integer format is created by formatting any basic integer type as a decimal integer, signed if it is a signed basic integer type.|
|%u||Integer types that do not contain signs can be formatted as unsigned decimal integers for any type of integer.|
|%x||In this function, you are able to format any basic integer type using the lowercase letters a through f in an unsigned hexadecimal format.|
|%X||Using uppercase letters A through F, this function formats any basic integer type as an unsigned hexadecimal integer.|
|%o||The formatter takes any basic integer type and transforms it into an unsigned octal integer type.|
|%e, %E, %f, %F, %g, %G||Floating point types (float, float32) are formatted according to a set of standards for floating point formats defined by a C-like programming language.|
|%e, %E||There is a signed value format that can be configured in the form [-]d.dddde[sign]ddd that consists of d as a single decimal digit, dddd as one or more decimal digits, and ddd as exactly three decimal digits, with sign = + or -.|
|%f||The function formats a sign value having the format [-]dddd.dddd, where dddd refers to a number of decimal digits. Depending on the magnitude of the number, the number of digits before the decimal point, as well as the number of digits after the decimal point, will be determined by the precision required.|
|%g, %G||The format in which a signed value should be printed is either f or e, depending upon the compactness of the value and precision of the value.|
|%M||Provides a format for a decimal value.|
|%O||In order to format any value, the object has to be boxed, and the method ToString is used to print it.|
|%A, %+A||This function formats any value based on the default layout settings, and prints the result. In order to print the structure of discriminated unions with private and internal representations, you can use the key combination of %+A.|
|%a||There are two arguments that must be provided when specifying a general format specifier. First, there is a function that takes two arguments: the first is a context parameter of the appropriate type for the formatting function being used (for example, a TextWriter), and the second is a value to print, which either outputs or returns an appropriate string.
In the second argument, you can specify which value you want to print.
|%t||There is only one argument required for a general format specifier; that is, a function that accepts a context parameter of the appropriate type (aTextWriter in this case), and either outputs or returns appropriate text based upon the parameters. An integer type consists of a byte, a sbyte, an int16, a uint16, an int32, a uint32, an int64, a uint64, a nativeint, and a unativeint. A floating point number can be expressed as a float or as a float32.|
A width parameter can be used as an optional parameter.
The minimal width of the result is an integer indicating the size of the result at the most minimal level.
As an example, %5d prints an integer with at least 5 characters of spaces between each digit.
Below is a table that describes the valid flags that should be set:
|0||The width of the field must be made up of zeros instead of spaces, as specified above.|
|–||Sets the result to be left-justified when within the specified width.|
|+||It specifies that the number will be accompanied by a + symbol if it is positive (to match the – symbol for negative numbers).|
|‘ ‘ (space)||If the number is positive, an extra space is to be added (in order to match a – sign for negative numbers).|
mrexamples - FSharp mrexamples - FSharp Hello, from mrexamplesa: 115.098000 b: 232.768000 c: 115.098 d: 232.768 e: 1.150980e+002 f: 2.327680e+002 False: false
F# IO Console Class
There is a class in this module that is part of the .NET framework.
For console applications, it represents the standard input streams, output streams, and error streams that are used in the program.
There are a variety of methods that can be used to read from and write to the console through it.
Below is a table that shows the different methods that can be used:
|Beep()||The console speaker will play the sound of a beep through the console.|
|Beep(Int32, Int32)||A beep will be played through the console speaker at a specified frequency and for a specified duration every time the button is pressed.|
|Clear||The purpose of this function is to clear the console buffer as well as the corresponding console window of any display information.|
|MoveBufferArea(Int32, Int32, Int32, Int32, Int32, Int32)||This function copies the screen buffer from a specified source area to a specified destination area.|
|MoveBufferArea(Int32, Int32, Int32, Int32, Int32, Int32, Char, ConsoleColor, ConsoleColor)||This function copies the screen buffer from a specified source area to a specified destination area.|
|OpenStandardError()||The stream of standard errors is acquired.|
|OpenStandardError(Int32)||A standard error stream, whose buffer size is specified, is acquired as a stream to store the standard error data.|
|OpenStandardInput()||The standard input stream is acquired by this method.|
|OpenStandardInput(Int32)||Provides an input stream that is set to a specified buffer size, in order to receive the standard input stream.|
|OpenStandardOutput()||The standard output stream is acquired by this function.|
|OpenStandardOutput(Int32)||An output stream with a specified buffer size is acquired from the standard output stream.|
|Read||From the standard input stream, this function reads the next character.|
|ReadKey()||Provides the user with the next character that will be entered or the function key that will be pressed next. It is displayed in the console window when a key is pressed.|
|ReadKey(Boolean)||This function is responsible for determining which character or function key has been pressed by the user next. You can choose to display the key you pressed in the console window if you wish.|
|ReadLine||From the standard input stream, the next line of characters will be read.|
|ResetColor||This will set both the background and foreground console colors to their default values.|
|SetBufferSize||Sets parameters for modifying the height and width of the screen buffer area by specifying the values that you want.|
|SetCursorPosition||Cursor position is set by the position of the cursor.|
|SetError||Specifies the TextWriter object to be used as the Error property.|
|SetIn||Specifies the TextReader object to be used as the In property.|
|SetOut||Specifies the TextReader object to be used as the Out property.|
|SetWindowPosition||It is used to set the position of the console window in relation to the buffer on the screen.|
|SetWindowSize||In the console window, the height and width are set to the values that are specified by the user.|
|Write(Boolean)||An output stream is created to write the text representation of the Boolean value specified to the standard output stream.|
|Write(Char)||The standard output stream is written with the Unicode character value specified as the input.|
|Write(Char)||The standard output stream is written with the specified array of Unicode characters in the specified order.|
|Write(Decimal)||An output stream is used to write a text representation of the decimal value specified as part of the command.|
|Write(Double)||The output stream of the standard output device is written with the text representation of the specified double-precision floating-point value.|
|Write(Int32)||The standard output stream will be written with the text representation of the 32-bit signed integer value pointed to by the argument.|
|Write(Int64)||The standard output stream will be written with the text representation of the 64-bit signed integer value pointed to by the argument.|
|Write(Object)||With the standard output stream, the 32-bit signed integer value typed in as the argument will be represented as the text representation of the 32-bit signed integer value.|
|Write(Single)||The standard output stream is written with a text representation of the specified single-precision floating-point value described in the specification.|
|Write(String)||This function outputs the string value specified in the input string to the standard output stream.|
|Write(UInt32)||It writes the 32-bit unsigned integer value specified in the input stream as a text representation to the standard output stream.|
|Write(UInt64)||It writes the 64-bit unsigned integer value specified in the input stream as a text representation to the standard output stream.|
|Write(String, Object)||Using the specified format information, the standard output stream will be populated with the text representation of the specified object.|
|Write(String, Object)||An array of objects will be written to the standard output stream using the specified format information in order to create the text representation of the array.|
|Write(Char, Int32, Int32)||The standard output stream is written with the specified subarray of Unicode characters in the specified order.|
|Write(String, Object, Object)||With the help of the specified format information, the standard output stream is written with the text representation of the specified objects.|
|Write(String, Object, Object, Object)||With the help of the specified format information, the standard output stream is written with the text representation of the specified objects.|
|Write(String, Object, Object, Object, Object)||With the help of the specified format information, the standard output stream will be used to write the text representation of the specified objects and variable-length parameter list.|
|WriteLine()||A standard output stream is written with the current line terminator in it.|
|WriteLine(Boolean)||To create a standard output stream, the specified Boolean value is written as a text representation, followed by the line terminator that governs the current stream.|
|WriteLine(Char)||The standard output stream is written with the Unicode character specified in the command line, followed by the current line terminator value.|
|WriteLine(Char)||The standard output stream is written with an array of Unicode characters, followed by the current line terminator, to represent the specified Unicode array.|
|WriteLine(Decimal)||An output stream to which the data is written is created by writing the decimal representation of the specified value, followed by the current line terminator.|
|WriteLine(Double)||A standard output stream is created for the specification of a double-precision floating-point value, followed by a line terminator, and the text representation of that value.|
|WriteLine(Int32)||There is a standard output stream which writes the text representation of the 32-bit signed integer value that has been specified, followed by the line terminator at the end of the line.|
|WriteLine(Int64)||There is a standard output stream which writes the text representation of the 64-bit signed integer value that has been specified, followed by the line terminator at the end of the line.|
|WriteLine(Object)||The text representation of the specified object is written to the standard output stream in the format text representation, followed by the current line terminator.|
|WriteLine(Single)||Upon pressing the write command, the text representation of the specified floating-point value will be written to the standard output stream, followed by the current line terminator.|
|WriteLine(String)||The specified string value will be written to the standard output stream along with the current line terminator, followed by the current line ending.|
|WriteLine(UInt32)||An unsigned 32-bit integer value is written to the standard output stream using the text representation along with the current line terminator, followed by the value exactly as specified.|
|WriteLine(UInt64)||An unsigned 64-bit integer value is written to the standard output stream using the text representation along with the current line terminator, followed by the value exactly as specified.|
|WriteLine(String, Object)||By using the specified format information for the standard output stream, the text representation of the specified object will be written, followed by the current line terminator, to the standard output stream.|
|WriteLine(String, Object)||The specified object array is written to standard output using the specified format information, followed by the current line terminator.|
|WriteLine(Char, Int32, Int32)||To the standard output stream, the specified Unicode subarray is written, followed by the current line terminator.|
|WriteLine(String, Object, Object)||With the specification of the format information, this program writes the text representation of the specified objects to the standard output stream, followed by the current line terminator.|
|WriteLine(String, Object, Object, Object)||With the specification of the format information, this program writes the text representation of the specified objects to the standard output stream, followed by the current line terminator.|
|WriteLine(String, Object, Object, Object, Object)||The format information for the standard output stream is used to write the text representation of the specified objects, followed by a list of parameters of variable length, followed by the current line terminator, into the standard output stream.|
In the example below, we demonstrate how to read from and write to the console at the same time:
The output of the above program is as follows:
What's your name? John Hello, John Greetings from mrexamples |2023-Apr-11|
Data Collection and Printing using Console Class
The following is an example of how to get user data and print it out:
And the output will be as:
Enter your name: John Enter your age: 24 Enter your gender: male Hello, John! Your age is: 24. And your gender is: male.
We wrote this code in F# to provide a simple console application that takes user input for name, age, and gender, and displays the output with a greeting message.
We start with an open statement for the System module, which is used to access the Console module for reading input and displaying output.
We define the getUserInput() function to read user input for name, age, and gender using the Console.ReadLine() function, which reads a line of text from the standard input. The values are then stored in the name, age, and gender variables, and a tuple (name, age, gender) is returned.
We define the displayOutput() function to take the name, age, and gender as input arguments and display them using the printfn function, which is used to format and display output to the console. The %s placeholders are used to format the string with the values of name, age, and gender.
In the main function, which is the entry point of the program denoted by the [<EntryPoint>] attribute, we call the getUserInput() function to get user input, and then call the displayOutput() function with the retrieved values. Finally, we return 0, indicating a successful program execution.
The System.IO Namespace
System.IO is a namespace that contains a number of useful classes for performing basic input/output operations within the system.
These types or classes include types that provide basic file and directory support, as well as those that allow reading and writing from and to files and data streams.
The following classes can be useful when working with the file system:
- System.IO.File is a class which is used to create, append, and delete files in a system.
- The System.IO.Directory class is used to create, move, and delete directories as part of system-wide operations.
- The System.IO.Path class is responsible for performing operations on strings, which represent paths to files.
- The System.IO.FileSystemWatcher class enables users to keep track of any changes made to a directory by listening to it.
The following lists classes that might be useful for working with streams (sequences of bytes)
- To read characters from a stream, we use a class called System.IO.StreamReader.
- In order to write characters to a stream, the System.IO.StreamWriter class is used.
- In order to create an in-memory stream of bytes, you need to use the System.IO.MemoryStream class.
Here is a brief description of each of the classes that can be found in the namespace as well as the classes themselves.
|BinaryReader||The primitive data types can be read in binary form and encoded according to a specific encoding specification.|
|BinaryWriter||The primitive types can be written in binary to a stream and the strings can be written in a specific encoding.|
|BufferedStream||This function adds a buffering layer on top of a stream in order to improve the efficiency of reads and writes.|
|Directory||Creates, moves, and enumerates directories and subdirectories through static methods, which can be used by any user.|
|DirectoryInfo||Creates, moves, and enumerates directories and subdirectories through instance methods, which can be used by any user.|
|DirectoryNotFoundException||The exception that is thrown when a part of a file or directory can’t be located after a search has been performed on it.|
|DriveInfo||Provides the ability to access information that is stored on a drive.|
|DriveNotFoundException||When you attempt to access a shared drive or drive that isn’t available, you are thrown an exception.|
|EndOfStreamException||A reading attempt that goes past the end of a stream will result in an exception being thrown.|
|ErrorEventArgs||FileSystemWatcher.Error is an event that provides data for the error that occurred.|
|File||This class provides static methods for creating, copying, deleting, moving, and opening a single file, as well as aiding in the creation of objects associated with a single file stream.|
|FileFormatException||This is an exception which is thrown when the data stream or the input file are incorrectly formatted or do not comply with a certain specification for the file format.|
|FileInfo||The file stream object has properties and instance methods that allow you to create, copy, delete, move, and open files, as well as aid in the creation of FileStream objects in the future.|
|FileLoadException||If a managed assembly is found and cannot be loaded, this exception is thrown, along with a description of the error.|
|FileNotFoundException||In order to access a file that doesn’t exist on disk, an exception is thrown whenever an attempt is made to access the file.|
|FileStream||Supports synchronous and asynchronous reads and writes centered around a file by offering a Stream around it.|
|FileSystemEventArgs||There are three types of events that can be recorded in the directory: Changed, Created, and Deleted.|
|FileSystemInfo||This class provides both the FileInfo object and the DirectoryInfo object as its base class.|
|FileSystemWatcher||When a directory or file within a directory changes, this class listens to the changes in the file system and raises an event when the changes have been made.|
|InternalBufferOverflowException||In the event of an internal buffer overflow, an exception is thrown.|
|InvalidDataException||If the data stream is in an invalid format, then this exception will be thrown when it is encountered.|
|IODescriptionAttribute||Visual designers can use this property to set the description that will appear when referencing an event, extender, or property.|
|IOException||An I/O error exception is thrown whenever there is an error in the data being read or written.|
|MemoryStream||This function creates a stream that has memory as its backing store.|
|Path||In this method, we perform operations on String instances that contain path information related to files or directories. There is a cross-platform nature to these operations, which makes them easy to implement.|
|PathTooLongException||Exceptions are thrown when the maximum length of a path or file name exceeds the maximum length that is defined by the system.|
|PipeException||This is thrown when one of the named pipes within the application encounters an error.|
|RenamedEventArgs||Renamed event data is provided in this object.|
|Stream||A sequence of bytes can be viewed generically as a sequence of bytes. The class in question is abstract in nature.|
|StreamReader||TextReader is a library that implements a method to read characters from a byte stream in a specific encoding based on the characters.|
|StreamWriter||Creates a TextWriter that can be used to write a stream of characters in an encoding of your choosing. The Reference Source for this type can be browsed if you are interested in browsing the source code for the .NET Framework.|
|StringReader||An implementation of the TextReader is provided that reads from a string.|
|StringWriter||Implements a TextWriter that allows information to be written to a string in a textual format. StringBuilder is the underlying class in which the information is stored.|
|TextReader||Assigns the reader to a group of characters that can be read sequentially in a variety of ways.|
|TextWriter||This type of writer represents someone who is capable of writing a series of sequential characters. The class is abstract in nature.|
|UnmanagedMemoryAccessor||A method which allows managed code to randomly access blocks of memory in unmanaged memory.|
|UnmanagedMemoryStream||Managed code can access unmanaged blocks of memory through this method.|
|WindowsRuntimeStorageExtensions||The Windows Runtime consists of extension methods for the IStorageFile and IStorageFolder interfaces to enable developers to use them when creating Windows Store apps.|
|WindowsRuntimeStreamExtensions||This extension provides methods to convert between streams in the Windows Runtime and streams in .NET for Windows Store apps that are managed through the .NET framework.|
Here is an example of creating a file called test.txt, writing a message to it, reading the text from the file, and printing it to the console in the following example.
And the output will be:
Hello Developers, Welcome to mrexamples!