SwiftScanner
is a pure native Swift implementation of a string scanner; with no dependecies, full unicode support (who does not love emoji?), lots of useful featurs and swift in mind, StringScanner is a good alternative to built-in Apple's NSScanner
.
β β Star our github repository to help us! β β
I'm also working on several other projects you may like. Take a look below:
Library | Description |
---|---|
SwiftDate | The best way to manage date/timezones in Swift |
Hydra | Write better async code: async/await & promises |
FlowKit | A new declarative approach to table managment. Forget datasource & delegates. |
SwiftRichString | Elegant & Painless NSAttributedString in Swift |
SwiftLocation | Efficient location manager |
SwiftMsgPack | Fast/efficient msgPack encoder/decoder |
SwiftScanner is initialized with a string and mantain an internal index used to navigate backward and forward through the string using two main concepts:
scan
to return string which also increment the internal indexpeek
to return a string without incrementing the internal index
Results of these operations returns collected String or Indexes.
If operation fail due to an error (ie. eof
, notFound
, invalidInt
...) and exception is thrown, in pure Swift style.
- scanChar()
- scanInt()
- scanFloat()
- scanHexInt()
- scan(upTo: UnicodeScalar)
- scan(upTo: CharacterSet)
- scan(untilIn: CharacterSet)
- scan(upTo: String)
- scan(until: Test)
- scan(length: Int)
- peek(upTo: UnicodeScalar)
- peek(upTo: CharacterSet)
- peek(untilIn: CharacterSet)
- peek(upTo: String)
- peek(until: Test)
- match(UnicodeScalar)
- match(String)
- reset()
- peekAtEnd()
- skip(length: Int)
- back(length: Int)
Example:
let scanner = StringScanner("Hello this is SwiftScanner")
let firstChar = try! scanner.scanChar() // get 'H'
Example:
let scanner = StringScanner("15 apples")
let parsedInt = try! scanner.scanInt() // get Int=15
Example:
let scanner = StringScanner("45.54 $")
let parsedFloat = try! scanner.scanFloat() // get Int=45.54
0x[VALUE]
(example:0x0000000000564534
)0X[VALUE]
(example:0x0929
)#[VALUE]
(example:#1602
)
If scan succeded scanner's position
is updated at the end of the represented string, otherwise an exception ((.notFound
, ).invalidHex
, .eof
) is thrown and index is not touched.
Example:
let scanner = StringScanner("#1602")
let value = try! scanner.scanHexInt(.bit16) // get Int=5634
let scanner = StringScanner("#0x0929")
let value = try! scanner.scanHexInt(.bit16) // get Int=2345
let scanner = StringScanner("#0x0000000000564534")
let value = try! scanner.scanHexInt(.bit64) // get Int=5653812
Example:
let scanner = StringScanner("Hello <bold>Daniele</bold>")
let partialString = try! scanner.scan(upTo: "<bold>") // get "Hello "
Example:
let scanner = StringScanner("Hello, I've at least 15 apples")
let partialString = try! scanner.scan(upTo: CharacterSet.decimalDigits) // get "Hello, I've at least "
Example:
let scanner = StringScanner("HELLO i'm mark")
let partialString = try! scanner.scan(untilIn: CharacterSet.lowercaseLetters) // get "HELLO"
Example:
let scanner = StringScanner("This is a simple test I've made")
let partialString = try! scanner.scan(upTo: "I've") // get "This is a simple test "
Example:
let scanner = StringScanner("Never be satisfied πͺ and always push yourself! π Do the things people say cannot be done")
let delimiters = CharacterSet(charactersIn: "πͺπ")
while !scanner.isAtEnd {
let block = scanner.scan(untilTrue: { char in
return (delimiters.contains(char) == false)
})
// Print:
// "Never be satisfied " (first iteration)
// "and always push yourself!" (second iteration)
// "Do the things people say cannot be done" (third iteration)
print("Block: \(block)")
try scanner.skip() // push over the character
}
Example:
let scanner = StringScanner("Never be satisfied")
let partialString = scanner.scan(5) // "Never"
Peek functions are the same as concept of scan()
but unless it it does not update internal scanner's position
index.
These functions usually return only starting index
of matched pattern.
Example:
let scanner = StringScanner("Never be satisfied")
let index = try! scanner.peek(upTo: "b") // return 6
Example:
let scanner = StringScanner("You are in queue: 123 is your position")
let index = try! scanner.peek(upTo: CharacterSet.decimalDigits) // return 18
Example:
let scanner = StringScanner("654 apples")
let index = try! scanner.peek(untilIn: CharacterSet.decimalDigits) // return 3
Example:
let scanner = StringScanner("654 apples in the bug")
let index = try! scanner.peek(upTo: "in") // return 11
Example:
let scanner = StringScanner("I'm very πͺ and π Go!")
let delimiters = CharacterSet(charactersIn: "πͺπ")
while !scanner.isAtEnd {
let prevIndex = scanner.position
let finalIndex = scanner.peek(untilTrue: { char in
return (delimiters.contains(char) == false)
})
// Distance will return:
// - 9 (first iteration)
// - 5 (second iteration)
// - 4 (third iteration)
let distance = scanner.string.distance(from: prevIndex, to: finalIndex)
try scanner.skip(length: distance + 1)
}
let scanner = StringScanner("πͺ and π")
let match = scanner.match("π") // return false
let scanner = StringScanner("I'm very πͺ and π Go!")
scanner.match("I'm very") // return true
use_frameworks!
pod 'SwiftScanner'
github 'malcommac/SwiftScanner'
Add swiftline as dependency in your Package.swift
import PackageDescription
let package = Package(name: "YourPackage",
dependencies: [
.Package(url: "https://github.com/malcommac/SwiftScanner.git", majorVersion: 0),
]
)
Run them with
swift test
Current version is compatible with:
-
Swift 4.x >= 1.0.4
-
Swift 3.x: up to 1.0.3
-
iOS 8 or later
-
macOS 10.10 or later
-
watchOS 2.0 or later
-
tvOS 9.0 or later
-
...and virtually any platform which is compatible with Swift 3 and implements the Swift Foundation Library
As open source creation any help is welcome!
The code of this library is licensed under MIT License; you can use it in commercial products without any limitation.
The only requirement is to add a line in your Credits/About section with the text below:
Portions SwiftScanner - http://github.com/malcommac/SwiftScanner
Created by Daniele Margutti and licensed under MIT License.