Jump to content

F Sharp Programming/Input and Output

From Wikibooks, open books for an open world
Previous: Mutable Collections Index Next: Exception Handling
F# : Basic I/O

Input and output, also called I/O, refers to any kind of communication between two hardware devices or between the user and the computer. This includes printing text out to the console, reading and writing files to disk, sending data over a socket, sending data to the printer, and a wide variety of other common tasks.

This page is not intended to provide an exhaustive look at .NET's I/O methods (readers seeking exhaustive references are encouraged to review the excellent documentation on the System.IO namespace on MSDN). This page will provide a cursory overview of some of the basic methods available to F# programmers for printing and working with files.

Working with the Console

[edit | edit source]

With F#

[edit | edit source]

By now, you're probably familiar with the printf, printfn, sprintf and its variants in the Printf module. However, just to describe these methods more formally, these methods are used for printf-style printing and formatting using % markers as placeholders:

Print methods take a format string and a series of arguments, for example:

> sprintf "Hi, I'm %s and I'm a %s" "Juliet" "Scorpio";;
val it : string = "Hi, I'm Juliet and I'm a Scorpio"

Methods in the Printf module are type-safe. For example, attempting to use substitute an int placeholder with a string results in a compilation error:

> sprintf "I'm %i years old" "kitty";;

  sprintf "I'm %i years old" "kitty";;
  ---------------------------^^^^^^^^

stdin(17,28): error FS0001: The type 'string' is not compatible with any of the types
byte,int16,int32,int64,sbyte,uint16,uint32,uint64,nativeint,unativeint, arising from 
the use of a printf-style format string.

According to the F# documentation, % placeholders consist of the following:

%[flags][width][.precision][type]

[flags] (optional)

Valid flags are:

0: add zeros instead of spaces to make up the required width
'-': left justify the result within the width specified
'+': add a '+' character if the number is positive (to match a '-' sign for negatives)
' ': add an extra space if the number is positive (to match a '-' sign for negatives)

[width] (optional)

The optional width is an integer indicating the minimal width of the result. For instance, %6d prints an integer, prefixing it with spaces to fill at least 6 characters. If width is '*', then an extra integer argument is taken to specify the corresponding width.

any number
'*':

[.precision] (optional)

Represents the number of digits after a floating point number.

> sprintf "%.2f" 12345.67890;;
val it : string = "12345.68"

> sprintf "%.7f" 12345.67890;;
val it : string = "12345.6789000"

[type] (required)

The following placeholder types are interpreted as follows:

     %b:         bool, formatted as "true" or "false"
     %s:         string, formatted as its unescaped contents
     %d, %i:     any basic integer type formatted as a decimal integer, signed if the basic integer type is signed.
     %u:         any basic integer type formatted as an unsigned decimal integer
     %x, %X, %o: any basic integer type formatted as an unsigned hexadecimal 
                 (a-f)/Hexadecimal (A-F)/Octal integer
 
     %e, %E, %f, %F, %g, %G: 
                 any basic floating point type (float,float32) formatted
                 using a C-style floating point format specifications, i.e
 
     %e, %E: Signed value having the form [-]d.dddde[sign]ddd where 
                 d is a single decimal digit, dddd is one or more decimal
                 digits, ddd is exactly three decimal digits, and sign 
                 is + or -
 
     %f:     Signed value having the form [-]dddd.dddd, where dddd is one
                 or more decimal digits. The number of digits before the 
                 decimal point depends on the magnitude of the number, and 
                 the number of digits after the decimal point depends on 
                 the requested precision.
 
     %g, %G: Signed value printed in f or e format, whichever is 
                 more compact for the given value and precision.
 
 
    %M:      System.Decimal value
 
    %O:      Any value, printed by boxing the object and using it's ToString method(s)
 
    %A:      Any value, printed by using Microsoft.FSharp.Text.StructuredFormat.Display.any_to_string with the default layout settings 
 
    %a:      A general format specifier, requires two arguments:
                 (1) a function which accepts two arguments:
                     (a) a context parameter of the appropriate type for the
                         given formatting function (e.g. an #System.IO.TextWriter)
                     (b) a value to print
                         and which either outputs or returns appropriate text.
 
                 (2) the particular value to print
 
 
    %t:      A general format specifier, requires one argument:
                 (1) a function which accepts a context parameter of the
                     appropriate type for the given formatting function (e.g. 
                     an #System.IO.TextWriter)and which either outputs or returns 
                     appropriate text.

  Basic integer types are:
     byte,sbyte,int16,uint16,int32,uint32,int64,uint64,nativeint,unativeint
  Basic floating point types are:
     float, float32

Programmers can print to the console using the printf method, however F# recommends reading console input using the System.Console.ReadLine() method.

With .NET

[edit | edit source]

.NET includes its own notation for format specifiers. .NET format strings are untyped. Additionally, .NET's format strings are designed to be extensible, meaning that a programmer can implement their own custom format strings. Format placeholders have the following form:

{index[, length][:formatString]}

For example, using the String.Format method in fsi:

> System.String.Format("Hi, my name is {0} and I'm a {1}", "Juliet", "Scorpio");;
val it : string = "Hi, my name is Juliet and I'm a Scorpio"

> System.String.Format("|{0,-50}|", "Left justified");;
val it : string = "|Left justified                                    |"

> System.String.Format("|{0,50}|", "Right justified");;
val it : string = "|                                   Right justified|"

> System.String.Format("|{0:yyyy-MMM-dd}|", System.DateTime.Now);;
val it : string = "|2009-Apr-06|"

See Number Format Strings, Date and Time Format Strings, and Enum Format Strings for a comprehensive reference on format specifiers for .NET.

Programmers can read and write to the console using the System.Console class:

open System

let main() =
    Console.Write("What's your name? ")
    let name = Console.ReadLine()
    Console.Write("Hello, {0}", name)
    
main()

System.IO Namespace

[edit | edit source]

The System.IO namespace contains a variety of useful classes for performing basic I/O.

Files and Directories

[edit | edit source]

The following classes are useful for interrogating the host filesystem:

Streams

[edit | edit source]

A stream is a sequence of bytes. .NET provides some classes which allow programmers to work with streams including:

Previous: Mutable Collections Index Next: Exception Handling