2022
Improved code of p-sun's and Justin Oroz's answers:
Code works with SubSequence
s, so it's uses less memory.
You're able to do:
// works well even on substrings
"01234567890"[i: 1] // Character "1"
"01234567890"[i: 15] // nil
"01234567890"[safe: 1..<5] // subsequence "1234"
"01234567890"[safe: 1...5] // subsequence "12345"
"012"[safe: 1..<15] // subsequence "12"
"012"[safe: 1...15] // subsequence "12"
"012"[unsafe: 1..<9] // will thrown FatalError OutOfBounds exception
"012"[unsafe: 1...9] // will thrown FatalError OutOfBounds exception
"012"[unsafe: -1..<2] // will thrown FatalError OutOfBounds exception
"012"[unsafe: -1...2] // will thrown FatalError OutOfBounds exception
public extension StringProtocol {
subscript(i idx: Int) -> Character? {
if idx >= self.count { return nil }
return self[self.index(self.startIndex, offsetBy: idx)]
}
}
public extension Substring {
subscript(i idx: Int) -> Character? {
if idx >= self.count { return nil }
return self.base[index(startIndex, offsetBy: idx)]
}
}
public extension StringProtocol {
/// Use this if you want to get OutOfBounds exception
subscript(unsafe bounds: Range<Int>) -> SubSequence {
let startIndex = index(self.startIndex, offsetBy: bounds.lowerBound)
return self[startIndex..<index(startIndex, offsetBy: bounds.count)]
}
/// Use this if you want to get OutOfBounds exception
subscript(unsafe bounds: ClosedRange<Int>) -> SubSequence {
let startIndex = index(self.startIndex, offsetBy: bounds.lowerBound)
return self[startIndex..<index(startIndex, offsetBy: bounds.count)]
}
}
public extension String {
/// Use this if you want to get result with any incorrect input
subscript(safe bounds: CountableClosedRange<Int>) -> SubSequence {
let lowerBound = max(0, Int(bounds.lowerBound) )
guard lowerBound < self.count else { return "" }
let upperBound = min(Int(bounds.upperBound), self.count-1)
guard upperBound >= 0 else { return "" }
let minIdx = index(startIndex, offsetBy: lowerBound )
let maxIdx = index(minIdx, offsetBy: upperBound-lowerBound )
return self[minIdx...maxIdx]
}
/// Use this if you want to get result with any incorrect input
subscript(safe bounds: CountableRange<Int>) -> SubSequence {
let lowerBound = max(0, bounds.lowerBound)
guard lowerBound < self.count else { return "" }
let upperBound = min(bounds.upperBound, self.count)
guard upperBound >= 0 else { return "" }
let minIdx = index(startIndex, offsetBy: lowerBound )
let maxIdx = index(minIdx, offsetBy: upperBound-lowerBound )
return self[minIdx..<maxIdx]
}
}
Code is tested: