I am new to Swift, I come from Java background. I want to make a struct for parsing input for a calculator. Here is my code
struct Parse {
var runningCalc: String
init(runningCalc: String) {
self.runningCalc = runningCalc
}
mutating func toArray(String: runningCalc){
runningCalc = runningCalc.split()
}}
I want to have input of a string, and have it return a string Array with delimiters of { -/*()%}.
In java you could do a string tokenizer to keep the delimiters in the returned string array.
How can I use the split or components method to separate my string input to a string array?
CodePudding user response:
I personally wouldn't use a struct. It's more common to extend an existing type with additional functionality. So, you want the components of a String
that includes delimiters? Sounds like you want to extend String
. Since String
conforms to StringProtocol
I'd extend StringProtocol
so that any other types that conform to StringProtocol
get this new functionality for free:
StringProtocol
already has a method called components(separatedBy:)
which takes a CharacterSet
(a set of characters) to delimit the receiver.
Let's implement a method similar to the existing StringProtocol
method.
extension StringProtocol {
func components(separatedByIncluding delims: CharacterSet) -> [String] {
var components: [String] = []
var component = ""
for character in self {
if String(character).rangeOfCharacter(from: delims) != nil {
if !component.isEmpty {
components.append(component)
}
components.append(String(character))
component = ""
} else {
component = [character]
}
}
if !component.isEmpty {
components.append(component)
}
return components
}
}
The method loops over each Character
and checks if it's a delimiter. If so, it adds a new String
to the array consisting of the delimiter Character
. If not, it constructs a substring consisting of non-delimiter Character
values. The resulting array of delimiters and substrings are then returned.
Usage would be:
func testExample() throws {
let delims = CharacterSet(charactersIn: "{ -/*()%}")
let text = "{abc def ghi-jkl/mno"
let components = text.components(separatedByIncluding: delims)
print("================")
print(components)
print("================")
}
The result is:
================
["{", "abc", " ", "def", " ", "ghi", "-", "jkl", "/", "mno"]
================
CodePudding user response:
AFAIK there is no native Swift split method that allows you to keep the delimiters. You will need to implement your own split method extending Collection. Something like this:
extension Collection {
func splitAndKeep(maxSplits: Int = .max, omittingEmptySubsequences: Bool = true, whereSeparator isSeparator: (Element) throws -> Bool) rethrows -> [SubSequence] {
guard !isEmpty else { return [] }
let positions = try indices.filter { try isSeparator(self[$0]) }
var subSequences: [SubSequence] = []
var start = startIndex
for position in positions {
if start < position {
let subsequence = self[start..<position]
if !subsequence.isEmpty || !omittingEmptySubsequences {
if subSequences.count 1 < maxSplits {
subSequences = [subsequence, self[position...position]]
} else if subSequences.count < maxSplits {
subSequences.append(subsequence)
}
if subSequences.count == maxSplits {
return subSequences
}
}
} else {
subSequences.append(self[position...position])
}
start = index(after: position)
}
let tail = self[start...]
if subSequences.count < maxSplits, !omittingEmptySubsequences || !tail.isEmpty {
subSequences.append(tail)
}
return subSequences
}
}
let string = "3*(22 13)/20"
let subsequences = string.splitAndKeep(whereSeparator: Set<Character>("{ -/*()%}.").contains) // ["3", "*", "(", "22", " ", "13", ")", "/", "20"]