Skip to content
Simple scanner for data streams like STDIN in Swift
Branch: master
Clone or download
Latest commit 1c329bd Feb 17, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
StreamScanner.xcodeproj * Update for Swift 4.2 Feb 17, 2019
StreamScanner * Update for Swift 4.2 Feb 17, 2019
StreamScannerTest Swift 3. Feb 6, 2017
StreamScannerTests Swift 3. Feb 6, 2017
.gitignore Initial Jun 27, 2015
README.md Update README.md Feb 6, 2017

README.md

Input Stream Scanner

A simple scanner for any kind of file input streams with arbitrary delimiters. Made with Swift, based on NSScanner.

Originally appeared as a necessity to parse the standard input for competitive programming challenges on HackerRank.

Installation

For competitive programming

Copy-paste the contents of this tiny sample and enjoy. For more general use cases check out the full version of StreamScanner.swift and see the usage and examples sections below.

As a framework

Compile, link against and don't forget to:

import StreamScanner

As a built-in class

Add StreamScanner.swift file to your project.

Usage

For convenience you can declare:

let stdin = StreamScanner.standardInput

and use stdin later on. Spaces and new lines are considered delimiters by default.

Or make your custom stream with:

let stream = StreamScanner(source: FileHandle(forReadingAtPath: "/path/to/file"),
                           delimiters: CharacterSet(charactersIn: "-.:\n"))

Now call read() -> Optional<T> to get the next value from a stream, where T is a type of a variable where you want to read() the stream.

You may call ready() -> Bool to check whether the stream currently contains any data it can parse. Note: the stream may try to grab some data from its source during this check.

Example

Imagine there's a file at /path/to/file with the following contents:

42.times show:not_a_double

And here's one of the ways to scan it with the stream just declared above:

var number: Int?    = stream.read() //parse an integer at the current position in the stream
var string: String? = stream.read() //now skip a delimiting dot and parse a string
var double: Double? = stream.read() //now skip a delimiting colon and try to parse a double

print("\(number) \(string) \(double)")  //Optional(42) Optional("times show") nil

More Examples

Read some arbitrary values of different types from the standard input

if
    let int:    Int     = stdin.read(),
    let string: String  = stdin.read(),
    let double: Double  = stdin.read(),
    let int64:  Int64   = stdin.read(),
    let float:  Float   = stdin.read()
{
    print("\(int) \(string) \(double) \(int64) \(float)")
}

Input:

+42 st_ring!
-0.987654321 12345678900
.42

Output:

42 st_ring! -0.987654321 12345678900 0.42

Read an array of Int64 of an arbitrary size from the standard input

if var count: Int = stdin.read()
{
    var array: [Int64] = []

    for _ in 0..<count
    {
        if let integer: Int64 = stdin.read()
        {
            array.append(integer)
        }
    }

    print(array)
}

Input:

2
1234567890123456789 987654321098765432

Output:

[1234567890123456789, 987654321098765432]

Read some strings from the stream in a for loop until it's empty

for name in stdin
{
    print("Hello, \(name)!")
}

Input:

Chris Ally Joshua

Output:

Hello, Chris!
Hello, Ally!
Hello, Joshua!

Read and present the contents of /etc/passwd file

if let input = FileHandle(forReadingAtPath: "/etc/passwd")
{
    let scanner = StreamScanner(source: input, delimiters: CharacterSet(charactersIn: ":\n"))

    print("User Database:")

    while let line: String = scanner.read()
    {
        //skip any comments
        if !line.hasPrefix("#")
        {
            let username = line

            if
                let valid:    String  = scanner.read(),
                let userId:   Int     = scanner.read(),
                let groupId:  Int     = scanner.read(),
                let gecos:    String  = scanner.read(),
                let home:     String  = scanner.read(),
                let shell:    String  = scanner.read()
            {
                print("------------------------------")
                print("User: \t\(username) (\(gecos))")
                print("UID:  \t\(userId)")
                print("GID:  \t\(groupId)")
                print("Home: \t\(home)")
                print("Shell:\t\(shell)")
            }
        }
    }
}

Output:

User Database:
------------------------------
User: 	nobody (Unprivileged User)
UID:  	-2
GID:  	-2
Home: 	/var/empty
Shell:	/usr/bin/false
------------------------------
User: 	root (System Administrator)
UID:  	0
GID:  	0
Home: 	/var/root
Shell:	/bin/sh
------------------------------
User: 	daemon (System Services)
UID:  	1
GID:  	1
Home: 	/var/root
Shell:	/usr/bin/false

License

This toy is free and open.

Creative Commons

You can’t perform that action at this time.